diff --git a/.golangci.yml b/.golangci.yml index 0d7f90e263c40..c3dd47ec29da6 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -70,9 +70,6 @@ issues: - path: modules/log/ linters: - errcheck - - path: routers/routes/web.go - linters: - - dupl - path: routers/api/v1/repo/issue_subscription.go linters: - dupl @@ -114,3 +111,4 @@ issues: linters: - staticcheck text: "svc.IsAnInteractiveSession is deprecated: Use IsWindowsService instead." + diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d68217b43c2c..587d04d3285fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,56 @@ This changelog goes through all the changes that have been made in each release without substantial changes to our git log; to see the highlights of what has been added to each release, please refer to the [blog](https://blog.gitea.io). +## [1.14.3](https://github.com/go-gitea/gitea/releases/tag/v1.14.3) - 2021-06-18 + +* SECURITY + * Encrypt migration credentials at rest (#15895) (#16187) + * Only check access tokens if they are likely to be tokens (#16164) (#16171) + * Add missing SameSite settings for the i_like_gitea cookie (#16037) (#16039) + * Fix setting of SameSite on cookies (#15989) (#15991) +* API + * Repository object only count releases as releases (#16184) (#16190) + * EditOrg respect RepoAdminChangeTeamAccess option (#16184) (#16190) + * Fix overly strict edit pr permissions (#15900) (#16081) +* BUGFIXES + * Run processors on whole of text (#16155) (#16185) + * Class `issue-keyword` is being incorrectly stripped off spans (#16163) (#16172) + * Fix language switch for install page (#16043) (#16128) + * Fix bug on getIssueIDsByRepoID (#16119) (#16124) + * Set self-adjusting deadline for connection writing (#16068) (#16123) + * Fix http path bug (#16117) (#16120) + * Fix data URI scramble (#16098) (#16118) + * Merge all deleteBranch as one function and also fix bug when delete branch don't close related PRs (#16067) (#16097) + * git migration: don't prompt interactively for clone credentials (#15902) (#16082) + * Fix case change in ownernames (#16045) (#16050) + * Don't manipulate input params in email notification (#16011) (#16033) + * Remove branch URL before IssueRefURL (#15968) (#15970) + * Fix layout of milestone view (#15927) (#15940) + * GitHub Migration, migrate draft releases too (#15884) (#15888) + * Close the gitrepo when deleting the repository (#15876) (#15887) + * Upgrade xorm to v1.1.0 (#15869) (#15885) + * Fix blame row height alignment (#15863) (#15883) + * Fix error message when saving generated LOCAL_ROOT_URL config (#15880) (#15882) + * Backport Fix LFS commit finder not working (#15856) (#15874) + * Stop calling WriteHeader in Write (#15862) (#15873) + * Add timeout to writing to responses (#15831) (#15872) + * Return go-get info on subdirs (#15642) (#15871) + * Restore PAM user autocreation functionality (#15825) (#15867) + * Fix truncate utf8 string (#15828) (#15854) + * Fix bound address/port for caddy's certmagic library (#15758) (#15848) + * Upgrade unrolled/render to v1.1.1 (#15845) (#15846) + * Queue manager FlushAll can loop rapidly - add delay (#15733) (#15840) + * Tagger can be empty, as can Commit and Author - tolerate this (#15835) (#15839) + * Set autocomplete off on branches selector (#15809) (#15833) + * Add missing error to Doctor log (#15813) (#15824) + * Move restore repo to internal router and invoke from command to avoid open the same db file or queues files (#15790) (#15816) +* ENHANCEMENTS + * Removable media support to snap package (#16136) (#16138) + * Move sans-serif fallback font higher than emoji fonts (#15855) (#15892) +* DOCKER + * Only write config in environment-to-ini if there are changes (#15861) (#15868) + * Only offer hostcertificates if they exist (#15849) (#15853) + ## [1.14.2](https://github.com/go-gitea/gitea/releases/tag/v1.14.2) - 2021-05-09 * API diff --git a/Dockerfile.rootless b/Dockerfile.rootless index 6f4e704f00726..43ae308e3ac96 100644 --- a/Dockerfile.rootless +++ b/Dockerfile.rootless @@ -60,6 +60,8 @@ USER git:git ENV GITEA_WORK_DIR /var/lib/gitea ENV GITEA_CUSTOM /var/lib/gitea/custom ENV GITEA_TEMP /tmp/gitea +ENV TMPDIR /tmp/gitea + #TODO add to docs the ability to define the ini to load (usefull to test and revert a config) ENV GITEA_APP_INI /etc/gitea/app.ini ENV HOME "/var/lib/gitea/git" diff --git a/MAINTAINERS b/MAINTAINERS index d59c85e92e159..0c94df87b9530 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -42,3 +42,4 @@ Norwin Roosen (@noerw) Kyle Dumont (@kdumontnu) Patrick Schratz (@pat-s) Janis Estelmann (@KN4CK3R) +Steven Kriegler (@justusbunsi) diff --git a/cmd/generate.go b/cmd/generate.go index 13a99c94f462b..35c77a815b1d9 100644 --- a/cmd/generate.go +++ b/cmd/generate.go @@ -71,7 +71,7 @@ func runGenerateInternalToken(c *cli.Context) error { } func runGenerateLfsJwtSecret(c *cli.Context) error { - JWTSecretBase64, err := generate.NewJwtSecret() + JWTSecretBase64, err := generate.NewJwtSecretBase64() if err != nil { return err } diff --git a/cmd/web.go b/cmd/web.go index 9c7d493339f04..0ba14ae70642c 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -17,7 +17,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/routers" - "code.gitea.io/gitea/routers/routes" + "code.gitea.io/gitea/routers/install" context2 "github.com/gorilla/context" "github.com/urfave/cli" @@ -88,7 +88,7 @@ func runWeb(ctx *cli.Context) error { } // Perform pre-initialization - needsInstall := routers.PreInstallInit(graceful.GetManager().HammerContext()) + needsInstall := install.PreloadSettings(graceful.GetManager().HammerContext()) if needsInstall { // Flag for port number in case first time run conflict if ctx.IsSet("port") { @@ -101,7 +101,7 @@ func runWeb(ctx *cli.Context) error { return err } } - c := routes.InstallRoutes() + c := install.Routes() err := listen(c, false) select { case <-graceful.GetManager().IsShutdown(): @@ -134,7 +134,7 @@ func runWeb(ctx *cli.Context) error { } // Set up Chi routes - c := routes.NormalRoutes() + c := routers.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 9ee692fd35b1a..9ce84f762cecd 100644 --- a/contrib/pr/checkout.go +++ b/contrib/pr/checkout.go @@ -31,7 +31,6 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers" - "code.gitea.io/gitea/routers/routes" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/config" @@ -116,7 +115,7 @@ func runPR() { //routers.GlobalInit() external.RegisterRenderers() markup.Init() - c := routes.NormalRoutes() + c := routers.NormalRoutes() log.Printf("[PR] Ready for testing !\n") log.Printf("[PR] Login with user1, user2, user3, ... with pass: password\n") diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 2f3fc1f1d8504..54320a58befb4 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -51,6 +51,12 @@ RUN_MODE = ; prod ;REDIRECT_OTHER_PORT = false ;PORT_TO_REDIRECT = 80 ;; +;; Timeout for any write to the connection. (Set to 0 to disable all timeouts.) +;PER_WRITE_TIMEOUT = 30s +;; +;; Timeout per Kb written to connections. +;PER_WRITE_PER_KB_TIMEOUT = 30s +;; ;; Permission for unix socket ;UNIX_SOCKET_PERMISSION = 666 ;; @@ -144,6 +150,14 @@ RUN_MODE = ; prod ;; Enable exposure of SSH clone URL to anonymous visitors, default is false ;SSH_EXPOSE_ANONYMOUS = false ;; +;; Timeout for any write to ssh connections. (Set to 0 to disable all timeouts.) +;; Will default to the PER_WRITE_TIMEOUT. +;SSH_PER_WRITE_TIMEOUT = 30s +;; +;; Timeout per Kb written to ssh connections. +;; Will default to the PER_WRITE_PER_KB_TIMEOUT. +;SSH_PER_WRITE_PER_KB_TIMEOUT = 30s +;; ;; Indicate whether to check minimum key size with corresponding type ;MINIMUM_KEY_SIZE_CHECK = false ;; @@ -1141,20 +1155,20 @@ PATH = ;STARTUP_TIMEOUT = 30s ;; ;; Issue indexer queue, currently support: channel, levelqueue or redis, default is levelqueue (deprecated - use [queue.issue_indexer]) -;ISSUE_INDEXER_QUEUE_TYPE = levelqueue +;ISSUE_INDEXER_QUEUE_TYPE = levelqueue; **DEPRECATED** use settings in `[queue.issue_indexer]`. ;; ;; When ISSUE_INDEXER_QUEUE_TYPE is levelqueue, this will be the path where the queue will be saved. ;; This can be overridden by `ISSUE_INDEXER_QUEUE_CONN_STR`. -;; default is queues/common -;ISSUE_INDEXER_QUEUE_DIR = queues/common +;; default is queues/common +;ISSUE_INDEXER_QUEUE_DIR = queues/common; **DEPRECATED** use settings in `[queue.issue_indexer]`. ;; ;; When `ISSUE_INDEXER_QUEUE_TYPE` is `redis`, this will store the redis connection string. ;; When `ISSUE_INDEXER_QUEUE_TYPE` is `levelqueue`, this is a directory or additional options of ;; the form `leveldb://path/to/db?option=value&....`, and overrides `ISSUE_INDEXER_QUEUE_DIR`. -;ISSUE_INDEXER_QUEUE_CONN_STR = "addrs=127.0.0.1:6379 db=0" +;ISSUE_INDEXER_QUEUE_CONN_STR = "addrs=127.0.0.1:6379 db=0"; **DEPRECATED** use settings in `[queue.issue_indexer]`. ;; ;; Batch queue number, default is 20 -;ISSUE_INDEXER_QUEUE_BATCH_NUMBER = 20 +;ISSUE_INDEXER_QUEUE_BATCH_NUMBER = 20; **DEPRECATED** use settings in `[queue.issue_indexer]`. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Repository Indexer settings @@ -1183,7 +1197,7 @@ PATH = ;REPO_INDEXER_EXCLUDE = ;; ;; -;UPDATE_BUFFER_LEN = 20 +;UPDATE_BUFFER_LEN = 20; **DEPRECATED** use settings in `[queue.issue_indexer]`. ;MAX_FILE_SIZE = 1048576 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1201,7 +1215,7 @@ PATH = ;; default to persistable-channel ;TYPE = persistable-channel ;; -;; data-dir for storing persistable queues and level queues, individual queues will default to `queues/common` meaning the queue is shared. +;; data-dir for storing persistable queues and level queues, individual queues will default to `queues/common` meaning the queue is shared. ;DATADIR = queues/ ;; ;; Default queue length before a channel queue will block 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 28ab922c439eb..4f84e2ac33269 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -94,10 +94,11 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`. - `REOPEN_KEYWORDS`: **reopen**, **reopens**, **reopened**: List of keywords used in Pull Request comments to automatically reopen a related issue - `DEFAULT_MERGE_MESSAGE_COMMITS_LIMIT`: **50**: In the default merge message for squash commits include at most this many commits. Set to `-1` to include all commits -- `DEFAULT_MERGE_MESSAGE_SIZE`: **5120**: In the default merge message for squash commits limit the size of the commit messages. Set to `-1` to have no limit. +- `DEFAULT_MERGE_MESSAGE_SIZE`: **5120**: In the default merge message for squash commits limit the size of the commit messages. Set to `-1` to have no limit. Only used if `POPULATE_SQUASH_COMMENT_WITH_COMMIT_MESSAGES` is `true`. - `DEFAULT_MERGE_MESSAGE_ALL_AUTHORS`: **false**: In the default merge message for squash commits walk all commits to include all authors in the Co-authored-by otherwise just use those in the limited list - `DEFAULT_MERGE_MESSAGE_MAX_APPROVERS`: **10**: In default merge messages limit the number of approvers listed as `Reviewed-by:`. Set to `-1` to include all. - `DEFAULT_MERGE_MESSAGE_OFFICIAL_APPROVERS_ONLY`: **true**: In default merge messages only include approvers who are officially allowed to review. +- `POPULATE_SQUASH_COMMENT_WITH_COMMIT_MESSAGES`: **false**: In default squash-merge messages include the commit message of all commits comprising the pull request. ### Repository - Issue (`repository.issue`) @@ -251,6 +252,9 @@ The following configuration set `Content-Type: application/vnd.android.package-a most cases you do not need to change the default value. Alter it only if your SSH server node is not the same as HTTP node. Do not set this variable if `PROTOCOL` is set to `unix`. +- `PER_WRITE_TIMEOUT`: **30s**: Timeout for any write to the connection. (Set to 0 to + disable all timeouts.) +- `PER_WRITE_PER_KB_TIMEOUT`: **10s**: Timeout per Kb written to connections. - `DISABLE_SSH`: **false**: Disable SSH feature when it's not available. - `START_SSH_SERVER`: **false**: When enabled, use the built-in SSH server. @@ -267,6 +271,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a - `SSH_AUTHORIZED_PRINCIPALS_ALLOW`: **off** or **username, email**: \[off, username, email, anything\]: Specify the principals values that users are allowed to use as principal. When set to `anything` no checks are done on the principal string. When set to `off` authorized principal are not allowed to be set. - `SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE`: **false/true**: Gitea will create a authorized_principals file by default when it is not using the internal ssh server and `SSH_AUTHORIZED_PRINCIPALS_ALLOW` is not `off`. - `SSH_AUTHORIZED_PRINCIPALS_BACKUP`: **false/true**: Enable SSH Authorized Principals Backup when rewriting all keys, default is true if `SSH_AUTHORIZED_PRINCIPALS_ALLOW` is not `off`. +- `SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE`: **{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}**: Set the template for the command to passed on authorized keys. Possible keys are: AppPath, AppWorkPath, CustomConf, CustomPath, Key - where Key is a `models.PublicKey` and the others are strings which are shellquoted. - `SSH_SERVER_CIPHERS`: **aes128-ctr, aes192-ctr, aes256-ctr, aes128-gcm@openssh.com, arcfour256, arcfour128**: For the built-in SSH server, choose the ciphers to support for SSH connections, for system SSH this setting has no effect. - `SSH_SERVER_KEY_EXCHANGES`: **diffie-hellman-group1-sha1, diffie-hellman-group14-sha1, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, curve25519-sha256@libssh.org**: For the built-in SSH server, choose the key exchange algorithms to support for SSH connections, for system SSH this setting has no effect. - `SSH_SERVER_MACS`: **hmac-sha2-256-etm@openssh.com, hmac-sha2-256, hmac-sha1, hmac-sha1-96**: For the built-in SSH server, choose the MACs to support for SSH connections, for system SSH this setting has no effect @@ -274,6 +279,9 @@ The following configuration set `Content-Type: application/vnd.android.package-a - `SSH_KEY_TEST_PATH`: **/tmp**: Directory to create temporary files in when testing public keys using ssh-keygen, default is the system temporary directory. - `SSH_KEYGEN_PATH`: **ssh-keygen**: Path to ssh-keygen, default is 'ssh-keygen' which means the shell is responsible for finding out which one to call. - `SSH_EXPOSE_ANONYMOUS`: **false**: Enable exposure of SSH clone URL to anonymous visitors, default is false. +- `SSH_PER_WRITE_TIMEOUT`: **30s**: Timeout for any write to the SSH connections. (Set to + 0 to disable all timeouts.) +- `SSH_PER_WRITE_PER_KB_TIMEOUT`: **10s**: Timeout per Kb written to SSH connections. - `MINIMUM_KEY_SIZE_CHECK`: **true**: Indicate whether to check minimum key size with corresponding type. - `OFFLINE_MODE`: **false**: Disables use of CDN for static files and Gravatar for profile pictures. @@ -349,10 +357,10 @@ relation to port exhaustion. - `ISSUE_INDEXER_NAME`: **gitea_issues**: Issue indexer name, available when ISSUE_INDEXER_TYPE is elasticsearch - `ISSUE_INDEXER_PATH`: **indexers/issues.bleve**: Index file used for issue search; available when ISSUE_INDEXER_TYPE is bleve and elasticsearch. - The next 4 configuration values are deprecated and should be set in `queue.issue_indexer` however are kept for backwards compatibility: -- `ISSUE_INDEXER_QUEUE_TYPE`: **levelqueue**: Issue indexer queue, currently supports:`channel`, `levelqueue`, `redis`. -- `ISSUE_INDEXER_QUEUE_DIR`: **queues/common**: When `ISSUE_INDEXER_QUEUE_TYPE` is `levelqueue`, this will be the path where the queue will be saved. (Previously this was `indexers/issues.queue`.) -- `ISSUE_INDEXER_QUEUE_CONN_STR`: **addrs=127.0.0.1:6379 db=0**: When `ISSUE_INDEXER_QUEUE_TYPE` is `redis`, this will store the redis connection string. When `ISSUE_INDEXER_QUEUE_TYPE` is `levelqueue`, this is a directory or additional options of the form `leveldb://path/to/db?option=value&....`, and overrides `ISSUE_INDEXER_QUEUE_DIR`. -- `ISSUE_INDEXER_QUEUE_BATCH_NUMBER`: **20**: Batch queue number. +- `ISSUE_INDEXER_QUEUE_TYPE`: **levelqueue**: Issue indexer queue, currently supports:`channel`, `levelqueue`, `redis`. **DEPRECATED** use settings in `[queue.issue_indexer]`. +- `ISSUE_INDEXER_QUEUE_DIR`: **queues/common**: When `ISSUE_INDEXER_QUEUE_TYPE` is `levelqueue`, this will be the path where the queue will be saved. **DEPRECATED** use settings in `[queue.issue_indexer]`. +- `ISSUE_INDEXER_QUEUE_CONN_STR`: **addrs=127.0.0.1:6379 db=0**: When `ISSUE_INDEXER_QUEUE_TYPE` is `redis`, this will store the redis connection string. When `ISSUE_INDEXER_QUEUE_TYPE` is `levelqueue`, this is a directory or additional options of the form `leveldb://path/to/db?option=value&....`, and overrides `ISSUE_INDEXER_QUEUE_DIR`. **DEPRECATED** use settings in `[queue.issue_indexer]`. +- `ISSUE_INDEXER_QUEUE_BATCH_NUMBER`: **20**: Batch queue number. **DEPRECATED** use settings in `[queue.issue_indexer]`. - `REPO_INDEXER_ENABLED`: **false**: Enables code search (uses a lot of disk space, about 6 times more than the repository size). - `REPO_INDEXER_TYPE`: **bleve**: Code search engine type, could be `bleve` or `elasticsearch`. @@ -363,14 +371,14 @@ relation to port exhaustion. - `REPO_INDEXER_INCLUDE`: **empty**: A comma separated list of glob patterns (see https://github.com/gobwas/glob) to **include** in the index. Use `**.txt` to match any files with .txt extension. An empty list means include all files. - `REPO_INDEXER_EXCLUDE`: **empty**: A comma separated list of glob patterns (see https://github.com/gobwas/glob) to **exclude** from the index. Files that match this list will not be indexed, even if they match in `REPO_INDEXER_INCLUDE`. - `REPO_INDEXER_EXCLUDE_VENDORED`: **true**: Exclude vendored files from index. -- `UPDATE_BUFFER_LEN`: **20**: Buffer length of index request. +- `UPDATE_BUFFER_LEN`: **20**: Buffer length of index request. **DEPRECATED** use settings in `[queue.issue_indexer]`. - `MAX_FILE_SIZE`: **1048576**: Maximum size in bytes of files to be indexed. - `STARTUP_TIMEOUT`: **30s**: If the indexer takes longer than this timeout to start - fail. (This timeout will be added to the hammer time above for child processes - as bleve will not start until the previous parent is shutdown.) Set to zero to never timeout. ## Queue (`queue` and `queue.*`) - `TYPE`: **persistable-channel**: General queue type, currently support: `persistable-channel` (uses a LevelDB internally), `channel`, `level`, `redis`, `dummy` -- `DATADIR`: **queues/**: Base DataDir for storing persistent and level queues. `DATADIR` for individual queues can be set in `queue.name` sections but will default to `DATADIR/`**`common`**. (Previously each queue would default to `DATADIR/`**`name`**.) +- `DATADIR`: **queues/**: Base DataDir for storing persistent and level queues. `DATADIR` for individual queues can be set in `queue.name` sections but will default to `DATADIR/`**`common`**. (Previously each queue would default to `DATADIR/`**`name`**.) - `LENGTH`: **20**: Maximal queue size before channel queues block - `BATCH_LENGTH`: **20**: Batch data before passing to the handler - `CONN_STR`: **redis://127.0.0.1:6379/0**: Connection string for the redis queue type. Options can be set using query params. Similarly LevelDB options can also be set using: **leveldb://relative/path?option=value** or **leveldb:///absolute/path?option=value**, and will override `DATADIR` @@ -851,7 +859,9 @@ NB: You must have `DISABLE_ROUTER_LOG` set to `false` for this option to take ef - `ACCESS_TOKEN_EXPIRATION_TIME`: **3600**: Lifetime of an OAuth2 access token in seconds - `REFRESH_TOKEN_EXPIRATION_TIME`: **730**: Lifetime of an OAuth2 refresh token in hours - `INVALIDATE_REFRESH_TOKENS`: **false**: Check if refresh token has already been used -- `JWT_SECRET`: **\**: OAuth2 authentication secret for access and refresh tokens, change this a unique string. +- `JWT_SIGNING_ALGORITHM`: **RS256**: Algorithm used to sign OAuth2 tokens. Valid values: \[`HS256`, `HS384`, `HS512`, `RS256`, `RS384`, `RS512`, `ES256`, `ES384`, `ES512`\] +- `JWT_SECRET`: **\**: OAuth2 authentication secret for access and refresh tokens, change this to a unique string. This setting is only needed if `JWT_SIGNING_ALGORITHM` is set to `HS256`, `HS384` or `HS512`. +- `JWT_SIGNING_PRIVATE_KEY_FILE`: **jwt/private.pem**: Private key file path used to sign OAuth2 tokens. The path is relative to `CUSTOM_PATH`. This setting is only needed if `JWT_SIGNING_ALGORITHM` is set to `RS256`, `RS384`, `RS512`, `ES256`, `ES384` or `ES512`. The file must contain a RSA or ECDSA private key in the PKCS8 format. - `MAX_TOKEN_LENGTH`: **32767**: Maximum length of token/cookie to accept from OAuth2 provider ## i18n (`i18n`) diff --git a/docs/content/doc/advanced/repo-mirror.en-us.md b/docs/content/doc/advanced/repo-mirror.en-us.md new file mode 100644 index 0000000000000..bda5b0fa5594c --- /dev/null +++ b/docs/content/doc/advanced/repo-mirror.en-us.md @@ -0,0 +1,88 @@ +--- +date: "2021-05-13T00:00:00-00:00" +title: "Repository Mirror" +slug: "repo-mirror" +weight: 45 +toc: false +draft: false +menu: + sidebar: + parent: "advanced" + name: "Repository Mirror" + weight: 45 + identifier: "repo-mirror" +--- + +# Repository Mirror + +Repository mirroring allows for the mirroring of repositories to and from external sources. You can use it to mirror branches, tags, and commits between repositories. + +**Table of Contents** + +{{< toc >}} + +## Use cases + +The following are some possible use cases for repository mirroring: + +- You migrated to Gitea but still need to keep your project in another source. In that case, you can simply set it up to mirror to Gitea (pull) and all the essential history of commits, tags, and branches are available in your Gitea instance. +- You have old projects in another source that you don’t use actively anymore, but don’t want to remove for archiving purposes. In that case, you can create a push mirror so that your active Gitea repository can push its changes to the old location. + +## Pulling from a remote repository + +For an existing remote repository, you can set up pull mirroring as follows: + +1. Select **New Migration** in the **Create...** menu on the top right. +2. Select the remote repository service. +3. Enter a repository URL. +4. If the repository needs authentication fill in your authentication information. +5. Check the box **This repository will be a mirror**. +5. Select **Migrate repository** to save the configuration. + +The repository now gets mirrored periodically from the remote repository. You can force a sync by selecting **Synchronize Now** in the repository settings. + +## Pushing to a remote repository + +For an existing repository, you can set up push mirroring as follows: + +1. In your repository, go to **Settings** > **Repository**, and then the **Mirror Settings** section. +2. Enter a repository URL. +3. If the repository needs authentication expand the **Authorization** section and fill in your authentication information. +4. Select **Add Push Mirror** to save the configuration. + +The repository now gets mirrored periodically to the remote repository. You can force a sync by selecting **Synchronize Now**. In case of an error a message displayed to help you resolve it. + +:exclamation::exclamation: **NOTE:** This will force push to the remote repository. This will overwrite any changes in the remote repository! :exclamation::exclamation: + +### Setting up a push mirror from Gitea to GitHub + +To set up a mirror from Gitea to GitHub, you need to follow these steps: + +1. Create a [GitHub personal access token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) with the *public_repo* box checked. +2. Fill in the **Git Remote Repository URL**: `https://github.com//.git`. +3. Fill in the **Authorization** fields with your GitHub username and the personal access token. +4. Select **Add Push Mirror** to save the configuration. + +The repository pushes shortly thereafter. To force a push, select the **Synchronize Now** button. + +### Setting up a push mirror from Gitea to GitLab + +To set up a mirror from Gitea to GitLab, you need to follow these steps: + +1. Create a [GitLab personal access token](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html) with *write_repository* scope. +2. Fill in the **Git Remote Repository URL**: `https:////.git`. +3. Fill in the **Authorization** fields with `oauth2` as **Username** and your GitLab personal access token as **Password**. +4. Select **Add Push Mirror** to save the configuration. + +The repository pushes shortly thereafter. To force a push, select the **Synchronize Now** button. + +### Setting up a push mirror from Gitea to Bitbucket + +To set up a mirror from Gitea to Bitbucket, you need to follow these steps: + +1. Create a [Bitbucket app password](https://support.atlassian.com/bitbucket-cloud/docs/app-passwords/) with the *Repository Write* box checked. +2. Fill in the **Git Remote Repository URL**: `https://bitbucket.org//.git`. +3. Fill in the **Authorization** fields with your Bitbucket username and the app password as **Password**. +4. Select **Add Push Mirror** to save the configuration. + +The repository pushes shortly thereafter. To force a push, select the **Synchronize Now** button. diff --git a/docs/content/doc/developers/oauth2-provider.md b/docs/content/doc/developers/oauth2-provider.md index 29305a24ca197..efe78eed97659 100644 --- a/docs/content/doc/developers/oauth2-provider.md +++ b/docs/content/doc/developers/oauth2-provider.md @@ -23,10 +23,13 @@ Gitea supports acting as an OAuth2 provider to allow third party applications to ## Endpoints -| Endpoint | URL | -| ---------------------- | --------------------------- | -| Authorization Endpoint | `/login/oauth/authorize` | -| Access Token Endpoint | `/login/oauth/access_token` | +| Endpoint | URL | +| ------------------------ | ----------------------------------- | +| OpenID Connect Discovery | `/.well-known/openid-configuration` | +| Authorization Endpoint | `/login/oauth/authorize` | +| Access Token Endpoint | `/login/oauth/access_token` | +| OpenID Connect UserInfo | `/login/oauth/userinfo` | +| JSON Web Key Set | `/login/oauth/keys` | ## Supported OAuth2 Grants diff --git a/go.mod b/go.mod index e2c1c6aba897d..c28db7b8edb63 100644 --- a/go.mod +++ b/go.mod @@ -11,24 +11,25 @@ require ( 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 - github.com/Microsoft/go-winio v0.4.18 // indirect + github.com/Microsoft/go-winio v0.5.0 // indirect github.com/NYTimes/gziphandler v1.1.1 - github.com/PuerkitoBio/goquery v1.5.1 - github.com/RoaringBitmap/roaring v0.6.0 // indirect - github.com/alecthomas/chroma v0.8.2 - github.com/andybalholm/brotli v1.0.1 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20210512092938-c05353c2d58c // indirect + github.com/PuerkitoBio/goquery v1.6.1 + github.com/RoaringBitmap/roaring v0.7.3 // indirect + github.com/alecthomas/chroma v0.9.1 + github.com/andybalholm/brotli v1.0.3 // indirect + github.com/andybalholm/cascadia v1.2.0 // indirect github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect - github.com/blevesearch/bleve/v2 v2.0.3 + github.com/blevesearch/bleve/v2 v2.0.5 github.com/boombuler/barcode v1.0.1 // indirect github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // indirect - github.com/caddyserver/certmagic v0.13.0 + github.com/caddyserver/certmagic v0.13.1 github.com/chi-middleware/proxy v1.1.1 github.com/couchbase/go-couchbase v0.0.0-20210224140812-5740cd35f448 // indirect github.com/couchbase/gomemcached v0.1.2 // indirect github.com/couchbase/goutils v0.0.0-20210118111533-e33d3ffb5401 // indirect github.com/denisenkom/go-mssqldb v0.10.0 github.com/dgrijalva/jwt-go v3.2.0+incompatible - github.com/dlclark/regexp2 v1.4.0 // indirect github.com/dustin/go-humanize v1.0.0 github.com/editorconfig/editorconfig-core-go/v2 v2.4.2 github.com/emirpasic/gods v1.12.0 @@ -37,14 +38,14 @@ require ( github.com/go-asn1-ber/asn1-ber v1.5.3 // indirect github.com/go-chi/chi v1.5.4 github.com/go-chi/cors v1.2.0 - github.com/go-enry/go-enry/v2 v2.6.1 - github.com/go-git/go-billy/v5 v5.1.0 - github.com/go-git/go-git/v5 v5.3.0 + github.com/go-enry/go-enry/v2 v2.7.0 + github.com/go-git/go-billy/v5 v5.3.1 + github.com/go-git/go-git/v5 v5.4.2 github.com/go-ldap/ldap/v3 v3.3.0 - github.com/go-redis/redis/v8 v8.8.2 + github.com/go-redis/redis/v8 v8.10.0 github.com/go-sql-driver/mysql v1.6.0 github.com/go-swagger/go-swagger v0.27.0 - github.com/go-testfixtures/testfixtures/v3 v3.6.0 + github.com/go-testfixtures/testfixtures/v3 v3.6.1 github.com/gobwas/glob v0.2.3 github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28 github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14 @@ -56,26 +57,29 @@ require ( github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/sessions v1.2.1 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-retryablehttp v0.7.0 // indirect github.com/hashicorp/go-version v1.3.1 + github.com/hashicorp/golang-lru v0.5.1 github.com/huandu/xstrings v1.3.2 github.com/issue9/identicon v1.2.0 github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7 - github.com/json-iterator/go v1.1.10 + github.com/json-iterator/go v1.1.11 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/kevinburke/ssh_config v1.1.0 // indirect github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4 - github.com/klauspost/compress v1.12.1 + github.com/klauspost/compress v1.13.0 github.com/klauspost/pgzip v1.2.5 // indirect github.com/lafriks/xormstore v1.4.0 - github.com/lib/pq v1.10.1 + github.com/lib/pq v1.10.2 + github.com/libdns/libdns v0.2.1 // indirect github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 github.com/markbates/goth v1.67.1 - github.com/mattn/go-isatty v0.0.12 - github.com/mattn/go-runewidth v0.0.12 // indirect + github.com/mattn/go-isatty v0.0.13 + github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mattn/go-sqlite3 v1.14.7 github.com/mholt/archiver/v3 v3.5.0 - github.com/microcosm-cc/bluemonday v1.0.8 - github.com/miekg/dns v1.1.40 // indirect + github.com/microcosm-cc/bluemonday v1.0.9 + github.com/miekg/dns v1.1.42 // indirect github.com/minio/md5-simd v1.1.2 // indirect github.com/minio/minio-go/v7 v7.0.10 github.com/minio/sha256-simd v1.0.0 // indirect @@ -87,12 +91,12 @@ require ( github.com/oliamb/cutter v0.2.2 github.com/olivere/elastic/v7 v7.0.24 github.com/pelletier/go-toml v1.9.0 // indirect - github.com/pierrec/lz4/v4 v4.1.3 // indirect + github.com/pierrec/lz4/v4 v4.1.7 // indirect github.com/pkg/errors v0.9.1 github.com/pquerna/otp v1.3.0 - github.com/prometheus/client_golang v1.10.0 + github.com/prometheus/client_golang v1.11.0 github.com/quasoft/websspi v1.0.0 - github.com/rivo/uniseg v0.2.0 // indirect + github.com/rs/xid v1.3.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sergi/go-diff v1.2.0 github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect @@ -105,24 +109,24 @@ require ( github.com/unknwon/com v1.0.1 github.com/unknwon/i18n v0.0.0-20210321134014-0ebbf2df1c44 github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae - github.com/unrolled/render v1.1.1 + github.com/unrolled/render v1.4.0 github.com/urfave/cli v1.22.5 - github.com/willf/bitset v1.1.11 // indirect - github.com/xanzy/go-gitlab v0.48.0 + github.com/xanzy/go-gitlab v0.50.0 github.com/yohcop/openid-go v1.0.0 - github.com/yuin/goldmark v1.3.5 - github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691 + github.com/yuin/goldmark v1.3.7 + github.com/yuin/goldmark-highlighting v0.0.0-20210516132338-9216f9c5aa01 github.com/yuin/goldmark-meta v1.0.0 + go.etcd.io/bbolt v1.3.6 // indirect go.jolheiser.com/hcaptcha v0.0.4 go.jolheiser.com/pwn v0.0.3 - go.uber.org/multierr v1.6.0 // indirect - go.uber.org/zap v1.16.0 // indirect - golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b - golang.org/x/net v0.0.0-20210421230115-4e50805a0758 - golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78 - golang.org/x/sys v0.0.0-20210421221651-33663a62ff08 + go.uber.org/multierr v1.7.0 // indirect + go.uber.org/zap v1.17.0 // indirect + golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a + golang.org/x/net v0.0.0-20210525063256-abc453219eb5 + golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c + golang.org/x/sys v0.0.0-20210608053332-aa57babbf139 golang.org/x/text v0.3.6 - golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect + golang.org/x/time v0.0.0-20210608053304-ed9ce3a009e4 // indirect golang.org/x/tools v0.1.0 gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df diff --git a/go.sum b/go.sum index b511d6c3f75c0..65a9f0b363fe4 100644 --- a/go.sum +++ b/go.sum @@ -57,48 +57,44 @@ github.com/6543/go-version v1.3.1 h1:HvOp+Telns7HWJ2Xo/05YXQSB2bE0WmVgbHqwMPZT4U github.com/6543/go-version v1.3.1/go.mod h1:oqFAHCwtLVUTLdhQmVZWYvaHXTdsbB4SY85at64SQEo= github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28= github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -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/Julusian/godocdown v0.0.0-20170816220326-6d19f8ff2df8/go.mod h1:INZr5t32rG59/5xeltqoCJoNY7e5x/3xoY9WSWVWg74= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.4.18 h1:yjwCO1nhWEShaA5qsmPOBzAOjRCa2PRLsDNZ5yBWXpg= -github.com/Microsoft/go-winio v0.4.18/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU= +github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= 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/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= +github.com/ProtonMail/go-crypto v0.0.0-20210512092938-c05353c2d58c h1:bNpaLLv2Y4kslsdkdCwAYu8Bak1aGVtxwi8Z/wy4Yuo= +github.com/ProtonMail/go-crypto v0.0.0-20210512092938-c05353c2d58c/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= +github.com/PuerkitoBio/goquery v1.6.1 h1:FgjbQZKl5HTmcn4sKBgvx8vv63nhyhIpv7lJpFGCWpk= +github.com/PuerkitoBio/goquery v1.6.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo= -github.com/RoaringBitmap/roaring v0.6.0 h1:tZcn2nJpUrZf+xQY8x+9QY7BxSETMjkdNG4Ts5zahyU= -github.com/RoaringBitmap/roaring v0.6.0/go.mod h1:WZ83fjBF/7uBHi6QoFyfGL4+xuV4Qn+xFkm4+vSzrhE= -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/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/RoaringBitmap/roaring v0.7.1/go.mod h1:jdT9ykXwHFNdJbEtxePexlFYH9LXucApeS0/+/g+p1I= +github.com/RoaringBitmap/roaring v0.7.3 h1:RwirWpvFONt2EwHHEHhER7S4BHZkyj3qL5LXLlnQPZ4= +github.com/RoaringBitmap/roaring v0.7.3/go.mod h1:jdT9ykXwHFNdJbEtxePexlFYH9LXucApeS0/+/g+p1I= +github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= +github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= -github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= -github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= -github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U= github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI= github.com/alecthomas/chroma v0.7.2-0.20200305040604-4f3623dce67a/go.mod h1:fv5SzZPFJbwp2NXJWpFIX7DZS4HgV1K4ew4Pc2OZD9s= -github.com/alecthomas/chroma v0.8.2 h1:x3zkuE2lUk/RIekyAJ3XRqSCP4zwWDfcw/YJCuCAACg= github.com/alecthomas/chroma v0.8.2/go.mod h1:sko8vR34/90zvl5QdcUdvzL3J8NKjAUx9va9jPuFNoM= +github.com/alecthomas/chroma v0.9.1 h1:cBmvQqRImzR5aWqdMxYZByND4S7BCS/g0svZb28h0Dc= +github.com/alecthomas/chroma v0.9.1/go.mod h1:eMuEnpA18XbG/WhOWtCzJHS7WqEtDAI+HxdwoW0nVSk= github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo= github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0= -github.com/alecthomas/kong v0.1.17-0.20190424132513-439c674f7ae0/go.mod h1:+inYUSluD+p4L8KdviBSgzcqEjUQOfC5fQDRFuc36lI= github.com/alecthomas/kong v0.2.1-0.20190708041108-0548c6b1afae/go.mod h1:+inYUSluD+p4L8KdviBSgzcqEjUQOfC5fQDRFuc36lI= github.com/alecthomas/kong v0.2.4/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE= -github.com/alecthomas/kong-hcl v0.1.8-0.20190615233001-b21fea9723c8/go.mod h1:MRgZdU3vrFd05IQ89AxUZ0aYdF39BYoNFa324SodPCA= github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 h1:p9Sln00KOTlrYkxI1zYWl1QLnEqAqEARBEYa8FQnQcY= github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -108,22 +104,20 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= -github.com/andybalholm/brotli v1.0.1 h1:KqhlKozYbRtJvsPrrEeXcO+N2l6NYT5A2QAFmSULpEc= -github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= -github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo= +github.com/andybalholm/brotli v1.0.3 h1:fpcw+r1N1h0Poc1F/pHbW40cUm/lMEQslZtCkBQ0UnM= +github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= +github.com/andybalholm/cascadia v1.2.0 h1:vuRCkM5Ozh/BfmsaTm26kbjm0mIOM3yS5Ek/F5h18aE= +github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= -github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= @@ -131,11 +125,8 @@ github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:o github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= -github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= github.com/aws/aws-sdk-go v1.38.3/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -144,10 +135,13 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bits-and-blooms/bitset v1.1.10/go.mod h1:w0XsmFg8qg6cmpTtJ0z3pKgjTDBMMnI/+I2syrE6XBE= +github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA= +github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/blevesearch/bleve/v2 v2.0.1/go.mod h1:OBP2Pktqik8vEiUlGhuWjYx7KiO4zD542+DHqICwM5w= -github.com/blevesearch/bleve/v2 v2.0.3 h1:mDrwrsRIA4PDYkfUNjoh5zGECvquuJIA3MJU5ivaO8E= -github.com/blevesearch/bleve/v2 v2.0.3/go.mod h1:ip+4iafiEq2gCY5rJXe87bT6LkF/OJMCjQEYIfTBfW8= +github.com/blevesearch/bleve/v2 v2.0.5 h1:184yM7uei4Cmw2SdKSdMWYg46OFRKsr+s8hBYc2FbuU= +github.com/blevesearch/bleve/v2 v2.0.5/go.mod h1:ZjWibgnbRX33c+vBRgla9QhPb4QOjD6fdVJ+R1Bk8LM= github.com/blevesearch/bleve_index_api v1.0.0 h1:Ds3XeuTxjXCkG6pgIwWDRyooJKNIuOKemnN0N0IkhTU= github.com/blevesearch/bleve_index_api v1.0.0/go.mod h1:fiwKS0xLEm+gBRgv5mumf0dhgFr2mDgZah1pqv1c1M4= github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo= @@ -164,8 +158,9 @@ github.com/blevesearch/snowballstem v0.9.0 h1:lMQ189YspGP6sXvZQ4WZ+MLawfV8wOmPoD github.com/blevesearch/snowballstem v0.9.0/go.mod h1:PivSj3JMc8WuaFkTSRDW2SlrulNWPl4ABg1tC/hlgLs= github.com/blevesearch/upsidedown_store_api v1.0.1 h1:1SYRwyoFLwG3sj0ed89RLtM15amfX2pXlYbFOnF8zNU= github.com/blevesearch/upsidedown_store_api v1.0.1/go.mod h1:MQDVGpHZrpe3Uy26zJBf/a8h0FZY6xJbthIMm8myH2Q= -github.com/blevesearch/vellum v1.0.3 h1:U86G41A7CtXNzzpIJHM8lSTUqz1Mp8U870TkcdCzZc8= github.com/blevesearch/vellum v1.0.3/go.mod h1:2u5ax02KeDuNWu4/C+hVQMD6uLN4txH1JbtpaDNLJRo= +github.com/blevesearch/vellum v1.0.4 h1:o6t7NxTnThp1es52uQvOJJx+9yK/nKXlWC5xl4LCz1U= +github.com/blevesearch/vellum v1.0.4/go.mod h1:cMhywHI0de50f7Nj42YgvyD6bFJ2WkNRvNBlNMrEVgY= github.com/blevesearch/zapx/v11 v11.1.10/go.mod h1:DTjbcBqrr/Uo82UBilDC8lEew42gN/OcIyiTNFtSijc= github.com/blevesearch/zapx/v11 v11.2.0 h1:GBkCJYsyj3eIU4+aiLPxoMz1PYvDbQZl/oXHIBZIP60= github.com/blevesearch/zapx/v11 v11.2.0/go.mod h1:gN/a0alGw1FZt/YGTo1G6Z6XpDkeOfujX5exY9sCQQM= @@ -187,10 +182,8 @@ github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b h1:L/QXpzIa3pOvUGt1D1lA5KjYhPBAN/3iWdP7xeFS9F0= github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= -github.com/caddyserver/certmagic v0.13.0 h1:ky0rntZvIFiUKFdIikYxj31WN+Ts0Od6Wjz83iTzxfc= -github.com/caddyserver/certmagic v0.13.0/go.mod h1:dNOzF4iOB7H9E51xTooMB90vs+2XNVtpnx0liQNsQY4= -github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/caddyserver/certmagic v0.13.1 h1:A5qLxh9J6/CYWEOHaj135IWAjCY0193ONxEy8jbOlPw= +github.com/caddyserver/certmagic v0.13.1/go.mod h1:+zhQtEgLOyXRA/KRduHXNhGGdTeqRM4ePj8eBGD/2CQ= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= @@ -201,15 +194,12 @@ github.com/chi-middleware/proxy v1.1.1/go.mod h1:jQwMEJct2tz9VmtCELxvnXoMfa+SOdi github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -217,10 +207,8 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/couchbase/ghistogram v0.1.0/go.mod h1:s1Jhy76zqfEecpNWJfWUiKZookAFaiGOEoyzgHt9i7k= github.com/couchbase/go-couchbase v0.0.0-20201026062457-7b3be89bbd89/go.mod h1:+/bddYDxXsf9qt0xpDUtRR47A2GjaXmGGAqQ/k3GJ8A= @@ -242,13 +230,11 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY= -github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E= github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ= github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk= 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/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denisenkom/go-mssqldb v0.10.0 h1:QykgLZBorFE95+gO3u9esLd0BmbvpWp0/waNNZfHBM8= @@ -267,20 +253,15 @@ github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/dvyukov/go-fuzz v0.0.0-20210429054444-fca39067bc72/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= github.com/editorconfig/editorconfig-core-go/v2 v2.4.2 h1:1lkDpSoAaFLrgYTVJ/eNCV+lkDSv/j9Wm0jcvDfVVEo= github.com/editorconfig/editorconfig-core-go/v2 v2.4.2/go.mod h1:IXeWRVO4LZRoNunhHh/oP6BQvTs94nB2pNvbw32l8tQ= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o= -github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= +github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= -github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -295,8 +276,6 @@ github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= -github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= -github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= @@ -318,25 +297,25 @@ github.com/go-chi/chi/v5 v5.0.1 h1:ALxjCrTf1aflOlkhMnCUP86MubbWFrzB3gkRPReLpTo= github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.0 h1:tV1g1XENQ8ku4Bq3K9ub2AtgG+p16SmzeMSGTwrOKdE= github.com/go-chi/cors v1.2.0/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= -github.com/go-enry/go-enry/v2 v2.6.1 h1:ckFkMVj2NeHpaQDFDiSjanVjNy2IiuMNivhXDB4c5Q0= -github.com/go-enry/go-enry/v2 v2.6.1/go.mod h1:GVzIiAytiS5uT/QiuakK7TF1u4xDab87Y8V5EJRpsIQ= +github.com/go-enry/go-enry/v2 v2.7.0 h1:bcAZfvX0LmAEMl8OEd8MPsfJCN2Io4mNU1an1Hh48VA= +github.com/go-enry/go-enry/v2 v2.7.0/go.mod h1:GVzIiAytiS5uT/QiuakK7TF1u4xDab87Y8V5EJRpsIQ= github.com/go-enry/go-oniguruma v1.2.1 h1:k8aAMuJfMrqm/56SG2lV9Cfti6tC4x8673aHCcBk+eo= github.com/go-enry/go-oniguruma v1.2.1/go.mod h1:bWDhYP+S6xZQgiRL7wlTScFYBe023B6ilRZbCAD5Hf4= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= -github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-billy/v5 v5.1.0 h1:4pl5BV4o7ZG/lterP4S6WzJ6xr49Ba5ET9ygheTYahk= -github.com/go-git/go-billy/v5 v5.1.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12 h1:PbKy9zOy4aAKrJ5pibIRpVO2BXnK1Tlcg+caKI7Ox5M= -github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= -github.com/go-git/go-git/v5 v5.3.0 h1:8WKMtJR2j8RntEXR/uvTKagfEt4GYlwQ7mntE4+0GWc= -github.com/go-git/go-git/v5 v5.3.0/go.mod h1:xdX4bWJ48aOrdhnl2XqHYstHbbp6+LFS4r4X+lNVprw= +github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= +github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8= +github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= +github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4= +github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-ldap/ldap/v3 v3.3.0 h1:lwx+SJpgOHd8tG6SumBQZXCmNX51zM8B1cfxJ5gv4tQ= github.com/go-ldap/ldap/v3 v3.3.0/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= @@ -440,9 +419,8 @@ github.com/go-openapi/validate v0.20.2/go.mod h1:e7OJoKNgd0twXZwIn0A43tHbvIcr/rZ 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-redis/redis/v8 v8.4.0/go.mod h1:A1tbYoHSa1fXwN+//ljcCYYJeLmVrwL9hbQN45Jdy0M= -github.com/go-redis/redis/v8 v8.8.2 h1:O/NcHqobw7SEptA0yA6up6spZVFtwE06SXM8rgLtsP8= -github.com/go-redis/redis/v8 v8.8.2/go.mod h1:F7resOH5Kdug49Otu24RjHWwgK7u9AmtqWMnCV1iP5Y= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-redis/redis/v8 v8.10.0 h1:OZwrQKuZqdJ4QIM8wn8rnuz868Li91xA3J2DEq+TPGA= +github.com/go-redis/redis/v8 v8.10.0/go.mod h1:vXLTvigok0VtUX0znvbcEW1SOt4OA9CU1ZfnOtKOaiM= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= @@ -453,8 +431,8 @@ github.com/go-swagger/go-swagger v0.27.0 h1:K7+nkBuf4oS1jTBrdvWqYFpqD69V5CN8HamZ github.com/go-swagger/go-swagger v0.27.0/go.mod h1:WodZVysInJilkW7e6IRw+dZGp5yW6rlMFZ4cb+THl9A= github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013 h1:l9rI6sNaZgNC0LnF3MiE+qTmyBA/tZAg1rtyrGbUMK0= github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013/go.mod h1:b65mBPzqzZWxOZGxSWrqs4GInLIn+u99Q9q7p+GKni0= -github.com/go-testfixtures/testfixtures/v3 v3.6.0 h1:fHrJWcZ0TOHA0UcExV0Nwx+5MR9QXVDWYdVfwe4DfmM= -github.com/go-testfixtures/testfixtures/v3 v3.6.0/go.mod h1:YUBpgqvleDRhkx4MQbzdA7A3G5ca2wLtf9bHbDqNaRQ= +github.com/go-testfixtures/testfixtures/v3 v3.6.1 h1:n4Fv95Exp0D05G6l6CAZv22Ck1EJK0pa0TfPqE4ncSs= +github.com/go-testfixtures/testfixtures/v3 v3.6.1/go.mod h1:Bsb2MoHAfHnNsPpSwAjtOs102mqDuM+1u3nE2OCi0N0= github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= @@ -483,9 +461,7 @@ github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28 h1:gBeyun7mySAKWg7Fb0GOcv0upX9bdaZScs8QcRo8mEY= github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14= @@ -496,7 +472,6 @@ github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85/go.mod h1:fR6z github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -578,14 +553,11 @@ github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKp 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= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/csrf v1.6.0/go.mod h1:7tSf8kmjNYr7IWDCYhd3U8Ck34iQ/Yw5CJu7bAkHEGI= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= -github.com/gorilla/handlers v1.4.1/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1 h1:LqbZZ9sNMWVjeXS4NN5oVvhMjDyLhmA1LG86oSo+IqY= @@ -596,17 +568,12 @@ github.com/gorilla/sessions v1.1.1/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= @@ -616,8 +583,9 @@ github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrj github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-retryablehttp v0.6.8 h1:92lWxgpa+fF3FozM4B3UZtHZMJX8T5XT+TFdCxsPyWs= github.com/hashicorp/go-retryablehttp v0.6.8/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4= +github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= @@ -625,6 +593,7 @@ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= @@ -635,13 +604,11 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/issue9/assert v1.4.1 h1:gUtOpMTeaE4JTe9kACma5foOHBvVt1p5XTFrULDwdXI= github.com/issue9/assert v1.4.1/go.mod h1:Yktk83hAVl1SPSYtd9kjhBizuiBIqUQyj+D5SE2yjVY= github.com/issue9/identicon v1.2.0 h1:ek+UcTTyMW/G0iNbLOAlrPC13eSzXTWhbJSs8PHhHGQ= @@ -692,10 +659,8 @@ github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7 h1:g0fAGBisHaE github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= @@ -705,10 +670,9 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= @@ -731,16 +695,16 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/compress v1.4.1/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.12.1 h1:/+xsCsk06wE38cyiqOR/o7U2fSftcH72xD+BQXmja/g= -github.com/klauspost/compress v1.12.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.13.0 h1:2T7tUoQrQT+fQWdaY5rjWztFGAFwbGD04iPJg90ZiOs= +github.com/klauspost/compress v1.13.0/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.2.5/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s= github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.0.4 h1:g0I61F2K2DjRHz1cnxlkNSBIaePVoJIjjnHui8QHbiw= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.6 h1:dQ5ueTiftKxp0gyjKSx5+8BtPWkyQbd95m8Gys/RarI= +github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= @@ -766,18 +730,15 @@ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.1 h1:6VXZrLU0jHBYyAqrSPa+MgPfnSvTPuMgK+k0o5kVFWo= -github.com/lib/pq v1.10.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/libdns/libdns v0.2.0 h1:ewg3ByWrdUrxrje8ChPVMBNcotg7H9LQYg+u5De2RzI= +github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= +github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libdns/libdns v0.2.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= -github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= -github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis= +github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 h1:uNwtsDp7ci48vBTTxDuwcoTXz4lwtDTe7TjCQ0noaWY= github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96/go.mod h1:mmIfjCSQlGYXmJ95jFN84AkQFnVABtKuJL8IrzwvUKQ= 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/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= @@ -795,6 +756,8 @@ github.com/markbates/goth v1.67.1 h1:gU5B0pzHVyhnJPwGynfFnkfvaQ39C1Sy+ewdl+bhAOw github.com/markbates/goth v1.67.1/go.mod h1:EyLFHGU5ySr2GXRDyJH5nu2dA7parbC8QwIYW/rGcWg= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= +github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -805,12 +768,12 @@ github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= +github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.12 h1:Y41i/hVW3Pgwr8gV+J23B9YEY0zxjptBuCWEaxmAOow= -github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= @@ -822,12 +785,12 @@ github.com/mholt/acmez v0.1.3 h1:J7MmNIk4Qf9b8mAGqAh4XkNeowv3f1zW816yf4zt7Qk= github.com/mholt/acmez v0.1.3/go.mod h1:8qnn8QA/Ewx8E3ZSsmscqsIjhhpxuy9vqdgbX2ceceM= github.com/mholt/archiver/v3 v3.5.0 h1:nE8gZIrw66cu4osS/U7UW7YDuGMHssxKutU8IfWxwWE= github.com/mholt/archiver/v3 v3.5.0/go.mod h1:qqTTPUK/HZPFgFQ/TJ3BzvTpF/dPtFVJXdQbCmeMxwc= -github.com/microcosm-cc/bluemonday v1.0.8 h1:JGc6zQRHqlp+UlLrsbUbbp0mOaJLV44vvQmBSU0Sfj0= -github.com/microcosm-cc/bluemonday v1.0.8/go.mod h1:HOT/6NaBlR0f9XlxD3zolN6Z3N8Lp4pvhp+jLS5ihnI= +github.com/microcosm-cc/bluemonday v1.0.9 h1:dpCwruVKoyrULicJwhuY76jB+nIxRVKv/e248Vx/BXg= +github.com/microcosm-cc/bluemonday v1.0.9/go.mod h1:B2riunDr9benLHghZB7hjIgdwSUzzs0pjCxFrWYEZFU= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.30/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -github.com/miekg/dns v1.1.40 h1:pyyPFfGMnciYUk/mXpKkVmeMQjfXqt3FAJ2hy7tPiLA= -github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/miekg/dns v1.1.42 h1:gWGe42RGaIqXQZ+r3WUGEKBEtvPHY2SXo4dqixDNxuY= +github.com/miekg/dns v1.1.42/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= @@ -867,28 +830,17 @@ github.com/msteinert/pam v0.0.0-20201130170657-e61372126161 h1:XQ1+fYPzaWZCVdu1x github.com/msteinert/pam v0.0.0-20201130170657-e61372126161/go.mod h1:np1wUFZ6tyoke22qDJZY40URn9Ae51gX7ljIWXN5TJs= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= -github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= -github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/niklasfasching/go-org v1.5.0 h1:V8IwoSPm/d61bceyWFxxnQLtlvNT+CjiYIhtZLdnMF0= github.com/niklasfasching/go-org v1.5.0/go.mod h1:sSb8ylwnAG+h8MGFDB3R1D5bxf8wA08REfhjShg3kjA= -github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E= github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ= github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/oliamb/cutter v0.2.2 h1:Lfwkya0HHNU1YLnGv2hTkzHfasrSMkgv4Dn+5rmlk3k= @@ -909,17 +861,7 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= github.com/onsi/gomega v1.10.5 h1:7n6FEkpFmfCoo2t+YYqXH0evK+a9ICQz0xcAy9dYcaQ= github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= -github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= -github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= -github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= -github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -928,19 +870,14 @@ github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAv github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pelletier/go-toml v1.9.0 h1:NOd0BRdOKpPf0SxkL3HxSQOG7rNh+4kl6PHcBPFs7Q0= github.com/pelletier/go-toml v1.9.0/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= -github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= -github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4/v4 v4.0.3/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pierrec/lz4/v4 v4.1.3 h1:/dvQpkb0o1pVlSgKNQqfkavlnXaIK+hJ0LXsKRUN9D4= -github.com/pierrec/lz4/v4 v4.1.3/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.7 h1:UDV9geJWhFIufAliH7HQlz9wP3JA0t748w+RwbWMLow= +github.com/pierrec/lz4/v4 v4.1.7/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -949,53 +886,45 @@ github.com/pquerna/cachecontrol v0.0.0-20201205024021-ac21108117ac/go.mod h1:hoL github.com/pquerna/otp v1.3.0 h1:oJV/SkzR33anKXwQU3Of42rL4wbrffP4uvUf1SvS5Xs= github.com/pquerna/otp v1.3.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.10.0 h1:/o0BDeWzLWXNZ+4q5gXltUvaMpJqckTa+jTNoB+z4cg= -github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= +github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.18.0 h1:WCVKW7aL6LEe1uryfI9dnEc2ZqNB1Fn0ok930v0iL1Y= -github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/quasoft/websspi v1.0.0 h1:5nDgdM5xSur9s+B5w2xQ5kxf5nUGqgFgU4W0aDLZ8Mw= github.com/quasoft/websspi v1.0.0/go.mod h1:HmVdl939dQ0WIXZhyik+ARdI03M6bQzaSEKcgpFmewk= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/robertkrimen/godocdown v0.0.0-20130622164427-0bfa04905481/go.mod h1:C9WhFzY47SzYBIvzFqSvHIR6ROgDo4TtdTuRaOMjF/s= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/xid v1.3.0 h1:6NjYksEUlhurdVehpc7S7dk6DAmcKv8V9gG0FsVN2U4= +github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= @@ -1004,7 +933,6 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= @@ -1039,7 +967,6 @@ github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIK github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/gunit v1.4.2/go.mod h1:ZjM1ozSIMJlAz/ay4SG8PeKF00ckUp+zMHZXV9/bvak= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= @@ -1053,7 +980,6 @@ github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSW github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -1063,11 +989,9 @@ github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM= +github.com/stephens2424/writerset v1.0.2/go.mod h1:aS2JhsMn6eA7e82oNmW4rfsgAOp9COBTTl8mzkwADnc= github.com/steveyen/gtreap v0.1.0 h1:CjhzTa274PyJLJuMZwIzCO1PfC00oRa8d1Kc78bFXJM= github.com/steveyen/gtreap v0.1.0/go.mod h1:kl/5J7XbrOmlIbYIXdRHDDE5QxHqpk0cmkT7Z4dM9/Y= -github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -1085,7 +1009,6 @@ github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpP github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/toqueteos/webbrowser v1.2.0 h1:tVP/gpK69Fx+qMJKsLE7TD8LuGWPnEV71wBN9rrstGQ= github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM= @@ -1103,20 +1026,14 @@ github.com/unknwon/i18n v0.0.0-20210321134014-0ebbf2df1c44 h1:7bSo/vjZKVYUoZfxpY github.com/unknwon/i18n v0.0.0-20210321134014-0ebbf2df1c44/go.mod h1:+5rDk6sDGpl3azws3O+f+GpFSyN9GVr0K8cvQLQM2ZQ= github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae h1:ihaXiJkaca54IaCSnEXtE/uSZOmPxKZhDfVLrzZLFDs= github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae/go.mod h1:1fdkY6xxl6ExVs2QFv7R0F5IRZHKA8RahhB9fMC9RvM= -github.com/unrolled/render v1.1.1 h1:FpzNzkvlJQIlVdVaqeVBGWiCS8gpbmjtrKpDmCn6p64= -github.com/unrolled/render v1.1.1/go.mod h1:gN9T0NhL4Bfbwu8ann7Ry/TGHYfosul+J0obPf6NBdM= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/unrolled/render v1.4.0 h1:p73obhpsXuE3paXOtcuXTBKgBJpLCfmABnsUiO35x+Q= +github.com/unrolled/render v1.4.0/go.mod h1:cK4RSTTVdND5j9EYEc0LAMOvdG11JeiKjyjfyZRvV2w= github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU= github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/willf/bitset v1.1.11 h1:N7Z7E9UvjW+sGsEl7k/SJrvY2reP1A07MrGuCjIOjRE= -github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= -github.com/xanzy/go-gitlab v0.48.0 h1:RP9r4pMDIwE2fbtc+QYiC1euDsPGHcAjPkhje4X3QPU= -github.com/xanzy/go-gitlab v0.48.0/go.mod h1:UW8JJbyBbqtOyBYNHRo261IRdHUFJr2m0y0z1xUiu+E= +github.com/xanzy/go-gitlab v0.50.0 h1:t7IoYTrnLSbdEZN7d8X/5zcr+ZM4TZQ2mXa8MqWlAZQ= +github.com/xanzy/go-gitlab v0.50.0/go.mod h1:Q+hQhV508bDPoBijv7YjK/Lvlb4PhVhJdKqXVQrUoAE= github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= @@ -1131,24 +1048,23 @@ github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1: github.com/yohcop/openid-go v1.0.0 h1:EciJ7ZLETHR3wOtxBvKXx9RV6eyHZpCaSZ1inbBaUXE= github.com/yohcop/openid-go v1.0.0/go.mod h1:/408xiwkeItSPJZSTPF7+VtZxPkPrRRpRNK2vjGh6yI= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= -github.com/yuin/goldmark v1.1.22/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5 h1:dPmz1Snjq0kmkz159iL7S6WzdahUTHnHB5M56WFVifs= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691 h1:VWSxtAiQNh3zgHJpdpkpVYjTPqRE3P6UZCOPa1nRDio= -github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691/go.mod h1:YLF3kDffRfUH/bTxOxHhV6lxwIB3Vfj91rEwNMS9MXo= +github.com/yuin/goldmark v1.3.6/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.3.7 h1:NSaHgaeJFCtWXCBkBKXw0rhgMuJ0VoE9FB5mWldcrQ4= +github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark-highlighting v0.0.0-20210516132338-9216f9c5aa01 h1:0SJnXjE4jDClMW6grE0xpNhwpqbPwkBTn8zpVw5C0SI= +github.com/yuin/goldmark-highlighting v0.0.0-20210516132338-9216f9c5aa01/go.mod h1:TwKQPa5XkCCRC2GRZ5wtfNUTQ2+9/i19mGRijFeJ4BE= github.com/yuin/goldmark-meta v1.0.0 h1:ScsatUIT2gFS6azqzLGUjgOnELsBOxMXerM3ogdJhAM= github.com/yuin/goldmark-meta v1.0.0/go.mod h1:zsNNOrZ4nLuyHAJeLQEZcQat8dm70SmB2kHbls092Gc= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= +go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.jolheiser.com/hcaptcha v0.0.4 h1:RrDERcr/Tz/kWyJenjVtI+V09RtLinXxlAemiwN5F+I= go.jolheiser.com/hcaptcha v0.0.4/go.mod h1:aw32WQOxnQZ6E06C0LypCf+sxNxPACyOnq+ZGnrIYho= go.jolheiser.com/pwn v0.0.3 h1:MQowb3QvCL5r5NmHmCPxw93SdjfgJ0q6rAwYn4i1Hjg= @@ -1162,8 +1078,6 @@ go.mongodb.org/mongo-driver v1.4.4/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4S go.mongodb.org/mongo-driver v1.4.6/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= go.mongodb.org/mongo-driver v1.5.1 h1:9nOVLGDfOaZ9R0tBumx/BcuqkbFpyTCU2r/Po7A2azI= go.mongodb.org/mongo-driver v1.5.1/go.mod h1:gRXCHX4Jo7J0IJ1oDQyUxF7jfy19UfxniMS4xxMmUqw= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -1172,32 +1086,30 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/otel v0.14.0/go.mod h1:vH5xEuwy7Rts0GNtsCW3HYQoZDY+OmBJ6t1bFGGlxgw= -go.opentelemetry.io/otel v0.19.0 h1:Lenfy7QHRXPZVsw/12CWpxX6d/JkrX8wrx2vO8G80Ng= -go.opentelemetry.io/otel v0.19.0/go.mod h1:j9bF567N9EfomkSidSfmMwIwIBuP37AMAIzVW85OxSg= -go.opentelemetry.io/otel/metric v0.19.0 h1:dtZ1Ju44gkJkYvo+3qGqVXmf88tc+a42edOywypengg= -go.opentelemetry.io/otel/metric v0.19.0/go.mod h1:8f9fglJPRnXuskQmKpnad31lcLJ2VmNNqIsx/uIwBSc= -go.opentelemetry.io/otel/oteltest v0.19.0 h1:YVfA0ByROYqTwOxqHVZYZExzEpfZor+MU1rU+ip2v9Q= -go.opentelemetry.io/otel/oteltest v0.19.0/go.mod h1:tI4yxwh8U21v7JD6R3BcA/2+RBoTKFexE/PJ/nSO7IA= -go.opentelemetry.io/otel/trace v0.19.0 h1:1ucYlenXIDA1OlHVLDZKX0ObXV5RLaq06DtUKz5e5zc= -go.opentelemetry.io/otel/trace v0.19.0/go.mod h1:4IXiNextNOpPnRlI4ryK69mn5iC84bjBWZQA5DXz/qg= +go.opentelemetry.io/otel v0.20.0 h1:eaP0Fqu7SXHwvjiqDq83zImeehOHX8doTvU9AwXON8g= +go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= +go.opentelemetry.io/otel/metric v0.20.0 h1:4kzhXFP+btKm4jwxpjIqjs41A7MakRFUS86bqLHTIw8= +go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= +go.opentelemetry.io/otel/oteltest v0.20.0 h1:HiITxCawalo5vQzdHfKeZurV8x7ljcqAgiWzF6Vaeaw= +go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= +go.opentelemetry.io/otel/trace v0.20.0 h1:1DL6EXUdcg95gukhuRRvLDO/4X5THh/5dIV52lqtnbw= +go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= +go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= -go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM= -go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= +go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1212,7 +1124,6 @@ golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -1223,8 +1134,9 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh 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-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1247,7 +1159,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= @@ -1267,12 +1178,10 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/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-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -1317,9 +1226,9 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/net v0.0.0-20210331060903-cb1fcc7394e5/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210421230115-4e50805a0758 h1:aEpZnXcAmXkd6AvLb2OPt+EN1Zu/8Ne3pCqPjja5PXY= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5 h1:wjuX4b5yYQnEQHzd+CBcrcC6OVR2J1CN6mUy0oSxIPo= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1331,8 +1240,8 @@ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78 h1:rPRtHfUb0UKZeZ6GH4K4Nt4YRbE9V1u+QZX5upZXqJQ= -golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c h1:pkQiBZBvdos9qq4wBAHqlzuZHEXo07pqV06ef90u1WI= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1343,8 +1252,9 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1352,7 +1262,6 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1383,7 +1292,6 @@ golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1405,6 +1313,7 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1414,13 +1323,17 @@ golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210421221651-33663a62ff08 h1:qyN5bV+96OX8pL78eXDuz6YlDPzCYgdW74H5yE9BoSU= -golang.org/x/sys v0.0.0-20210421221651-33663a62ff08/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210608053332-aa57babbf139 h1:C+AwYEtBp/VQwoLntUmQ/yx3MS9vmZaKNdw5eOpoQe8= +golang.org/x/sys v0.0.0-20210608053332-aa57babbf139/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1432,14 +1345,12 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210608053304-ed9ce3a009e4 h1:1asO3s7vR+9MvZSNRwUBBTjecxbGtfvmxjy2VWbFR5g= +golang.org/x/time v0.0.0-20210608053304-ed9ce3a009e4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1477,7 +1388,6 @@ golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -1515,7 +1425,6 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1537,7 +1446,6 @@ google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1551,7 +1459,6 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= @@ -1583,15 +1490,10 @@ google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= @@ -1627,10 +1529,8 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -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= @@ -1660,14 +1560,12 @@ gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= modernc.org/cc/v3 v3.31.5-0.20210308123301-7a3e9dab9009 h1:u0oCo5b9wyLr++HF3AN9JicGhkUxJhMz51+8TIZH9N0= modernc.org/cc/v3 v3.31.5-0.20210308123301-7a3e9dab9009/go.mod h1:0R6jl1aZlIl2avnYfbfHBS1QB6/f+16mihBObaBC878= @@ -1701,8 +1599,6 @@ mvdan.cc/xurls/v2 v2.2.0/go.mod h1:EV1RMtya9D6G5DMYPGD8zTQzaHet6Jh8gFlRgGRJeO8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 h1:mUcz5b3FJbP5Cvdq7Khzn6J9OCUQJaBwgBkCR+MOwSs= strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY= xorm.io/builder v0.3.7/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= diff --git a/integrations/api_admin_test.go b/integrations/api_admin_test.go index b3a0de36d369e..b93179d21df0b 100644 --- a/integrations/api_admin_test.go +++ b/integrations/api_admin_test.go @@ -195,7 +195,7 @@ func TestAPIEditUser(t *testing.T) { assert.EqualValues(t, "email is not allowed to be empty string", errMap["message"].(string)) user2 := models.AssertExistsAndLoadBean(t, &models.User{LoginName: "user2"}).(*models.User) - assert.Equal(t, false, user2.IsRestricted) + assert.False(t, user2.IsRestricted) bTrue := true req = NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{ // required @@ -206,5 +206,5 @@ func TestAPIEditUser(t *testing.T) { }) session.MakeRequest(t, req, http.StatusOK) user2 = models.AssertExistsAndLoadBean(t, &models.User{LoginName: "user2"}).(*models.User) - assert.Equal(t, true, user2.IsRestricted) + assert.True(t, user2.IsRestricted) } diff --git a/integrations/api_gpg_keys_test.go b/integrations/api_gpg_keys_test.go index e664c3c256553..b4f19031afc17 100644 --- a/integrations/api_gpg_keys_test.go +++ b/integrations/api_gpg_keys_test.go @@ -36,7 +36,6 @@ func TestGPGKeys(t *testing.T) { } for _, tc := range tt { - //Basic test on result code t.Run(tc.name, func(t *testing.T) { t.Run("ViewOwnGPGKeys", func(t *testing.T) { @@ -78,42 +77,42 @@ func TestGPGKeys(t *testing.T) { primaryKey1 := keys[0] //Primary key 1 assert.EqualValues(t, "38EA3BCED732982C", primaryKey1.KeyID) - assert.EqualValues(t, 1, len(primaryKey1.Emails)) + assert.Len(t, primaryKey1.Emails, 1) assert.EqualValues(t, "user2@example.com", primaryKey1.Emails[0].Email) - assert.EqualValues(t, true, primaryKey1.Emails[0].Verified) + assert.True(t, primaryKey1.Emails[0].Verified) subKey := primaryKey1.SubsKey[0] //Subkey of 38EA3BCED732982C assert.EqualValues(t, "70D7C694D17D03AD", subKey.KeyID) - assert.EqualValues(t, 0, len(subKey.Emails)) + assert.Empty(t, subKey.Emails) primaryKey2 := keys[1] //Primary key 2 - assert.EqualValues(t, "FABF39739FE1E927", primaryKey2.KeyID) - assert.EqualValues(t, 1, len(primaryKey2.Emails)) - assert.EqualValues(t, "user21@example.com", primaryKey2.Emails[0].Email) - assert.EqualValues(t, false, primaryKey2.Emails[0].Verified) + assert.EqualValues(t, "3CEF46EF40BEFC3E", primaryKey2.KeyID) + assert.Len(t, primaryKey2.Emails, 1) + assert.EqualValues(t, "user2-2@example.com", primaryKey2.Emails[0].Email) + assert.False(t, primaryKey2.Emails[0].Verified) var key api.GPGKey req = NewRequest(t, "GET", "/api/v1/user/gpg_keys/"+strconv.FormatInt(primaryKey1.ID, 10)+"?token="+token) //Primary key 1 resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &key) assert.EqualValues(t, "38EA3BCED732982C", key.KeyID) - assert.EqualValues(t, 1, len(key.Emails)) + assert.Len(t, key.Emails, 1) assert.EqualValues(t, "user2@example.com", key.Emails[0].Email) - assert.EqualValues(t, true, key.Emails[0].Verified) + assert.True(t, key.Emails[0].Verified) req = NewRequest(t, "GET", "/api/v1/user/gpg_keys/"+strconv.FormatInt(subKey.ID, 10)+"?token="+token) //Subkey of 38EA3BCED732982C resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &key) assert.EqualValues(t, "70D7C694D17D03AD", key.KeyID) - assert.EqualValues(t, 0, len(key.Emails)) + assert.Empty(t, key.Emails) req = NewRequest(t, "GET", "/api/v1/user/gpg_keys/"+strconv.FormatInt(primaryKey2.ID, 10)+"?token="+token) //Primary key 2 resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &key) - assert.EqualValues(t, "FABF39739FE1E927", key.KeyID) - assert.EqualValues(t, 1, len(key.Emails)) - assert.EqualValues(t, "user21@example.com", key.Emails[0].Email) - assert.EqualValues(t, false, key.Emails[0].Verified) + assert.EqualValues(t, "3CEF46EF40BEFC3E", key.KeyID) + assert.Len(t, key.Emails, 1) + assert.EqualValues(t, "user2-2@example.com", key.Emails[0].Email) + assert.False(t, key.Emails[0].Verified) }) @@ -124,7 +123,7 @@ func TestGPGKeys(t *testing.T) { req := NewRequest(t, "GET", "/api/v1/repos/user2/repo16/branches/not-signed?token="+token) resp := session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &branch) - assert.EqualValues(t, false, branch.Commit.Verification.Verified) + assert.False(t, branch.Commit.Verification.Verified) }) t.Run("SignedWithNotValidatedEmail", func(t *testing.T) { @@ -132,7 +131,7 @@ func TestGPGKeys(t *testing.T) { req := NewRequest(t, "GET", "/api/v1/repos/user2/repo16/branches/good-sign-not-yet-validated?token="+token) resp := session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &branch) - assert.EqualValues(t, false, branch.Commit.Verification.Verified) + assert.False(t, branch.Commit.Verification.Verified) }) t.Run("SignedWithValidEmail", func(t *testing.T) { @@ -140,7 +139,7 @@ func TestGPGKeys(t *testing.T) { req := NewRequest(t, "GET", "/api/v1/repos/user2/repo16/branches/good-sign?token="+token) resp := session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &branch) - assert.EqualValues(t, true, branch.Commit.Verification.Verified) + assert.True(t, branch.Commit.Verification.Verified) }) }) } @@ -231,35 +230,46 @@ uy6MA3VSB99SK9ducGmE1Jv8mcziREroz2TEGr0zPs6h } func testCreateValidSecondaryEmailGPGKey(t *testing.T, makeRequest makeRequestFunc, token string, expected int) { - //User2 //secondary and not activated + //User2 //secondary and not activated testCreateGPGKey(t, makeRequest, token, expected, `-----BEGIN PGP PUBLIC KEY BLOCK----- -mQENBFmGWN4BCAC18V4tVGO65VLCV7p14FuXJlUtZ5CuYMvgEkcOqrvRaBSW9ao4 -PGESOhJpfWpnW3QgJniYndLzPpsmdHEclEER6aZjiNgReWPOjHD5tykWocZAJqXD -eY1ym59gvVMLcfbV2yQsyR2hbJlc+dJsl16tigSEe3nwxZSw2IsW92pgEzT9JNUr -Q+mC8dw4dqY0tYmFazYUGNxufUc/twgQT/Or1aNs0az5Q6Jft4rrTRsh/S7We0VB -COKGkdcQyYgAls7HJBuPjQRi6DM9VhgBSHLAgSLyaUcZvhZBJr8Qe/q4PP3/kYDJ -wm4RMnjOLz2pFZPgtRqgcAwpmFtLrACbEB3JABEBAAG0GlVzZXIyIDx1c2VyMjFA -ZXhhbXBsZS5jb20+iQFUBBMBCAA+FiEEPOLHOjPSO42DWM57+r85c5/h6ScFAlmG -WN4CGwMFCQPCZwAFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AACgkQ+r85c5/h6Sfx -Lgf/dq64NBV8+X9an3seaLxePRviva48e4K67/wV/JxtXNO5Z/DhMGz5kHXCsG9D -CXuWYO8ehlTjEnMZ6qqdDnY+H6bQsb2OS5oPn4RwpPXslAjEKtojPAr0dDsMS2DB -dUuIm1AoOnewOVO0OFRf1EqX1bivxnN0FVMcO0m8AczfnKDaGb0y/qg/Y9JAsKqp -j5pZNMWUkntRtGySeJ4CVJMmkVKJAHsa1Qj6MKdFeid4h4y94cBJ4ZdyBxNdpQOx -ydf0doicovfeqGNO4oWzsGP4RBK2CqGPCUT+EFl20jPvMkKwOjxgqc8p0z3b2UT9 -+9bnmCGHgF/fW1HJ3iKmfFPqnLkBDQRZhljeAQgA5AirU/NJGgm19ZJYFOiHftjS -azbrPxGeD3cSqmvDPIMc1DNZGfQV5D4EVumnVbQBtL6xHFoGKz9KisUMbe4a/X2J -S8JmIphQWG0vMJX1DaZIzr2gT71MnPD7JMGsSUCh5dIKpTNTZX4w+oGPGOu0/UlL -x0448AryKwp30J2p6D4GeI0nb03n35S2lTOpnHDn1wj7Jl/8LS2fdFOdNaNHXSZe -twdSwJKhyBEiScgeHBDyKqo8zWkYoSb9eA2HiYlbVaiNtp24KP1mIEpiUdrRjWno -zauYSZGHZlOFMgF4dKWuetPiuH9m7UYZGKyMLfQ9vYFb+xcPh2bLCQHJ1OEmMQAR -AQABiQE8BBgBCAAmFiEEPOLHOjPSO42DWM57+r85c5/h6ScFAlmGWN4CGwwFCQPC -ZwAACgkQ+r85c5/h6Sfjfwf+O4WEjRdvPJLxNy7mfAGoAqDMHIwyH/tVzYgyVhnG -h/+cfRxJbGc3rpjYdr8dmvghzjEAout8uibPWaIqs63RCAPGPqgWLfxNO5c8+y8V -LZMVOTV26l2olkkdBWAuhLqKTNh6TiQva03yhOgHWj4XDvFfxICWPFXVd6t5ELpD -iApGu1OAj8JfhmzbG03Yzx+Ku7bWDxMonx3V/IDEu5LS5zrboHYDKCA53bXXghoi -Aceqql+PKrDwEjoY4bptwMHLmcjGjdCQ//Qx1neho7nZcS7xjTucY8gQuulwCyXF -y6wM+wMz8dunIG9gw4+Re6c4Rz9tX1kzxLrU7Pl21tMqfg== -=0N/9 +mQGNBGC2K2cBDAC1+Xgk+8UfhASVgRngQi4rnQ8k0t+bWsBz4Czd26+cxVDRwlTT +8PALdrbrY/e9iXjcVcZ8Npo4UYe7/LfnL57dc7tgbenRGYYrWyVoNNv58BVw4xCY +RmgvdHWIIPGuz3aME0smHxbJ2KewYTqjTPuVKF/wrHTwCpVWdjYKC5KDo3yx0mro +xf9vOJOnkWNMiEw7TiZfkrbUqxyA53BVsSNKRX5C3b4FJcVT7eiAq7sDAaFxjEHy +ahZslmvg7XZxWzSVzxDNesR7f4xuop8HBjzaluJoVuwiyWculTvz1b6hyHVQr+ad +h8JGjj1tySI65OTFsTuptsfHXjtjl/NR4P6BXkf+FVwweaTQaEzpHkv0m9b9pY43 +CY/8XtS4uNPermiLG/Z0BB1eOCdoOQVHpjOa55IXQWhxXB6NZVyowiUbrR7jLDQy +5JP7D1HmErTR8JRm3VDqGbSaCgugRgFX+lb/fpgFp9k02OeK+JQudolZOt1mVk+T +C4xmEWxfiH15/JMAEQEAAbQbdXNlcjIgPHVzZXIyLTJAZXhhbXBsZS5jb20+iQHU +BBMBCAA+FiEEB/Y4DM3Ba2H9iXmlPO9G70C+/D4FAmC2K2cCGwMFCQPCZwAFCwkI +BwIGFQoJCAsCBBYCAwECHgECF4AACgkQPO9G70C+/D59/Av/XZIhCH4X2FpxCO3d +oCa+sbYkBL5xeUoPfAx5ThXzqL/tllO88TKTMEGZF3k5pocXWH0xmhqlvDTcdb0i +W3O0CN8FLmuotU51c0JC1mt9zwJP9PeJNyqxrMm01Yzj55z/Dz3QHSTlDjrWTWjn +YBqDf2HfdM177oydfSYmevZni1aDmBalWpFPRvqISCO7uFnvg1hJQ5mD/0qie663 +QJ8LAAANg32H9DyPnYi9wU62WX0DMUVTjKctT3cnYCbirjjJ7ZlCCm+cf61CRX1B +E1Ng/Ef3ZcUfXWitZSjfET/pKEMSNjsQawFpZ/LPCBl+UPHzaTPAASeGJvcbZ3py +wZQLQc1MCu2hmMBQ8zHQTdS2Pp0RISxCQLYvVQL6DrcJDNiSqn9p9RQt5c5r5Pjx +80BIPcjj3glOVP7PYE2azQAkt6reEjhimwCfjeDpiPnkBTY7Av2jCcUFhhemDY/j +TRXK1paLphhJ36zC22SeHGxNNakjjuUakqB85DEUeoWuVm6ouQGNBGC2K2cBDADx +G2rIAgMjdPtofhkEZXwv6zdNwmYOlIIM+59bam9Ep/vFq8F5f+xldevm5dvM8SeR +pNwDGSOUf5OKBWBdsJFhlYBl7+EcKd/Tent/XS6JoA9ffF33b+r04L543+ykiKON +WYeYi0F4WwYTIQgqZHJze1sPVkYGR5F0bL8PAcLuwd5dzZVi/q2HakrGdg29N8oY +b/XnoR7FflPrNYdzO6hawi5Inx7KS7aWa0ZkARb0F4HSct+/m6nAZVsoJINLudyQ +ut2NWeU8rWIm1hqyIxQFvuQJy46umq++10J/sWA98bkg41Rx+72+eP7DM5v8IgUp +clJsfljRXIBWbmRAVZvtNI7PX9fwMMhf4M7wHO7G2WV39o1exKps5xFFcn8PUQiX +jCSR81M145CgCdmLUR1y0pdkN/WIqjXBhkPIvO2dxEcodMNHb1aUUuUOnww6+xIP +8rGVw+a2DUiALc8Qr5RP21AYKRctfiwhSQh2KODveMtyLI3U9C/eLRPp+QM3XB8A +EQEAAYkBvAQYAQgAJhYhBAf2OAzNwWth/Yl5pTzvRu9Avvw+BQJgtitnAhsMBQkD +wmcAAAoJEDzvRu9Avvw+3FcMAJBwupyJ4zwQFxTJ5BkDlusG3U2FXEf3bDrXhvNd +qi8eS8Vo/vRiH/w/my5JFpz1o2tJToryF71D+uF5DTItalKquhsQ9reAEmXggqOh +9Jd9mWJIEEWcRORiLNDKENKvE8bouw4U4hRaSF0IaGzAe5mO+oOvwal8L97wFxrZ +4leM1GzkopiuNfbkkBBw2KJcMjYBHzzXSCALnVwhjbgkBEWPIg38APT3cr9KfnMM +q8+tvsGLj4piAl3Lww7+GhSsDOUXH8btR41BSAQDrbO5q6oi/h4nuxoNmQIDW/Ug +s+dd5hnY2FtHRjb4FCR9kAjdTE6stc8wzohWfbg1N+12TTA2ylByAumICVXixavH +RJ7l0OiWJk388qw9mqh3k8HcBxL7OfDlFC9oPmCS0iYiIwW/Yc80kBhoxcvl/Xa7 +mIMMn8taHIaQO7v9ln2EVQYTzbNCmwTw9ovTM0j/Pbkg2EftfP1TCoxQHvBnsCED +6qgtsUdi5eviONRkBgeZtN3oxA== +=MgDv -----END PGP PUBLIC KEY BLOCK-----`) } diff --git a/integrations/api_issue_test.go b/integrations/api_issue_test.go index f932f79feea07..74512cafa3f6e 100644 --- a/integrations/api_issue_test.go +++ b/integrations/api_issue_test.go @@ -25,9 +25,10 @@ func TestAPIListIssues(t *testing.T) { session := loginUser(t, owner.Name) token := getTokenForLoggedInUser(t, session) - req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues?state=all&token=%s", - owner.Name, repo.Name, token) - resp := session.MakeRequest(t, req, http.StatusOK) + link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/issues", owner.Name, repo.Name)) + + link.RawQuery = url.Values{"token": {token}, "state": {"all"}}.Encode() + resp := session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) var apiIssues []*api.Issue DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, models.GetCount(t, &models.Issue{RepoID: repo.ID})) @@ -36,15 +37,34 @@ func TestAPIListIssues(t *testing.T) { } // test milestone filter - req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues?state=all&type=all&milestones=ignore,milestone1,3,4&token=%s", - owner.Name, repo.Name, token) - resp = session.MakeRequest(t, req, http.StatusOK) + link.RawQuery = url.Values{"token": {token}, "state": {"all"}, "type": {"all"}, "milestones": {"ignore,milestone1,3,4"}}.Encode() + resp = session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) DecodeJSON(t, resp, &apiIssues) if assert.Len(t, apiIssues, 2) { assert.EqualValues(t, 3, apiIssues[0].Milestone.ID) assert.EqualValues(t, 1, apiIssues[1].Milestone.ID) } + link.RawQuery = url.Values{"token": {token}, "state": {"all"}, "created_by": {"user2"}}.Encode() + resp = session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) + DecodeJSON(t, resp, &apiIssues) + if assert.Len(t, apiIssues, 1) { + assert.EqualValues(t, 5, apiIssues[0].ID) + } + + link.RawQuery = url.Values{"token": {token}, "state": {"all"}, "assigned_by": {"user1"}}.Encode() + resp = session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) + DecodeJSON(t, resp, &apiIssues) + if assert.Len(t, apiIssues, 1) { + assert.EqualValues(t, 1, apiIssues[0].ID) + } + + link.RawQuery = url.Values{"token": {token}, "state": {"all"}, "mentioned_by": {"user4"}}.Encode() + resp = session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) + DecodeJSON(t, resp, &apiIssues) + if assert.Len(t, apiIssues, 1) { + assert.EqualValues(t, 1, apiIssues[0].ID) + } } func TestAPICreateIssue(t *testing.T) { @@ -65,8 +85,8 @@ func TestAPICreateIssue(t *testing.T) { resp := session.MakeRequest(t, req, http.StatusCreated) var apiIssue api.Issue DecodeJSON(t, resp, &apiIssue) - assert.Equal(t, apiIssue.Body, body) - assert.Equal(t, apiIssue.Title, title) + assert.Equal(t, body, apiIssue.Body) + assert.Equal(t, title, apiIssue.Title) models.AssertExistsAndLoadBean(t, &models.Issue{ RepoID: repoBefore.ID, @@ -202,6 +222,20 @@ func TestAPISearchIssues(t *testing.T) { resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 1) + + query = url.Values{"milestones": {"milestone1"}, "state": {"all"}} + link.RawQuery = query.Encode() + req = NewRequest(t, "GET", link.String()) + resp = session.MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &apiIssues) + assert.Len(t, apiIssues, 1) + + query = url.Values{"milestones": {"milestone1,milestone3"}, "state": {"all"}} + link.RawQuery = query.Encode() + req = NewRequest(t, "GET", link.String()) + resp = session.MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &apiIssues) + assert.Len(t, apiIssues, 2) } func TestAPISearchIssuesWithLabels(t *testing.T) { diff --git a/integrations/api_notification_test.go b/integrations/api_notification_test.go index 42041734200b0..96af14fb82a9f 100644 --- a/integrations/api_notification_test.go +++ b/integrations/api_notification_test.go @@ -45,14 +45,14 @@ func TestAPINotification(t *testing.T) { assert.Len(t, apiNL, 3) assert.EqualValues(t, 4, apiNL[0].ID) - assert.EqualValues(t, true, apiNL[0].Unread) - assert.EqualValues(t, false, apiNL[0].Pinned) + assert.True(t, apiNL[0].Unread) + assert.False(t, apiNL[0].Pinned) assert.EqualValues(t, 3, apiNL[1].ID) - assert.EqualValues(t, false, apiNL[1].Unread) - assert.EqualValues(t, true, apiNL[1].Pinned) + assert.False(t, apiNL[1].Unread) + assert.True(t, apiNL[1].Pinned) assert.EqualValues(t, 2, apiNL[2].ID) - assert.EqualValues(t, false, apiNL[2].Unread) - assert.EqualValues(t, false, apiNL[2].Pinned) + assert.False(t, apiNL[2].Unread) + assert.False(t, apiNL[2].Pinned) // -- GET /repos/{owner}/{repo}/notifications -- req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/notifications?status-types=unread&token=%s", user2.Name, repo1.Name, token)) @@ -74,8 +74,8 @@ func TestAPINotification(t *testing.T) { DecodeJSON(t, resp, &apiN) assert.EqualValues(t, 5, apiN.ID) - assert.EqualValues(t, false, apiN.Pinned) - assert.EqualValues(t, true, apiN.Unread) + assert.False(t, apiN.Pinned) + assert.True(t, apiN.Unread) assert.EqualValues(t, "issue4", apiN.Subject.Title) assert.EqualValues(t, "Issue", apiN.Subject.Type) assert.EqualValues(t, thread5.Issue.APIURL(), apiN.Subject.URL) diff --git a/integrations/api_oauth2_apps_test.go b/integrations/api_oauth2_apps_test.go index 0ba56b6c9fca8..5c90dbb3bca12 100644 --- a/integrations/api_oauth2_apps_test.go +++ b/integrations/api_oauth2_apps_test.go @@ -123,7 +123,7 @@ func testAPIGetOAuth2Application(t *testing.T) { assert.EqualValues(t, existApp.ClientID, expectedApp.ClientID) assert.Len(t, expectedApp.ClientID, 36) assert.Empty(t, expectedApp.ClientSecret) - assert.EqualValues(t, len(expectedApp.RedirectURIs), 1) + assert.Len(t, expectedApp.RedirectURIs, 1) assert.EqualValues(t, existApp.RedirectURIs[0], expectedApp.RedirectURIs[0]) models.AssertExistsAndLoadBean(t, &models.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) } @@ -156,7 +156,7 @@ func testAPIUpdateOAuth2Application(t *testing.T) { DecodeJSON(t, resp, &app) expectedApp := app - assert.EqualValues(t, len(expectedApp.RedirectURIs), 2) + assert.Len(t, expectedApp.RedirectURIs, 2) assert.EqualValues(t, expectedApp.RedirectURIs[0], appBody.RedirectURIs[0]) assert.EqualValues(t, expectedApp.RedirectURIs[1], appBody.RedirectURIs[1]) models.AssertExistsAndLoadBean(t, &models.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) diff --git a/integrations/api_org_test.go b/integrations/api_org_test.go index 551da30326949..bc4428b99e150 100644 --- a/integrations/api_org_test.go +++ b/integrations/api_org_test.go @@ -69,7 +69,7 @@ func TestAPIOrgCreate(t *testing.T) { // user1 on this org is public var users []*api.User DecodeJSON(t, resp, &users) - assert.EqualValues(t, 1, len(users)) + assert.Len(t, users, 1) assert.EqualValues(t, "user1", users[0].UserName) }) } diff --git a/integrations/api_pull_review_test.go b/integrations/api_pull_review_test.go index 19b05d545b0b7..ebe8539a826d3 100644 --- a/integrations/api_pull_review_test.go +++ b/integrations/api_pull_review_test.go @@ -37,15 +37,15 @@ func TestAPIPullReview(t *testing.T) { assert.EqualValues(t, 8, reviews[3].ID) assert.EqualValues(t, "APPROVED", reviews[3].State) assert.EqualValues(t, 0, reviews[3].CodeCommentsCount) - assert.EqualValues(t, true, reviews[3].Stale) - assert.EqualValues(t, false, reviews[3].Official) + assert.True(t, reviews[3].Stale) + assert.False(t, reviews[3].Official) assert.EqualValues(t, 10, reviews[5].ID) assert.EqualValues(t, "REQUEST_CHANGES", reviews[5].State) assert.EqualValues(t, 1, reviews[5].CodeCommentsCount) assert.EqualValues(t, -1, reviews[5].Reviewer.ID) // ghost user - assert.EqualValues(t, false, reviews[5].Stale) - assert.EqualValues(t, true, reviews[5].Official) + assert.False(t, reviews[5].Stale) + assert.True(t, reviews[5].Official) // test GetPullReview req = NewRequestf(t, http.MethodGet, "/api/v1/repos/%s/%s/pulls/%d/reviews/%d?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, reviews[3].ID, token) @@ -118,14 +118,14 @@ func TestAPIPullReview(t *testing.T) { resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &review) assert.EqualValues(t, 6, review.ID) - assert.EqualValues(t, true, review.Dismissed) + assert.True(t, review.Dismissed) // test dismiss review req = NewRequest(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews/%d/undismissals?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, review.ID, token)) resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &review) assert.EqualValues(t, 6, review.ID) - assert.EqualValues(t, false, review.Dismissed) + assert.False(t, review.Dismissed) // test DeletePullReview req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, token), &api.CreatePullReviewOptions{ @@ -151,15 +151,15 @@ func TestAPIPullReview(t *testing.T) { assert.EqualValues(t, 11, reviews[0].ID) assert.EqualValues(t, "REQUEST_REVIEW", reviews[0].State) assert.EqualValues(t, 0, reviews[0].CodeCommentsCount) - assert.EqualValues(t, false, reviews[0].Stale) - assert.EqualValues(t, true, reviews[0].Official) + assert.False(t, reviews[0].Stale) + assert.True(t, reviews[0].Official) assert.EqualValues(t, "test_team", reviews[0].ReviewerTeam.Name) assert.EqualValues(t, 12, reviews[1].ID) assert.EqualValues(t, "REQUEST_REVIEW", reviews[1].State) assert.EqualValues(t, 0, reviews[0].CodeCommentsCount) - assert.EqualValues(t, false, reviews[1].Stale) - assert.EqualValues(t, true, reviews[1].Official) + assert.False(t, reviews[1].Stale) + assert.True(t, reviews[1].Official) assert.EqualValues(t, 1, reviews[1].Reviewer.ID) } diff --git a/integrations/api_releases_test.go b/integrations/api_releases_test.go index 26bf752ccae9e..027b282036f64 100644 --- a/integrations/api_releases_test.go +++ b/integrations/api_releases_test.go @@ -7,6 +7,7 @@ package integrations import ( "fmt" "net/http" + "net/url" "testing" "code.gitea.io/gitea/models" @@ -16,6 +17,58 @@ import ( "github.com/stretchr/testify/assert" ) +func TestAPIListReleases(t *testing.T) { + defer prepareTestEnv(t)() + + repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) + user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) + session := loginUser(t, user2.LowerName) + token := getTokenForLoggedInUser(t, session) + + link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/releases", user2.Name, repo.Name)) + link.RawQuery = url.Values{"token": {token}}.Encode() + resp := session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) + var apiReleases []*api.Release + DecodeJSON(t, resp, &apiReleases) + if assert.Len(t, apiReleases, 3) { + for _, release := range apiReleases { + switch release.ID { + case 1: + assert.False(t, release.IsDraft) + assert.False(t, release.IsPrerelease) + case 4: + assert.True(t, release.IsDraft) + assert.False(t, release.IsPrerelease) + case 5: + assert.False(t, release.IsDraft) + assert.True(t, release.IsPrerelease) + default: + assert.NoError(t, fmt.Errorf("unexpected release: %v", release)) + } + } + } + + // test filter + testFilterByLen := func(auth bool, query url.Values, expectedLength int, msgAndArgs ...string) { + link.RawQuery = query.Encode() + if auth { + query.Set("token", token) + resp = session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) + } else { + resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) + } + DecodeJSON(t, resp, &apiReleases) + assert.Len(t, apiReleases, expectedLength, msgAndArgs) + } + + testFilterByLen(false, url.Values{"draft": {"true"}}, 0, "anon should not see drafts") + testFilterByLen(true, url.Values{"draft": {"true"}}, 1, "repo owner should see drafts") + testFilterByLen(true, url.Values{"draft": {"false"}}, 2, "exclude drafts") + testFilterByLen(true, url.Values{"draft": {"false"}, "pre-release": {"false"}}, 1, "exclude drafts and pre-releases") + testFilterByLen(true, url.Values{"pre-release": {"true"}}, 1, "only get pre-release") + testFilterByLen(true, url.Values{"draft": {"true"}, "pre-release": {"true"}}, 0, "there is no pre-release draft") +} + func createNewReleaseUsingAPI(t *testing.T, session *TestSession, token string, owner *models.User, repo *models.Repository, name, target, title, desc string) *api.Release { urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/releases?token=%s", owner.Name, repo.Name, token) diff --git a/integrations/api_repo_lfs_locks_test.go b/integrations/api_repo_lfs_locks_test.go index ffc239567dc91..03549c11f4c20 100644 --- a/integrations/api_repo_lfs_locks_test.go +++ b/integrations/api_repo_lfs_locks_test.go @@ -11,6 +11,7 @@ import ( "time" "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" @@ -40,7 +41,7 @@ func TestAPILFSLocksNotLogin(t *testing.T) { repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) req := NewRequestf(t, "GET", "/%s/%s.git/info/lfs/locks", user.Name, repo.Name) - req.Header.Set("Accept", "application/vnd.git-lfs+json") + req.Header.Set("Accept", lfs.MediaType) resp := MakeRequest(t, req, http.StatusUnauthorized) var lfsLockError api.LFSLockError DecodeJSON(t, resp, &lfsLockError) @@ -102,8 +103,8 @@ func TestAPILFSLocksLogged(t *testing.T) { for _, test := range tests { session := loginUser(t, test.user.Name) req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/%s.git/info/lfs/locks", test.repo.FullName()), map[string]string{"path": test.path}) - req.Header.Set("Accept", "application/vnd.git-lfs+json") - req.Header.Set("Content-Type", "application/vnd.git-lfs+json") + req.Header.Set("Accept", lfs.MediaType) + req.Header.Set("Content-Type", lfs.MediaType) resp := session.MakeRequest(t, req, test.httpResult) if len(test.addTime) > 0 { var lfsLock api.LFSLockResponse @@ -119,7 +120,7 @@ func TestAPILFSLocksLogged(t *testing.T) { for _, test := range resultsTests { session := loginUser(t, test.user.Name) req := NewRequestf(t, "GET", "/%s.git/info/lfs/locks", test.repo.FullName()) - req.Header.Set("Accept", "application/vnd.git-lfs+json") + req.Header.Set("Accept", lfs.MediaType) resp := session.MakeRequest(t, req, http.StatusOK) var lfsLocks api.LFSLockList DecodeJSON(t, resp, &lfsLocks) @@ -131,8 +132,8 @@ func TestAPILFSLocksLogged(t *testing.T) { } req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/%s.git/info/lfs/locks/verify", test.repo.FullName()), map[string]string{}) - req.Header.Set("Accept", "application/vnd.git-lfs+json") - req.Header.Set("Content-Type", "application/vnd.git-lfs+json") + req.Header.Set("Accept", lfs.MediaType) + req.Header.Set("Content-Type", lfs.MediaType) resp = session.MakeRequest(t, req, http.StatusOK) var lfsLocksVerify api.LFSLockListVerify DecodeJSON(t, resp, &lfsLocksVerify) @@ -155,8 +156,8 @@ func TestAPILFSLocksLogged(t *testing.T) { for _, test := range deleteTests { session := loginUser(t, test.user.Name) req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/%s.git/info/lfs/locks/%s/unlock", test.repo.FullName(), test.lockID), map[string]string{}) - req.Header.Set("Accept", "application/vnd.git-lfs+json") - req.Header.Set("Content-Type", "application/vnd.git-lfs+json") + req.Header.Set("Accept", lfs.MediaType) + req.Header.Set("Content-Type", lfs.MediaType) resp := session.MakeRequest(t, req, http.StatusOK) var lfsLockRep api.LFSLockResponse DecodeJSON(t, resp, &lfsLockRep) @@ -168,7 +169,7 @@ func TestAPILFSLocksLogged(t *testing.T) { for _, test := range resultsTests { session := loginUser(t, test.user.Name) req := NewRequestf(t, "GET", "/%s.git/info/lfs/locks", test.repo.FullName()) - req.Header.Set("Accept", "application/vnd.git-lfs+json") + req.Header.Set("Accept", lfs.MediaType) resp := session.MakeRequest(t, req, http.StatusOK) var lfsLocks api.LFSLockList DecodeJSON(t, resp, &lfsLocks) diff --git a/integrations/api_repo_lfs_test.go b/integrations/api_repo_lfs_test.go new file mode 100644 index 0000000000000..d0328fd12110c --- /dev/null +++ b/integrations/api_repo_lfs_test.go @@ -0,0 +1,466 @@ +// 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 integrations + +import ( + "bytes" + "net/http" + "path" + "strconv" + "strings" + "testing" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/lfs" + "code.gitea.io/gitea/modules/setting" + + jsoniter "github.com/json-iterator/go" + "github.com/stretchr/testify/assert" +) + +func TestAPILFSNotStarted(t *testing.T) { + defer prepareTestEnv(t)() + + setting.LFS.StartServer = false + + user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) + repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) + + req := NewRequestf(t, "POST", "/%s/%s.git/info/lfs/objects/batch", user.Name, repo.Name) + MakeRequest(t, req, http.StatusNotFound) + req = NewRequestf(t, "PUT", "/%s/%s.git/info/lfs/objects/oid/10", user.Name, repo.Name) + MakeRequest(t, req, http.StatusNotFound) + req = NewRequestf(t, "GET", "/%s/%s.git/info/lfs/objects/oid/name", user.Name, repo.Name) + MakeRequest(t, req, http.StatusNotFound) + req = NewRequestf(t, "GET", "/%s/%s.git/info/lfs/objects/oid", user.Name, repo.Name) + MakeRequest(t, req, http.StatusNotFound) + req = NewRequestf(t, "POST", "/%s/%s.git/info/lfs/verify", user.Name, repo.Name) + MakeRequest(t, req, http.StatusNotFound) +} + +func TestAPILFSMediaType(t *testing.T) { + defer prepareTestEnv(t)() + + setting.LFS.StartServer = true + + user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) + repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) + + req := NewRequestf(t, "POST", "/%s/%s.git/info/lfs/objects/batch", user.Name, repo.Name) + MakeRequest(t, req, http.StatusUnsupportedMediaType) + req = NewRequestf(t, "POST", "/%s/%s.git/info/lfs/verify", user.Name, repo.Name) + MakeRequest(t, req, http.StatusUnsupportedMediaType) +} + +func createLFSTestRepository(t *testing.T, name string) *models.Repository { + ctx := NewAPITestContext(t, "user2", "lfs-"+name+"-repo") + t.Run("CreateRepo", doAPICreateRepository(ctx, false)) + + repo, err := models.GetRepositoryByOwnerAndName("user2", "lfs-"+name+"-repo") + assert.NoError(t, err) + + return repo +} + +func TestAPILFSBatch(t *testing.T) { + defer prepareTestEnv(t)() + + setting.LFS.StartServer = true + + repo := createLFSTestRepository(t, "batch") + + content := []byte("dummy1") + oid := storeObjectInRepo(t, repo.ID, &content) + defer repo.RemoveLFSMetaObjectByOid(oid) + + session := loginUser(t, "user2") + + newRequest := func(t testing.TB, br *lfs.BatchRequest) *http.Request { + req := NewRequestWithJSON(t, "POST", "/user2/lfs-batch-repo.git/info/lfs/objects/batch", br) + req.Header.Set("Accept", lfs.MediaType) + req.Header.Set("Content-Type", lfs.MediaType) + return req + } + decodeResponse := func(t *testing.T, b *bytes.Buffer) *lfs.BatchResponse { + var br lfs.BatchResponse + + json := jsoniter.ConfigCompatibleWithStandardLibrary + assert.NoError(t, json.Unmarshal(b.Bytes(), &br)) + return &br + } + + t.Run("InvalidJsonRequest", func(t *testing.T) { + defer PrintCurrentTest(t)() + + req := newRequest(t, nil) + + session.MakeRequest(t, req, http.StatusBadRequest) + }) + + t.Run("InvalidOperation", func(t *testing.T) { + defer PrintCurrentTest(t)() + + req := newRequest(t, &lfs.BatchRequest{ + Operation: "dummy", + }) + + session.MakeRequest(t, req, http.StatusBadRequest) + }) + + t.Run("InvalidPointer", func(t *testing.T) { + defer PrintCurrentTest(t)() + + req := newRequest(t, &lfs.BatchRequest{ + Operation: "download", + Objects: []lfs.Pointer{ + {Oid: "dummy"}, + {Oid: oid, Size: -1}, + }, + }) + + resp := session.MakeRequest(t, req, http.StatusOK) + br := decodeResponse(t, resp.Body) + assert.Len(t, br.Objects, 2) + assert.Equal(t, "dummy", br.Objects[0].Oid) + assert.Equal(t, oid, br.Objects[1].Oid) + assert.Equal(t, int64(0), br.Objects[0].Size) + assert.Equal(t, int64(-1), br.Objects[1].Size) + assert.NotNil(t, br.Objects[0].Error) + assert.NotNil(t, br.Objects[1].Error) + assert.Equal(t, http.StatusUnprocessableEntity, br.Objects[0].Error.Code) + assert.Equal(t, http.StatusUnprocessableEntity, br.Objects[1].Error.Code) + assert.Equal(t, "Oid or size are invalid", br.Objects[0].Error.Message) + assert.Equal(t, "Oid or size are invalid", br.Objects[1].Error.Message) + }) + + t.Run("PointerSizeMissmatch", func(t *testing.T) { + defer PrintCurrentTest(t)() + + req := newRequest(t, &lfs.BatchRequest{ + Operation: "download", + Objects: []lfs.Pointer{ + {Oid: oid, Size: 1}, + }, + }) + + resp := session.MakeRequest(t, req, http.StatusOK) + br := decodeResponse(t, resp.Body) + assert.Len(t, br.Objects, 1) + assert.NotNil(t, br.Objects[0].Error) + assert.Equal(t, http.StatusUnprocessableEntity, br.Objects[0].Error.Code) + assert.Equal(t, "Object "+oid+" is not 1 bytes", br.Objects[0].Error.Message) + }) + + t.Run("Download", func(t *testing.T) { + defer PrintCurrentTest(t)() + + t.Run("PointerNotInStore", func(t *testing.T) { + defer PrintCurrentTest(t)() + + req := newRequest(t, &lfs.BatchRequest{ + Operation: "download", + Objects: []lfs.Pointer{ + {Oid: "fb8f7d8435968c4f82a726a92395be4d16f2f63116caf36c8ad35c60831ab042", Size: 6}, + }, + }) + + resp := session.MakeRequest(t, req, http.StatusOK) + br := decodeResponse(t, resp.Body) + assert.Len(t, br.Objects, 1) + assert.NotNil(t, br.Objects[0].Error) + assert.Equal(t, http.StatusNotFound, br.Objects[0].Error.Code) + }) + + t.Run("MetaNotFound", func(t *testing.T) { + defer PrintCurrentTest(t)() + + p := lfs.Pointer{Oid: "05eeb4eb5be71f2dd291ca39157d6d9effd7d1ea19cbdc8a99411fe2a8f26a00", Size: 6} + + contentStore := lfs.NewContentStore() + exist, err := contentStore.Exists(p) + assert.NoError(t, err) + assert.False(t, exist) + err = contentStore.Put(p, bytes.NewReader([]byte("dummy0"))) + assert.NoError(t, err) + + req := newRequest(t, &lfs.BatchRequest{ + Operation: "download", + Objects: []lfs.Pointer{p}, + }) + + resp := session.MakeRequest(t, req, http.StatusOK) + br := decodeResponse(t, resp.Body) + assert.Len(t, br.Objects, 1) + assert.NotNil(t, br.Objects[0].Error) + assert.Equal(t, http.StatusNotFound, br.Objects[0].Error.Code) + }) + + t.Run("Success", func(t *testing.T) { + defer PrintCurrentTest(t)() + + req := newRequest(t, &lfs.BatchRequest{ + Operation: "download", + Objects: []lfs.Pointer{ + {Oid: oid, Size: 6}, + }, + }) + + resp := session.MakeRequest(t, req, http.StatusOK) + br := decodeResponse(t, resp.Body) + assert.Len(t, br.Objects, 1) + assert.Nil(t, br.Objects[0].Error) + assert.Contains(t, br.Objects[0].Actions, "download") + l := br.Objects[0].Actions["download"] + assert.NotNil(t, l) + assert.NotEmpty(t, l.Href) + }) + }) + + t.Run("Upload", func(t *testing.T) { + defer PrintCurrentTest(t)() + + t.Run("FileTooBig", func(t *testing.T) { + defer PrintCurrentTest(t)() + + oldMaxFileSize := setting.LFS.MaxFileSize + setting.LFS.MaxFileSize = 2 + + req := newRequest(t, &lfs.BatchRequest{ + Operation: "upload", + Objects: []lfs.Pointer{ + {Oid: "fb8f7d8435968c4f82a726a92395be4d16f2f63116caf36c8ad35c60831ab042", Size: 6}, + }, + }) + + resp := session.MakeRequest(t, req, http.StatusOK) + br := decodeResponse(t, resp.Body) + assert.Len(t, br.Objects, 1) + assert.NotNil(t, br.Objects[0].Error) + assert.Equal(t, http.StatusUnprocessableEntity, br.Objects[0].Error.Code) + assert.Equal(t, "Size must be less than or equal to 2", br.Objects[0].Error.Message) + + setting.LFS.MaxFileSize = oldMaxFileSize + }) + + t.Run("AddMeta", func(t *testing.T) { + defer PrintCurrentTest(t)() + + p := lfs.Pointer{Oid: "05eeb4eb5be71f2dd291ca39157d6d9effd7d1ea19cbdc8a99411fe2a8f26a00", Size: 6} + + contentStore := lfs.NewContentStore() + exist, err := contentStore.Exists(p) + assert.NoError(t, err) + assert.True(t, exist) + + meta, err := repo.GetLFSMetaObjectByOid(p.Oid) + assert.Nil(t, meta) + assert.Equal(t, models.ErrLFSObjectNotExist, err) + + req := newRequest(t, &lfs.BatchRequest{ + Operation: "upload", + Objects: []lfs.Pointer{p}, + }) + + resp := session.MakeRequest(t, req, http.StatusOK) + br := decodeResponse(t, resp.Body) + assert.Len(t, br.Objects, 1) + assert.Nil(t, br.Objects[0].Error) + assert.Empty(t, br.Objects[0].Actions) + + meta, err = repo.GetLFSMetaObjectByOid(p.Oid) + assert.NoError(t, err) + assert.NotNil(t, meta) + }) + + t.Run("AlreadyExists", func(t *testing.T) { + defer PrintCurrentTest(t)() + + req := newRequest(t, &lfs.BatchRequest{ + Operation: "upload", + Objects: []lfs.Pointer{ + {Oid: oid, Size: 6}, + }, + }) + + resp := session.MakeRequest(t, req, http.StatusOK) + br := decodeResponse(t, resp.Body) + assert.Len(t, br.Objects, 1) + assert.Nil(t, br.Objects[0].Error) + assert.Empty(t, br.Objects[0].Actions) + }) + + t.Run("NewFile", func(t *testing.T) { + defer PrintCurrentTest(t)() + + req := newRequest(t, &lfs.BatchRequest{ + Operation: "upload", + Objects: []lfs.Pointer{ + {Oid: "d6f175817f886ec6fbbc1515326465fa96c3bfd54a4ea06cfd6dbbd8340e0153", Size: 1}, + }, + }) + + resp := session.MakeRequest(t, req, http.StatusOK) + br := decodeResponse(t, resp.Body) + assert.Len(t, br.Objects, 1) + assert.Nil(t, br.Objects[0].Error) + assert.Contains(t, br.Objects[0].Actions, "upload") + ul := br.Objects[0].Actions["upload"] + assert.NotNil(t, ul) + assert.NotEmpty(t, ul.Href) + assert.Contains(t, br.Objects[0].Actions, "verify") + vl := br.Objects[0].Actions["verify"] + assert.NotNil(t, vl) + assert.NotEmpty(t, vl.Href) + }) + }) +} + +func TestAPILFSUpload(t *testing.T) { + defer prepareTestEnv(t)() + + setting.LFS.StartServer = true + + repo := createLFSTestRepository(t, "upload") + + content := []byte("dummy3") + oid := storeObjectInRepo(t, repo.ID, &content) + defer repo.RemoveLFSMetaObjectByOid(oid) + + session := loginUser(t, "user2") + + newRequest := func(t testing.TB, p lfs.Pointer, content string) *http.Request { + req := NewRequestWithBody(t, "PUT", path.Join("/user2/lfs-upload-repo.git/info/lfs/objects/", p.Oid, strconv.FormatInt(p.Size, 10)), strings.NewReader(content)) + return req + } + + t.Run("InvalidPointer", func(t *testing.T) { + defer PrintCurrentTest(t)() + + req := newRequest(t, lfs.Pointer{Oid: "dummy"}, "") + + session.MakeRequest(t, req, http.StatusUnprocessableEntity) + }) + + t.Run("AlreadyExistsInStore", func(t *testing.T) { + defer PrintCurrentTest(t)() + + p := lfs.Pointer{Oid: "83de2e488b89a0aa1c97496b888120a28b0c1e15463a4adb8405578c540f36d4", Size: 6} + + contentStore := lfs.NewContentStore() + exist, err := contentStore.Exists(p) + assert.NoError(t, err) + assert.False(t, exist) + err = contentStore.Put(p, bytes.NewReader([]byte("dummy5"))) + assert.NoError(t, err) + + meta, err := repo.GetLFSMetaObjectByOid(p.Oid) + assert.Nil(t, meta) + assert.Equal(t, models.ErrLFSObjectNotExist, err) + + req := newRequest(t, p, "") + + session.MakeRequest(t, req, http.StatusOK) + + meta, err = repo.GetLFSMetaObjectByOid(p.Oid) + assert.NoError(t, err) + assert.NotNil(t, meta) + }) + + t.Run("MetaAlreadyExists", func(t *testing.T) { + defer PrintCurrentTest(t)() + + req := newRequest(t, lfs.Pointer{Oid: oid, Size: 6}, "") + + session.MakeRequest(t, req, http.StatusOK) + }) + + t.Run("HashMissmatch", func(t *testing.T) { + defer PrintCurrentTest(t)() + + req := newRequest(t, lfs.Pointer{Oid: "2581dd7bbc1fe44726de4b7dd806a087a978b9c5aec0a60481259e34be09b06a", Size: 1}, "a") + + session.MakeRequest(t, req, http.StatusUnprocessableEntity) + }) + + t.Run("SizeMissmatch", func(t *testing.T) { + defer PrintCurrentTest(t)() + + req := newRequest(t, lfs.Pointer{Oid: "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb", Size: 2}, "a") + + session.MakeRequest(t, req, http.StatusUnprocessableEntity) + }) + + t.Run("Success", func(t *testing.T) { + defer PrintCurrentTest(t)() + + p := lfs.Pointer{Oid: "6ccce4863b70f258d691f59609d31b4502e1ba5199942d3bc5d35d17a4ce771d", Size: 5} + + req := newRequest(t, p, "gitea") + + session.MakeRequest(t, req, http.StatusOK) + + contentStore := lfs.NewContentStore() + exist, err := contentStore.Exists(p) + assert.NoError(t, err) + assert.True(t, exist) + + meta, err := repo.GetLFSMetaObjectByOid(p.Oid) + assert.NoError(t, err) + assert.NotNil(t, meta) + }) +} + +func TestAPILFSVerify(t *testing.T) { + defer prepareTestEnv(t)() + + setting.LFS.StartServer = true + + repo := createLFSTestRepository(t, "verify") + + content := []byte("dummy3") + oid := storeObjectInRepo(t, repo.ID, &content) + defer repo.RemoveLFSMetaObjectByOid(oid) + + session := loginUser(t, "user2") + + newRequest := func(t testing.TB, p *lfs.Pointer) *http.Request { + req := NewRequestWithJSON(t, "POST", "/user2/lfs-verify-repo.git/info/lfs/verify", p) + req.Header.Set("Accept", lfs.MediaType) + req.Header.Set("Content-Type", lfs.MediaType) + return req + } + + t.Run("InvalidJsonRequest", func(t *testing.T) { + defer PrintCurrentTest(t)() + + req := newRequest(t, nil) + + session.MakeRequest(t, req, http.StatusUnprocessableEntity) + }) + + t.Run("InvalidPointer", func(t *testing.T) { + defer PrintCurrentTest(t)() + + req := newRequest(t, &lfs.Pointer{}) + + session.MakeRequest(t, req, http.StatusUnprocessableEntity) + }) + + t.Run("PointerNotExisting", func(t *testing.T) { + defer PrintCurrentTest(t)() + + req := newRequest(t, &lfs.Pointer{Oid: "fb8f7d8435968c4f82a726a92395be4d16f2f63116caf36c8ad35c60831ab042", Size: 6}) + + session.MakeRequest(t, req, http.StatusNotFound) + }) + + t.Run("Success", func(t *testing.T) { + defer PrintCurrentTest(t)() + + req := newRequest(t, &lfs.Pointer{Oid: oid, Size: 6}) + + session.MakeRequest(t, req, http.StatusOK) + }) +} diff --git a/integrations/api_repo_tags_test.go b/integrations/api_repo_tags_test.go index 9988a4830017b..1bd9fa616819e 100644 --- a/integrations/api_repo_tags_test.go +++ b/integrations/api_repo_tags_test.go @@ -5,6 +5,7 @@ package integrations import ( + "fmt" "net/http" "testing" @@ -15,23 +16,53 @@ import ( "github.com/stretchr/testify/assert" ) -func TestAPIReposGetTags(t *testing.T) { +func TestAPIRepoTags(t *testing.T) { defer prepareTestEnv(t)() user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // Login as User2. session := loginUser(t, user.Name) token := getTokenForLoggedInUser(t, session) - req := NewRequestf(t, "GET", "/api/v1/repos/%s/repo1/tags?token="+token, user.Name) + repoName := "repo1" + + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/tags?token=%s", user.Name, repoName, token) resp := session.MakeRequest(t, req, http.StatusOK) var tags []*api.Tag DecodeJSON(t, resp, &tags) - assert.EqualValues(t, 1, len(tags)) + assert.Len(t, tags, 1) assert.Equal(t, "v1.1", tags[0].Name) + assert.Equal(t, "Initial commit", tags[0].Message) assert.Equal(t, "65f1bf27bc3bf70f64657658635e66094edbcb4d", tags[0].Commit.SHA) assert.Equal(t, setting.AppURL+"api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d", tags[0].Commit.URL) assert.Equal(t, setting.AppURL+"user2/repo1/archive/v1.1.zip", tags[0].ZipballURL) assert.Equal(t, setting.AppURL+"user2/repo1/archive/v1.1.tar.gz", tags[0].TarballURL) + + newTag := createNewTagUsingAPI(t, session, token, user.Name, repoName, "awesome-tag", "", "nice!\nand some text") + resp = session.MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &tags) + assert.Len(t, tags, 2) + for _, tag := range tags { + if tag.Name != "v1.1" { + assert.EqualValues(t, newTag.Name, tag.Name) + assert.EqualValues(t, newTag.Message, tag.Message) + assert.EqualValues(t, "nice!\nand some text", tag.Message) + assert.EqualValues(t, newTag.Commit.SHA, tag.Commit.SHA) + } + } +} + +func createNewTagUsingAPI(t *testing.T, session *TestSession, token string, ownerName, repoName, name, target, msg string) *api.Tag { + urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/tags?token=%s", ownerName, repoName, token) + req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateTagOption{ + TagName: name, + Message: msg, + Target: target, + }) + resp := session.MakeRequest(t, req, http.StatusCreated) + + var respObj api.Tag + DecodeJSON(t, resp, &respObj) + return &respObj } diff --git a/integrations/api_repo_teams_test.go b/integrations/api_repo_teams_test.go index a07b580346850..4a155130b9354 100644 --- a/integrations/api_repo_teams_test.go +++ b/integrations/api_repo_teams_test.go @@ -33,12 +33,12 @@ func TestAPIRepoTeams(t *testing.T) { DecodeJSON(t, res, &teams) if assert.Len(t, teams, 2) { assert.EqualValues(t, "Owners", teams[0].Name) - assert.EqualValues(t, false, teams[0].CanCreateOrgRepo) + assert.False(t, teams[0].CanCreateOrgRepo) assert.EqualValues(t, []string{"repo.code", "repo.issues", "repo.pulls", "repo.releases", "repo.wiki", "repo.ext_wiki", "repo.ext_issues"}, teams[0].Units) assert.EqualValues(t, "owner", teams[0].Permission) assert.EqualValues(t, "test_team", teams[1].Name) - assert.EqualValues(t, false, teams[1].CanCreateOrgRepo) + assert.False(t, teams[1].CanCreateOrgRepo) assert.EqualValues(t, []string{"repo.issues"}, teams[1].Units) assert.EqualValues(t, "write", teams[1].Permission) } diff --git a/integrations/api_repo_test.go b/integrations/api_repo_test.go index cfd3b58d649d6..7052e74b018ea 100644 --- a/integrations/api_repo_test.go +++ b/integrations/api_repo_test.go @@ -466,7 +466,7 @@ func TestAPIRepoTransfer(t *testing.T) { session := loginUser(t, user.Name) token := getTokenForLoggedInUser(t, session) repoName := "moveME" - repo := new(models.Repository) + apiRepo := new(api.Repository) req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/user/repos?token=%s", token), &api.CreateRepoOption{ Name: repoName, Description: "repo move around", @@ -475,12 +475,12 @@ func TestAPIRepoTransfer(t *testing.T) { AutoInit: true, }) resp := session.MakeRequest(t, req, http.StatusCreated) - DecodeJSON(t, resp, repo) + DecodeJSON(t, resp, apiRepo) //start testing for _, testCase := range testCases { user = models.AssertExistsAndLoadBean(t, &models.User{ID: testCase.ctxUserID}).(*models.User) - repo = models.AssertExistsAndLoadBean(t, &models.Repository{ID: repo.ID}).(*models.Repository) + repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: apiRepo.ID}).(*models.Repository) session = loginUser(t, user.Name) token = getTokenForLoggedInUser(t, session) req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer?token=%s", repo.OwnerName, repo.Name, token), &api.TransferRepoOption{ @@ -491,6 +491,34 @@ func TestAPIRepoTransfer(t *testing.T) { } //cleanup - repo = models.AssertExistsAndLoadBean(t, &models.Repository{ID: repo.ID}).(*models.Repository) + repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: apiRepo.ID}).(*models.Repository) _ = models.DeleteRepository(user, repo.OwnerID, repo.ID) } + +func TestAPIRepoGetReviewers(t *testing.T) { + defer prepareTestEnv(t)() + user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) + session := loginUser(t, user.Name) + token := getTokenForLoggedInUser(t, session) + repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) + + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/reviewers?token=%s", user.Name, repo.Name, token) + resp := session.MakeRequest(t, req, http.StatusOK) + var reviewers []*api.User + DecodeJSON(t, resp, &reviewers) + assert.Len(t, reviewers, 4) +} + +func TestAPIRepoGetAssignees(t *testing.T) { + defer prepareTestEnv(t)() + user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) + session := loginUser(t, user.Name) + token := getTokenForLoggedInUser(t, session) + repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) + + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/assignees?token=%s", user.Name, repo.Name, token) + resp := session.MakeRequest(t, req, http.StatusOK) + var assignees []*api.User + DecodeJSON(t, resp, &assignees) + assert.Len(t, assignees, 1) +} diff --git a/integrations/api_repo_topic_test.go b/integrations/api_repo_topic_test.go index b96489ae2211d..5e42bc64bf921 100644 --- a/integrations/api_repo_topic_test.go +++ b/integrations/api_repo_topic_test.go @@ -88,7 +88,7 @@ func TestAPIRepoTopic(t *testing.T) { req = NewRequest(t, "GET", url) res = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, res, &topics) - assert.Equal(t, 25, len(topics.TopicNames)) + assert.Len(t, topics.TopicNames, 25) // Test writing more topics than allowed newTopics = append(newTopics, "t26") @@ -115,7 +115,7 @@ func TestAPIRepoTopic(t *testing.T) { req = NewRequest(t, "GET", url) res = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, res, &topics) - assert.Equal(t, 0, len(topics.TopicNames)) + assert.Empty(t, topics.TopicNames) // Test add a topic to repo with write access (requires repo admin access) req = NewRequestf(t, "PUT", "/api/v1/repos/%s/%s/topics/%s?token=%s", user3.Name, repo3.Name, "topicName", token4) diff --git a/integrations/api_team_test.go b/integrations/api_team_test.go index d89385447092f..8b202862a159c 100644 --- a/integrations/api_team_test.go +++ b/integrations/api_team_test.go @@ -148,7 +148,7 @@ func TestAPITeamSearch(t *testing.T) { resp := session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &results) assert.NotEmpty(t, results.Data) - assert.Equal(t, 1, len(results.Data)) + assert.Len(t, results.Data, 1) assert.Equal(t, "test_team", results.Data[0].Name) // no access if not organization member diff --git a/integrations/api_user_email_test.go b/integrations/api_user_email_test.go index 8d0a0cdf1b274..9d2b7485d8526 100644 --- a/integrations/api_user_email_test.go +++ b/integrations/api_user_email_test.go @@ -33,7 +33,7 @@ func TestAPIListEmails(t *testing.T) { Primary: true, }, { - Email: "user21@example.com", + Email: "user2-2@example.com", Verified: false, Primary: false, }, @@ -55,7 +55,7 @@ func TestAPIAddEmail(t *testing.T) { session.MakeRequest(t, req, http.StatusUnprocessableEntity) opts = api.CreateEmailOption{ - Emails: []string{"user22@example.com"}, + Emails: []string{"user2-3@example.com"}, } req = NewRequestWithJSON(t, "POST", "/api/v1/user/emails?token="+token, &opts) resp := session.MakeRequest(t, req, http.StatusCreated) @@ -64,7 +64,7 @@ func TestAPIAddEmail(t *testing.T) { DecodeJSON(t, resp, &emails) assert.EqualValues(t, []*api.Email{ { - Email: "user22@example.com", + Email: "user2-3@example.com", Verified: true, Primary: false, }, @@ -79,13 +79,13 @@ func TestAPIDeleteEmail(t *testing.T) { token := getTokenForLoggedInUser(t, session) opts := api.DeleteEmailOption{ - Emails: []string{"user22@example.com"}, + Emails: []string{"user2-3@example.com"}, } req := NewRequestWithJSON(t, "DELETE", "/api/v1/user/emails?token="+token, &opts) session.MakeRequest(t, req, http.StatusNotFound) opts = api.DeleteEmailOption{ - Emails: []string{"user21@example.com"}, + Emails: []string{"user2-2@example.com"}, } req = NewRequestWithJSON(t, "DELETE", "/api/v1/user/emails?token="+token, &opts) session.MakeRequest(t, req, http.StatusNoContent) diff --git a/integrations/create_no_session_test.go b/integrations/create_no_session_test.go index c864b9c7ae125..46f111b6f7d84 100644 --- a/integrations/create_no_session_test.go +++ b/integrations/create_no_session_test.go @@ -14,7 +14,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/routers/routes" + "code.gitea.io/gitea/routers" "gitea.com/go-chi/session" jsoniter "github.com/json-iterator/go" @@ -58,7 +58,7 @@ func TestSessionFileCreation(t *testing.T) { oldSessionConfig := setting.SessionConfig.ProviderConfig defer func() { setting.SessionConfig.ProviderConfig = oldSessionConfig - c = routes.NormalRoutes() + c = routers.NormalRoutes() }() var config session.Options @@ -84,7 +84,7 @@ func TestSessionFileCreation(t *testing.T) { setting.SessionConfig.ProviderConfig = string(newConfigBytes) - c = routes.NormalRoutes() + c = routers.NormalRoutes() t.Run("NoSessionOnViewIssue", func(t *testing.T) { defer PrintCurrentTest(t)() diff --git a/integrations/git_smart_http_test.go b/integrations/git_smart_http_test.go new file mode 100644 index 0000000000000..9a4e3689c1ce9 --- /dev/null +++ b/integrations/git_smart_http_test.go @@ -0,0 +1,69 @@ +// 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 integrations + +import ( + "io/ioutil" + "net/http" + "net/url" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGitSmartHTTP(t *testing.T) { + onGiteaRun(t, testGitSmartHTTP) +} + +func testGitSmartHTTP(t *testing.T, u *url.URL) { + var kases = []struct { + p string + code int + }{ + { + p: "user2/repo1/info/refs", + code: 200, + }, + { + p: "user2/repo1/HEAD", + code: 200, + }, + { + p: "user2/repo1/objects/info/alternates", + code: 404, + }, + { + p: "user2/repo1/objects/info/http-alternates", + code: 404, + }, + { + p: "user2/repo1/../../custom/conf/app.ini", + code: 404, + }, + { + p: "user2/repo1/objects/info/../../../../custom/conf/app.ini", + code: 404, + }, + { + p: `user2/repo1/objects/info/..\..\..\..\custom\conf\app.ini`, + code: 400, + }, + } + + for _, kase := range kases { + t.Run(kase.p, func(t *testing.T) { + p := u.String() + kase.p + req, err := http.NewRequest("GET", p, nil) + assert.NoError(t, err) + req.SetBasicAuth("user2", userPassword) + resp, err := http.DefaultClient.Do(req) + assert.NoError(t, err) + defer resp.Body.Close() + assert.EqualValues(t, kase.code, resp.StatusCode) + _, err = ioutil.ReadAll(resp.Body) + assert.NoError(t, err) + }) + } +} diff --git a/integrations/integration_test.go b/integrations/integration_test.go index 74227416c4be5..d755977d1ab16 100644 --- a/integrations/integration_test.go +++ b/integrations/integration_test.go @@ -34,7 +34,6 @@ import ( "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" jsoniter "github.com/json-iterator/go" @@ -88,7 +87,7 @@ func TestMain(m *testing.M) { defer cancel() initIntegrationTest() - c = routes.NormalRoutes() + c = routers.NormalRoutes() // integration test settings... if setting.Cfg != nil { diff --git a/integrations/lfs_getobject_test.go b/integrations/lfs_getobject_test.go index 789c7572a77e5..337a93567a49f 100644 --- a/integrations/lfs_getobject_test.go +++ b/integrations/lfs_getobject_test.go @@ -15,27 +15,18 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/routers/routes" + "code.gitea.io/gitea/routers/web" + jsoniter "github.com/json-iterator/go" gzipp "github.com/klauspost/compress/gzip" "github.com/stretchr/testify/assert" ) -var lfsID = int64(20000) - func storeObjectInRepo(t *testing.T, repositoryID int64, content *[]byte) string { pointer, err := lfs.GeneratePointer(bytes.NewReader(*content)) assert.NoError(t, err) - var lfsMetaObject *models.LFSMetaObject - if setting.Database.UsePostgreSQL { - lfsMetaObject = &models.LFSMetaObject{ID: lfsID, Pointer: pointer, RepositoryID: repositoryID} - } else { - lfsMetaObject = &models.LFSMetaObject{Pointer: pointer, RepositoryID: repositoryID} - } - - lfsID++ - lfsMetaObject, err = models.NewLFSMetaObject(lfsMetaObject) + _, err = models.NewLFSMetaObject(&models.LFSMetaObject{Pointer: pointer, RepositoryID: repositoryID}) assert.NoError(t, err) contentStore := lfs.NewContentStore() exist, err := contentStore.Exists(pointer) @@ -108,7 +99,7 @@ func TestGetLFSLarge(t *testing.T) { t.Skip() return } - content := make([]byte, routes.GzipMinSize*10) + content := make([]byte, web.GzipMinSize*10) for i := range content { content[i] = byte(i % 256) } @@ -124,7 +115,7 @@ func TestGetLFSGzip(t *testing.T) { t.Skip() return } - b := make([]byte, routes.GzipMinSize*10) + b := make([]byte, web.GzipMinSize*10) for i := range b { b[i] = byte(i % 256) } @@ -145,7 +136,7 @@ func TestGetLFSZip(t *testing.T) { t.Skip() return } - b := make([]byte, routes.GzipMinSize*10) + b := make([]byte, web.GzipMinSize*10) for i := range b { b[i] = byte(i % 256) } @@ -210,7 +201,14 @@ func TestGetLFSRange(t *testing.T) { "Range": []string{tt.in}, } resp := storeAndGetLfs(t, &content, &h, tt.status) - assert.Equal(t, tt.out, resp.Body.String()) + if tt.status == http.StatusPartialContent || tt.status == http.StatusOK { + assert.Equal(t, tt.out, resp.Body.String()) + } else { + var er lfs.ErrorResponse + err := jsoniter.Unmarshal(resp.Body.Bytes(), &er) + assert.NoError(t, err) + assert.Equal(t, tt.out, er.Message) + } }) } } diff --git a/services/mirror/mirror_test.go b/integrations/mirror_pull_test.go similarity index 89% rename from services/mirror/mirror_test.go rename to integrations/mirror_pull_test.go index 20492c784bdb8..0e4da74fcf42a 100644 --- a/services/mirror/mirror_test.go +++ b/integrations/mirror_pull_test.go @@ -2,28 +2,24 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package mirror +package integrations import ( "context" - "path/filepath" "testing" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/git" migration "code.gitea.io/gitea/modules/migrations/base" "code.gitea.io/gitea/modules/repository" + mirror_service "code.gitea.io/gitea/services/mirror" release_service "code.gitea.io/gitea/services/release" "github.com/stretchr/testify/assert" ) -func TestMain(m *testing.M) { - models.MainTest(m, filepath.Join("..", "..")) -} - -func TestRelease_MirrorDelete(t *testing.T) { - assert.NoError(t, models.PrepareTestDatabase()) +func TestMirrorPull(t *testing.T) { + defer prepareTestEnv(t)() user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) @@ -76,7 +72,7 @@ func TestRelease_MirrorDelete(t *testing.T) { err = mirror.GetMirror() assert.NoError(t, err) - _, ok := runSync(ctx, mirror.Mirror) + ok := mirror_service.SyncPullMirror(ctx, mirror.ID) assert.True(t, ok) count, err := models.GetReleaseCountByRepoID(mirror.ID, findOptions) @@ -87,7 +83,7 @@ func TestRelease_MirrorDelete(t *testing.T) { assert.NoError(t, err) assert.NoError(t, release_service.DeleteReleaseByID(release.ID, user, true)) - _, ok = runSync(ctx, mirror.Mirror) + ok = mirror_service.SyncPullMirror(ctx, mirror.ID) assert.True(t, ok) count, err = models.GetReleaseCountByRepoID(mirror.ID, findOptions) diff --git a/integrations/mirror_push_test.go b/integrations/mirror_push_test.go new file mode 100644 index 0000000000000..3191ef770444b --- /dev/null +++ b/integrations/mirror_push_test.go @@ -0,0 +1,86 @@ +// 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 integrations + +import ( + "context" + "fmt" + "net/http" + "net/url" + "testing" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/repository" + "code.gitea.io/gitea/modules/setting" + mirror_service "code.gitea.io/gitea/services/mirror" + + "github.com/stretchr/testify/assert" +) + +func TestMirrorPush(t *testing.T) { + onGiteaRun(t, testMirrorPush) +} + +func testMirrorPush(t *testing.T, u *url.URL) { + defer prepareTestEnv(t)() + + setting.Migrations.AllowLocalNetworks = true + + user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) + srcRepo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) + + mirrorRepo, err := repository.CreateRepository(user, user, models.CreateRepoOptions{ + Name: "test-push-mirror", + }) + assert.NoError(t, err) + + ctx := NewAPITestContext(t, user.LowerName, srcRepo.Name) + + doCreatePushMirror(ctx, fmt.Sprintf("%s%s/%s", u.String(), url.PathEscape(ctx.Username), url.PathEscape(mirrorRepo.Name)), user.LowerName, userPassword)(t) + + mirrors, err := models.GetPushMirrorsByRepoID(srcRepo.ID) + assert.NoError(t, err) + assert.Len(t, mirrors, 1) + + ok := mirror_service.SyncPushMirror(context.Background(), mirrors[0].ID) + assert.True(t, ok) + + srcGitRepo, err := git.OpenRepository(srcRepo.RepoPath()) + assert.NoError(t, err) + defer srcGitRepo.Close() + + srcCommit, err := srcGitRepo.GetBranchCommit("master") + assert.NoError(t, err) + + mirrorGitRepo, err := git.OpenRepository(mirrorRepo.RepoPath()) + assert.NoError(t, err) + defer mirrorGitRepo.Close() + + mirrorCommit, err := mirrorGitRepo.GetBranchCommit("master") + assert.NoError(t, err) + + assert.Equal(t, srcCommit.ID, mirrorCommit.ID) +} + +func doCreatePushMirror(ctx APITestContext, address, username, password string) func(t *testing.T) { + return func(t *testing.T) { + csrf := GetCSRF(t, ctx.Session, fmt.Sprintf("/%s/%s/settings", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame))) + + req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/settings", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame)), map[string]string{ + "_csrf": csrf, + "action": "push-mirror-add", + "push_mirror_address": address, + "push_mirror_username": username, + "push_mirror_password": password, + "push_mirror_interval": "0", + }) + ctx.Session.MakeRequest(t, req, http.StatusFound) + + flashCookie := ctx.Session.GetCookie("macaron_flash") + assert.NotNil(t, flashCookie) + assert.Contains(t, flashCookie.Value, "success") + } +} diff --git a/integrations/org_count_test.go b/integrations/org_count_test.go index 755ee3cee59f0..20917dc17e0c5 100644 --- a/integrations/org_count_test.go +++ b/integrations/org_count_test.go @@ -114,11 +114,12 @@ func doCheckOrgCounts(username string, orgCounts map[string]int, strict bool, ca Name: username, }).(*models.User) - user.GetOrganizations(&models.SearchOrganizationsOptions{All: true}) + orgs, err := models.GetOrgsByUserID(user.ID, true) + assert.NoError(t, err) calcOrgCounts := map[string]int{} - for _, org := range user.Orgs { + for _, org := range orgs { calcOrgCounts[org.LowerName] = org.NumRepos count, ok := canonicalCounts[org.LowerName] if ok { diff --git a/integrations/org_test.go b/integrations/org_test.go index b0212f4af7a73..ee61aae6f5ac0 100644 --- a/integrations/org_test.go +++ b/integrations/org_test.go @@ -33,7 +33,7 @@ func TestOrgRepos(t *testing.T) { htmlDoc := NewHTMLParser(t, resp.Body) sel := htmlDoc.doc.Find("a.name") - assert.EqualValues(t, len(repos), len(sel.Nodes)) + assert.Len(t, repos, len(sel.Nodes)) for i := 0; i < len(repos); i++ { assert.EqualValues(t, repos[i], strings.TrimSpace(sel.Eq(i).Text())) } diff --git a/integrations/release_test.go b/integrations/release_test.go index 365bc04d8738f..ac5df315d5972 100644 --- a/integrations/release_test.go +++ b/integrations/release_test.go @@ -85,7 +85,7 @@ func TestCreateRelease(t *testing.T) { session := loginUser(t, "user2") createNewRelease(t, session, "/user2/repo1", "v0.0.1", "v0.0.1", false, false) - checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.stable"), 3) + checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.stable"), 4) } func TestCreateReleasePreRelease(t *testing.T) { @@ -94,7 +94,7 @@ func TestCreateReleasePreRelease(t *testing.T) { session := loginUser(t, "user2") createNewRelease(t, session, "/user2/repo1", "v0.0.1", "v0.0.1", true, false) - checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.prerelease"), 3) + checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.prerelease"), 4) } func TestCreateReleaseDraft(t *testing.T) { @@ -103,7 +103,7 @@ func TestCreateReleaseDraft(t *testing.T) { session := loginUser(t, "user2") createNewRelease(t, session, "/user2/repo1", "v0.0.1", "v0.0.1", false, true) - checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.draft"), 3) + checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.draft"), 4) } func TestCreateReleasePaging(t *testing.T) { @@ -142,7 +142,7 @@ func TestViewReleaseListNoLogin(t *testing.T) { htmlDoc := NewHTMLParser(t, rsp.Body) releases := htmlDoc.Find("#release-list li.ui.grid") - assert.Equal(t, 1, releases.Length()) + assert.Equal(t, 2, releases.Length()) links := make([]string, 0, 5) releases.Each(func(i int, s *goquery.Selection) { @@ -153,7 +153,7 @@ func TestViewReleaseListNoLogin(t *testing.T) { links = append(links, link) }) - assert.EqualValues(t, []string{"/user2/repo1/releases/tag/v1.1"}, links) + assert.EqualValues(t, []string{"/user2/repo1/releases/tag/v1.0", "/user2/repo1/releases/tag/v1.1"}, links) } func TestViewReleaseListLogin(t *testing.T) { @@ -169,7 +169,7 @@ func TestViewReleaseListLogin(t *testing.T) { htmlDoc := NewHTMLParser(t, rsp.Body) releases := htmlDoc.Find("#release-list li.ui.grid") - assert.Equal(t, 2, releases.Length()) + assert.Equal(t, 3, releases.Length()) links := make([]string, 0, 5) releases.Each(func(i int, s *goquery.Selection) { @@ -180,8 +180,11 @@ func TestViewReleaseListLogin(t *testing.T) { links = append(links, link) }) - assert.EqualValues(t, []string{"/user2/repo1/releases/tag/draft-release", - "/user2/repo1/releases/tag/v1.1"}, links) + assert.EqualValues(t, []string{ + "/user2/repo1/releases/tag/draft-release", + "/user2/repo1/releases/tag/v1.0", + "/user2/repo1/releases/tag/v1.1", + }, links) } func TestViewTagsList(t *testing.T) { @@ -197,12 +200,12 @@ func TestViewTagsList(t *testing.T) { htmlDoc := NewHTMLParser(t, rsp.Body) tags := htmlDoc.Find(".tag-list tr") - assert.Equal(t, 2, tags.Length()) + assert.Equal(t, 3, tags.Length()) tagNames := make([]string, 0, 5) tags.Each(func(i int, s *goquery.Selection) { tagNames = append(tagNames, s.Find(".tag a.df.ac").Text()) }) - assert.EqualValues(t, []string{"delete-tag", "v1.1"}, tagNames) + assert.EqualValues(t, []string{"v1.0", "delete-tag", "v1.1"}, tagNames) } diff --git a/integrations/repo_commits_test.go b/integrations/repo_commits_test.go index 042849db7cf5b..306175d812f27 100644 --- a/integrations/repo_commits_test.go +++ b/integrations/repo_commits_test.go @@ -66,7 +66,7 @@ func doTestRepoCommitWithStatus(t *testing.T, state string, classes ...string) { doc = NewHTMLParser(t, resp.Body) // Check if commit status is displayed in message column sel := doc.doc.Find("#commits-table tbody tr td.message a.commit-statuses-trigger i.commit-status") - assert.Equal(t, sel.Length(), 1) + assert.Equal(t, 1, sel.Length()) for _, class := range classes { assert.True(t, sel.HasClass(class)) } diff --git a/integrations/repo_test.go b/integrations/repo_test.go index c1652aeb1d32b..8c4cdf5a969fc 100644 --- a/integrations/repo_test.go +++ b/integrations/repo_test.go @@ -154,12 +154,12 @@ func TestViewRepoWithSymlinks(t *testing.T) { file := strings.Trim(s.Find("A").Text(), " \t\n") return fmt.Sprintf("%s: %s", file, cls) }) - assert.Equal(t, len(items), 5) - assert.Equal(t, items[0], "a: svg octicon-file-directory") - assert.Equal(t, items[1], "link_b: svg octicon-file-submodule") - assert.Equal(t, items[2], "link_d: svg octicon-file-symlink-file") - assert.Equal(t, items[3], "link_hi: svg octicon-file-symlink-file") - assert.Equal(t, items[4], "link_link: svg octicon-file-symlink-file") + assert.Len(t, items, 5) + assert.Equal(t, "a: svg octicon-file-directory", items[0]) + assert.Equal(t, "link_b: svg octicon-file-submodule", items[1]) + assert.Equal(t, "link_d: svg octicon-file-symlink-file", items[2]) + assert.Equal(t, "link_hi: svg octicon-file-symlink-file", items[3]) + assert.Equal(t, "link_link: svg octicon-file-symlink-file", items[4]) } // TestViewAsRepoAdmin tests PR #2167 diff --git a/models/attachment_test.go b/models/attachment_test.go index fa7fd3471bab0..700b7c09dbb11 100644 --- a/models/attachment_test.go +++ b/models/attachment_test.go @@ -61,11 +61,11 @@ func TestGetByCommentOrIssueID(t *testing.T) { // count of attachments from issue ID attachments, err := GetAttachmentsByIssueID(1) assert.NoError(t, err) - assert.Equal(t, 1, len(attachments)) + assert.Len(t, attachments, 1) attachments, err = GetAttachmentsByCommentID(1) assert.NoError(t, err) - assert.Equal(t, 2, len(attachments)) + assert.Len(t, attachments, 2) } func TestDeleteAttachments(t *testing.T) { @@ -122,7 +122,7 @@ func TestGetAttachmentsByUUIDs(t *testing.T) { attachList, err := GetAttachmentsByUUIDs(DefaultDBContext(), []string{"a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a17", "not-existing-uuid"}) assert.NoError(t, err) - assert.Equal(t, 2, len(attachList)) + assert.Len(t, attachList, 2) assert.Equal(t, "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", attachList[0].UUID) assert.Equal(t, "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a17", attachList[1].UUID) assert.Equal(t, int64(1), attachList[0].IssueID) diff --git a/models/consistency.go b/models/consistency.go index 77a8018266e51..9cfd02195effd 100644 --- a/models/consistency.go +++ b/models/consistency.go @@ -180,17 +180,21 @@ func CountOrphanedLabels() (int64, error) { } norepo, err := x.Table("label"). - Join("LEFT", "repository", "label.repo_id=repository.id"). - Where(builder.IsNull{"repository.id"}).And(builder.Gt{"label.repo_id": 0}). - Count("id") + Where(builder.And( + builder.Gt{"repo_id": 0}, + builder.NotIn("repo_id", builder.Select("id").From("repository")), + )). + Count() if err != nil { return 0, err } noorg, err := x.Table("label"). - Join("LEFT", "`user`", "label.org_id=`user`.id"). - Where(builder.IsNull{"`user`.id"}).And(builder.Gt{"label.org_id": 0}). - Count("id") + Where(builder.And( + builder.Gt{"org_id": 0}, + builder.NotIn("org_id", builder.Select("id").From("user")), + )). + Count() if err != nil { return 0, err } @@ -206,17 +210,21 @@ func DeleteOrphanedLabels() error { } // delete labels with none existing repos - if _, err := x.In("id", builder.Select("label.id").From("label"). - Join("LEFT", "repository", "label.repo_id=repository.id"). - Where(builder.IsNull{"repository.id"}).And(builder.Gt{"label.repo_id": 0})). + if _, err := x. + Where(builder.And( + builder.Gt{"repo_id": 0}, + builder.NotIn("repo_id", builder.Select("id").From("repository")), + )). Delete(Label{}); err != nil { return err } // delete labels with none existing orgs - if _, err := x.In("id", builder.Select("label.id").From("label"). - Join("LEFT", "`user`", "label.org_id=`user`.id"). - Where(builder.IsNull{"`user`.id"}).And(builder.Gt{"label.org_id": 0})). + if _, err := x. + Where(builder.And( + builder.Gt{"org_id": 0}, + builder.NotIn("org_id", builder.Select("id").From("user")), + )). Delete(Label{}); err != nil { return err } @@ -227,15 +235,14 @@ func DeleteOrphanedLabels() error { // CountOrphanedIssueLabels return count of IssueLabels witch have no label behind anymore func CountOrphanedIssueLabels() (int64, error) { return x.Table("issue_label"). - Join("LEFT", "label", "issue_label.label_id = label.id"). - Where(builder.IsNull{"label.id"}).Count() + NotIn("label_id", builder.Select("id").From("label")). + Count() } // DeleteOrphanedIssueLabels delete IssueLabels witch have no label behind anymore func DeleteOrphanedIssueLabels() error { - _, err := x.In("id", builder.Select("issue_label.id").From("issue_label"). - Join("LEFT", "label", "issue_label.label_id = label.id"). - Where(builder.IsNull{"label.id"})). + _, err := x. + NotIn("label_id", builder.Select("id").From("label")). Delete(IssueLabel{}) return err diff --git a/models/error.go b/models/error.go index 48cba57a8135c..501bf8686936d 100644 --- a/models/error.go +++ b/models/error.go @@ -237,6 +237,21 @@ func (err ErrEmailAddressNotExist) Error() string { return fmt.Sprintf("Email address does not exist [email: %s]", err.Email) } +// ErrPrimaryEmailCannotDelete primary email address cannot be deleted +type ErrPrimaryEmailCannotDelete struct { + Email string +} + +// IsErrPrimaryEmailCannotDelete checks if an error is an ErrPrimaryEmailCannotDelete +func IsErrPrimaryEmailCannotDelete(err error) bool { + _, ok := err.(ErrPrimaryEmailCannotDelete) + return ok +} + +func (err ErrPrimaryEmailCannotDelete) Error() string { + return fmt.Sprintf("Primary email address cannot be deleted [email: %s]", err.Email) +} + // ErrOpenIDAlreadyUsed represents a "OpenIDAlreadyUsed" kind of error. type ErrOpenIDAlreadyUsed struct { OpenID string diff --git a/models/fixtures/email_address.yml b/models/fixtures/email_address.yml index c37d9a787778f..e7df5fdc5f01c 100644 --- a/models/fixtures/email_address.yml +++ b/models/fixtures/email_address.yml @@ -1,35 +1,279 @@ - id: 1 - uid: 1 + uid: 11 email: user11@example.com + lower_email: user11@example.com is_activated: false + is_primary: true - id: 2 - uid: 1 + uid: 12 email: user12@example.com - is_activated: false + lower_email: user12@example.com + is_activated: true + is_primary: true - id: 3 uid: 2 email: user2@example.com + lower_email: user2@example.com is_activated: true + is_primary: true - id: 4 - uid: 2 + uid: 21 email: user21@example.com - is_activated: false + lower_email: user21@example.com + is_activated: true + is_primary: true - id: 5 uid: 9999999 email: user9999999@example.com + lower_email: user9999999@example.com is_activated: true + is_primary: false - id: 6 uid: 10 + email: user10@example.com + lower_email: user10@example.com + is_activated: true + is_primary: true + +- + id: 7 + uid: 10 email: user101@example.com + lower_email: user101@example.com + is_activated: true + is_primary: false + +- + id: 8 + uid: 9 + email: user9@example.com + lower_email: user9@example.com + is_activated: false + is_primary: true + +- + id: 9 + uid: 1 + email: user1@example.com + lower_email: user1@example.com + is_activated: true + is_primary: true + +- + id: 10 + uid: 3 + email: user3@example.com + lower_email: user3@example.com + is_activated: true + is_primary: true + +- + id: 11 + uid: 4 + email: user4@example.com + lower_email: user4@example.com + is_activated: true + is_primary: true + +- + id: 12 + uid: 5 + email: user5@example.com + lower_email: user5@example.com is_activated: true + is_primary: true + +- + id: 13 + uid: 6 + email: user6@example.com + lower_email: user6@example.com + is_activated: true + is_primary: true + +- + id: 14 + uid: 7 + email: user7@example.com + lower_email: user7@example.com + is_activated: true + is_primary: true + +- + id: 15 + uid: 8 + email: user8@example.com + lower_email: user8@example.com + is_activated: true + is_primary: true + +- + id: 16 + uid: 13 + email: user13@example.com + lower_email: user13@example.com + is_activated: true + is_primary: true + +- + id: 17 + uid: 14 + email: user14@example.com + lower_email: user14@example.com + is_activated: true + is_primary: true + +- + id: 18 + uid: 15 + email: user15@example.com + lower_email: user15@example.com + is_activated: true + is_primary: true + +- + id: 19 + uid: 16 + email: user16@example.com + lower_email: user16@example.com + is_activated: true + is_primary: true + +- + id: 20 + uid: 17 + email: user17@example.com + lower_email: user17@example.com + is_activated: true + is_primary: true + +- + id: 21 + uid: 18 + email: user18@example.com + lower_email: user18@example.com + is_activated: true + is_primary: true + +- + id: 22 + uid: 19 + email: user19@example.com + lower_email: user19@example.com + is_activated: true + is_primary: true + +- + id: 23 + uid: 20 + email: user20@example.com + lower_email: user20@example.com + is_activated: true + is_primary: true + +- + id: 24 + uid: 22 + email: limited_org@example.com + lower_email: limited_org@example.com + is_activated: true + is_primary: true + +- + id: 25 + uid: 23 + email: privated_org@example.com + lower_email: privated_org@example.com + is_activated: true + is_primary: true + +- + id: 26 + uid: 24 + email: user24@example.com + lower_email: user24@example.com + is_activated: true + is_primary: true + +- + id: 27 + uid: 25 + email: org25@example.com + lower_email: org25@example.com + is_activated: true + is_primary: true + +- + id: 28 + uid: 26 + email: org26@example.com + lower_email: org26@example.com + is_activated: true + is_primary: true + +- + id: 29 + uid: 27 + email: user27@example.com + lower_email: user27@example.com + is_activated: true + is_primary: true + +- + id: 30 + uid: 28 + email: user28@example.com + lower_email: user28@example.com + is_activated: true + is_primary: true + +- + id: 31 + uid: 29 + email: user29@example.com + lower_email: user29@example.com + is_activated: true + is_primary: true + +- + id: 32 + uid: 30 + email: user30@example.com + lower_email: user30@example.com + is_activated: true + is_primary: true + +- + id: 33 + uid: 1 + email: user1-2@example.com + lower_email: user1-2@example.com + is_activated: true + is_primary: false + +- + id: 34 + uid: 1 + email: user1-3@example.com + lower_email: user1-3@example.com + is_activated: true + is_primary: false + +- + id: 35 + uid: 2 + email: user2-2@example.com + lower_email: user2-2@example.com + is_activated: false + is_primary: false \ No newline at end of file diff --git a/models/fixtures/issue.yml b/models/fixtures/issue.yml index 31df00d9e6999..946899d6ff034 100644 --- a/models/fixtures/issue.yml +++ b/models/fixtures/issue.yml @@ -152,7 +152,7 @@ - id: 13 repo_id: 50 - index: 0 + index: 1 poster_id: 2 name: issue in active repo content: we'll be testing github issue 13171 with this. @@ -164,7 +164,7 @@ - id: 14 repo_id: 51 - index: 0 + index: 1 poster_id: 2 name: issue in archived repo content: we'll be testing github issue 13171 with this. diff --git a/models/fixtures/issue_index.yml b/models/fixtures/issue_index.yml new file mode 100644 index 0000000000000..49d95c57ab777 --- /dev/null +++ b/models/fixtures/issue_index.yml @@ -0,0 +1,24 @@ +- + group_id: 1 + max_index: 5 +- + group_id: 2 + max_index: 2 +- + group_id: 3 + max_index: 2 +- + group_id: 10 + max_index: 1 +- + group_id: 48 + max_index: 1 +- + group_id: 42 + max_index: 1 +- + group_id: 50 + max_index: 1 +- + group_id: 51 + max_index: 1 \ No newline at end of file diff --git a/models/fixtures/issue_user.yml b/models/fixtures/issue_user.yml index 8039b1e40ff15..64824316ea275 100644 --- a/models/fixtures/issue_user.yml +++ b/models/fixtures/issue_user.yml @@ -17,4 +17,4 @@ uid: 4 issue_id: 1 is_read: false - is_mentioned: false + is_mentioned: true diff --git a/models/fixtures/release.yml b/models/fixtures/release.yml index 5e577d3fddbc7..1703f959d2687 100644 --- a/models/fixtures/release.yml +++ b/models/fixtures/release.yml @@ -1,5 +1,4 @@ -- - id: 1 +- id: 1 repo_id: 1 publisher_id: 2 tag_name: "v1.1" @@ -13,8 +12,7 @@ is_tag: false created_unix: 946684800 -- - id: 2 +- id: 2 repo_id: 40 publisher_id: 2 tag_name: "v1.1" @@ -28,8 +26,7 @@ is_tag: false created_unix: 946684800 -- - id: 3 +- id: 3 repo_id: 1 publisher_id: 2 tag_name: "delete-tag" @@ -43,8 +40,7 @@ is_tag: true created_unix: 946684800 -- - id: 4 +- id: 4 repo_id: 1 publisher_id: 2 tag_name: "draft-release" @@ -55,3 +51,18 @@ is_prerelease: false is_tag: false created_unix: 1619524806 + +- id: 5 + repo_id: 1 + publisher_id: 2 + tag_name: "v1.0" + lower_tag_name: "v1.0" + target: "master" + title: "pre-release" + note: "some text for a pre release" + sha1: "65f1bf27bc3bf70f64657658635e66094edbcb4d" + num_commits: 1 + is_draft: false + is_prerelease: true + is_tag: false + created_unix: 946684800 diff --git a/models/gpg_key.go b/models/gpg_key.go index 2ffcf47ca7d46..9530eacb0a701 100644 --- a/models/gpg_key.go +++ b/models/gpg_key.go @@ -301,7 +301,7 @@ func parseGPGKey(ownerID int64, e *openpgp.Entity) (*GPGKey, error) { } email := strings.ToLower(strings.TrimSpace(ident.UserId.Email)) for _, e := range userEmails { - if e.Email == email { + if e.LowerEmail == email { emails = append(emails, e) break } diff --git a/models/index.go b/models/index.go new file mode 100644 index 0000000000000..18db13c490c7c --- /dev/null +++ b/models/index.go @@ -0,0 +1,113 @@ +// 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 models + +import ( + "errors" + "fmt" + + "code.gitea.io/gitea/modules/setting" +) + +// ResourceIndex represents a resource index which could be used as issue/release and others +// We can create different tables i.e. issue_index, release_index and etc. +type ResourceIndex struct { + GroupID int64 `xorm:"unique"` + MaxIndex int64 `xorm:"index"` +} + +// IssueIndex represents the issue index table +type IssueIndex ResourceIndex + +// upsertResourceIndex the function will not return until it acquires the lock or receives an error. +func upsertResourceIndex(e Engine, tableName string, groupID int64) (err error) { + // An atomic UPSERT operation (INSERT/UPDATE) is the only operation + // that ensures that the key is actually locked. + switch { + case setting.Database.UseSQLite3 || setting.Database.UsePostgreSQL: + _, err = e.Exec(fmt.Sprintf("INSERT INTO %s (group_id, max_index) "+ + "VALUES (?,1) ON CONFLICT (group_id) DO UPDATE SET max_index = %s.max_index+1", + tableName, tableName), groupID) + case setting.Database.UseMySQL: + _, err = e.Exec(fmt.Sprintf("INSERT INTO %s (group_id, max_index) "+ + "VALUES (?,1) ON DUPLICATE KEY UPDATE max_index = max_index+1", tableName), + groupID) + case setting.Database.UseMSSQL: + // https://weblogs.sqlteam.com/dang/2009/01/31/upsert-race-condition-with-merge/ + _, err = e.Exec(fmt.Sprintf("MERGE %s WITH (HOLDLOCK) as target "+ + "USING (SELECT ? AS group_id) AS src "+ + "ON src.group_id = target.group_id "+ + "WHEN MATCHED THEN UPDATE SET target.max_index = target.max_index+1 "+ + "WHEN NOT MATCHED THEN INSERT (group_id, max_index) "+ + "VALUES (src.group_id, 1);", tableName), + groupID) + default: + return fmt.Errorf("database type not supported") + } + return +} + +var ( + // ErrResouceOutdated represents an error when request resource outdated + ErrResouceOutdated = errors.New("resource outdated") + // ErrGetResourceIndexFailed represents an error when resource index retries 3 times + ErrGetResourceIndexFailed = errors.New("get resource index failed") +) + +const ( + maxDupIndexAttempts = 3 +) + +// GetNextResourceIndex retried 3 times to generate a resource index +func GetNextResourceIndex(tableName string, groupID int64) (int64, error) { + for i := 0; i < maxDupIndexAttempts; i++ { + idx, err := getNextResourceIndex(tableName, groupID) + if err == ErrResouceOutdated { + continue + } + if err != nil { + return 0, err + } + return idx, nil + } + return 0, ErrGetResourceIndexFailed +} + +// deleteResouceIndex delete resource index +func deleteResouceIndex(e Engine, tableName string, groupID int64) error { + _, err := e.Exec(fmt.Sprintf("DELETE FROM %s WHERE group_id=?", tableName), groupID) + return err +} + +// getNextResourceIndex return the next index +func getNextResourceIndex(tableName string, groupID int64) (int64, error) { + sess := x.NewSession() + defer sess.Close() + if err := sess.Begin(); err != nil { + return 0, err + } + var preIdx int64 + _, err := sess.SQL(fmt.Sprintf("SELECT max_index FROM %s WHERE group_id = ?", tableName), groupID).Get(&preIdx) + if err != nil { + return 0, err + } + + if err := upsertResourceIndex(sess, tableName, groupID); err != nil { + return 0, err + } + + var curIdx int64 + has, err := sess.SQL(fmt.Sprintf("SELECT max_index FROM %s WHERE group_id = ? AND max_index=?", tableName), groupID, preIdx+1).Get(&curIdx) + if err != nil { + return 0, err + } + if !has { + return 0, ErrResouceOutdated + } + if err := sess.Commit(); err != nil { + return 0, err + } + return curIdx, nil +} diff --git a/models/index_test.go b/models/index_test.go new file mode 100644 index 0000000000000..40e570ad9fa77 --- /dev/null +++ b/models/index_test.go @@ -0,0 +1,27 @@ +// 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 models + +import ( + "fmt" + "sync" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestResourceIndex(t *testing.T) { + assert.NoError(t, PrepareTestDatabase()) + + var wg sync.WaitGroup + for i := 0; i < 100; i++ { + wg.Add(1) + go func(i int) { + testInsertIssue(t, fmt.Sprintf("issue %d", i+1), "my issue", 0) + wg.Done() + }(i) + } + wg.Wait() +} diff --git a/models/issue.go b/models/issue.go index 6912df6c28ac7..ffbc110a6becc 100644 --- a/models/issue.go +++ b/models/issue.go @@ -78,9 +78,8 @@ var ( ) const ( - issueTasksRegexpStr = `(^\s*[-*]\s\[[\sxX]\]\s.)|(\n\s*[-*]\s\[[\sxX]\]\s.)` - issueTasksDoneRegexpStr = `(^\s*[-*]\s\[[xX]\]\s.)|(\n\s*[-*]\s\[[xX]\]\s.)` - issueMaxDupIndexAttempts = 3 + issueTasksRegexpStr = `(^\s*[-*]\s\[[\sxX]\]\s.)|(\n\s*[-*]\s\[[\sxX]\]\s.)` + issueTasksDoneRegexpStr = `(^\s*[-*]\s\[[xX]\]\s.)|(\n\s*[-*]\s\[[xX]\]\s.)` ) func init() { @@ -896,21 +895,17 @@ func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) { } } - // Milestone validation should happen before insert actual object. - if _, err := e.SetExpr("`index`", "coalesce(MAX(`index`),0)+1"). - Where("repo_id=?", opts.Issue.RepoID). - Insert(opts.Issue); err != nil { - return ErrNewIssueInsert{err} + if opts.Issue.Index <= 0 { + return fmt.Errorf("no issue index provided") + } + if opts.Issue.ID > 0 { + return fmt.Errorf("issue exist") } - inserted, err := getIssueByID(e, opts.Issue.ID) - if err != nil { + if _, err := e.Insert(opts.Issue); err != nil { return err } - // Patch Index with the value calculated by the database - opts.Issue.Index = inserted.Index - if opts.Issue.MilestoneID > 0 { if _, err = e.Exec("UPDATE `milestone` SET num_issues=num_issues+1 WHERE id=?", opts.Issue.MilestoneID); err != nil { return err @@ -987,24 +982,13 @@ func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) { // NewIssue creates new issue with labels for repository. func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string) (err error) { - // Retry several times in case INSERT fails due to duplicate key for (repo_id, index); see #7887 - i := 0 - for { - if err = newIssueAttempt(repo, issue, labelIDs, uuids); err == nil { - return nil - } - if !IsErrNewIssueInsert(err) { - return err - } - if i++; i == issueMaxDupIndexAttempts { - break - } - log.Error("NewIssue: error attempting to insert the new issue; will retry. Original error: %v", err) + idx, err := GetNextResourceIndex("issue_index", repo.ID) + if err != nil { + return fmt.Errorf("generate issue index failed: %v", err) } - return fmt.Errorf("NewIssue: too many errors attempting to insert the new issue. Last error was: %v", err) -} -func newIssueAttempt(repo *Repository, issue *Issue, labelIDs []int64, uuids []string) (err error) { + issue.Index = idx + sess := x.NewSession() defer sess.Close() if err = sess.Begin(); err != nil { @@ -1086,7 +1070,7 @@ func getIssuesByIDs(e Engine, issueIDs []int64) ([]*Issue, error) { func getIssueIDsByRepoID(e Engine, repoID int64) ([]int64, error) { ids := make([]int64, 0, 10) - err := e.Table("issue").Where("repo_id = ?", repoID).Find(&ids) + err := e.Table("issue").Cols("id").Where("repo_id = ?", repoID).Find(&ids) return ids, err } @@ -1116,6 +1100,7 @@ type IssuesOptions struct { LabelIDs []int64 IncludedLabelNames []string ExcludedLabelNames []string + IncludeMilestones []string SortType string IssueIDs []int64 UpdatedAfterUnix int64 @@ -1143,9 +1128,18 @@ func sortIssuesSession(sess *xorm.Session, sortType string, priorityRepoID int64 sess.Desc("issue.priority") case "nearduedate": // 253370764800 is 01/01/9999 @ 12:00am (UTC) - sess.OrderBy("CASE WHEN issue.deadline_unix = 0 THEN 253370764800 ELSE issue.deadline_unix END ASC") + sess.Join("LEFT", "milestone", "issue.milestone_id = milestone.id"). + OrderBy("CASE " + + "WHEN issue.deadline_unix = 0 AND (milestone.deadline_unix = 0 OR milestone.deadline_unix IS NULL) THEN 253370764800 " + + "WHEN milestone.deadline_unix = 0 OR milestone.deadline_unix IS NULL THEN issue.deadline_unix " + + "WHEN milestone.deadline_unix < issue.deadline_unix OR issue.deadline_unix = 0 THEN milestone.deadline_unix " + + "ELSE issue.deadline_unix END ASC") case "farduedate": - sess.Desc("issue.deadline_unix") + sess.Join("LEFT", "milestone", "issue.milestone_id = milestone.id"). + OrderBy("CASE " + + "WHEN milestone.deadline_unix IS NULL THEN issue.deadline_unix " + + "WHEN milestone.deadline_unix < issue.deadline_unix OR issue.deadline_unix = 0 THEN milestone.deadline_unix " + + "ELSE issue.deadline_unix END DESC") case "priorityrepo": sess.OrderBy("CASE WHEN issue.repo_id = " + strconv.FormatInt(priorityRepoID, 10) + " THEN 1 ELSE 2 END, issue.created_unix DESC") default: @@ -1248,6 +1242,13 @@ func (opts *IssuesOptions) setupSession(sess *xorm.Session) { if len(opts.ExcludedLabelNames) > 0 { sess.And(builder.NotIn("issue.id", BuildLabelNamesIssueIDsCondition(opts.ExcludedLabelNames))) } + + if len(opts.IncludeMilestones) > 0 { + sess.In("issue.milestone_id", + builder.Select("id"). + From("milestone"). + Where(builder.In("name", opts.IncludeMilestones))) + } } func applyReposCondition(sess *xorm.Session, repoIDs []int64) *xorm.Session { diff --git a/models/issue_comment.go b/models/issue_comment.go index 26bf122dc9835..1b98b248b1fcc 100644 --- a/models/issue_comment.go +++ b/models/issue_comment.go @@ -762,6 +762,8 @@ func updateCommentInfos(e *xorm.Session, opts *CreateCommentOptions, comment *Co } } fallthrough + case CommentTypeReview: + fallthrough case CommentTypeComment: if _, err = e.Exec("UPDATE `issue` SET num_comments=num_comments+1 WHERE id=?", opts.Issue.ID); err != nil { return err diff --git a/models/issue_stopwatch_test.go b/models/issue_stopwatch_test.go index 6fc2b1ce5d718..b6af5e93b51a7 100644 --- a/models/issue_stopwatch_test.go +++ b/models/issue_stopwatch_test.go @@ -67,7 +67,7 @@ func TestCreateOrStopIssueStopwatch(t *testing.T) { assert.NoError(t, CreateOrStopIssueStopwatch(user3, issue1)) sw := AssertExistsAndLoadBean(t, &Stopwatch{UserID: 3, IssueID: 1}).(*Stopwatch) - assert.Equal(t, true, sw.CreatedUnix <= timeutil.TimeStampNow()) + assert.LessOrEqual(t, sw.CreatedUnix, timeutil.TimeStampNow()) assert.NoError(t, CreateOrStopIssueStopwatch(user2, issue2)) AssertNotExistsBean(t, &Stopwatch{UserID: 2, IssueID: 2}) diff --git a/models/issue_test.go b/models/issue_test.go index c21b1d6ae98e6..f2c9b7a68f8e5 100644 --- a/models/issue_test.go +++ b/models/issue_test.go @@ -36,6 +36,14 @@ func TestIssue_ReplaceLabels(t *testing.T) { testSuccess(1, []int64{}) } +func Test_GetIssueIDsByRepoID(t *testing.T) { + assert.NoError(t, PrepareTestDatabase()) + + ids, err := GetIssueIDsByRepoID(1) + assert.NoError(t, err) + assert.Len(t, ids, 5) +} + func TestIssueAPIURL(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) issue := AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue) @@ -337,37 +345,45 @@ func TestGetRepoIDsForIssuesOptions(t *testing.T) { } } -func testInsertIssue(t *testing.T, title, content string) { - repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) - user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) - - issue := Issue{ - RepoID: repo.ID, - PosterID: user.ID, - Title: title, - Content: content, - } - err := NewIssue(repo, &issue, nil, nil) - assert.NoError(t, err) - +func testInsertIssue(t *testing.T, title, content string, expectIndex int64) *Issue { var newIssue Issue - has, err := x.ID(issue.ID).Get(&newIssue) - assert.NoError(t, err) - assert.True(t, has) - assert.EqualValues(t, issue.Title, newIssue.Title) - assert.EqualValues(t, issue.Content, newIssue.Content) - // there are 5 issues and max index is 5 on repository 1, so this one should 6 - assert.EqualValues(t, 6, newIssue.Index) + t.Run(title, func(t *testing.T) { + repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) + user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + + issue := Issue{ + RepoID: repo.ID, + PosterID: user.ID, + Title: title, + Content: content, + } + err := NewIssue(repo, &issue, nil, nil) + assert.NoError(t, err) - _, err = x.ID(issue.ID).Delete(new(Issue)) - assert.NoError(t, err) + has, err := x.ID(issue.ID).Get(&newIssue) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, issue.Title, newIssue.Title) + assert.EqualValues(t, issue.Content, newIssue.Content) + if expectIndex > 0 { + assert.EqualValues(t, expectIndex, newIssue.Index) + } + }) + return &newIssue } func TestIssue_InsertIssue(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) - testInsertIssue(t, "my issue1", "special issue's comments?") - testInsertIssue(t, `my issue2, this is my son's love \n \r \ `, "special issue's '' comments?") + // there are 5 issues and max index is 5 on repository 1, so this one should 6 + issue := testInsertIssue(t, "my issue1", "special issue's comments?", 6) + _, err := x.ID(issue.ID).Delete(new(Issue)) + assert.NoError(t, err) + + issue = testInsertIssue(t, `my issue2, this is my son's love \n \r \ `, "special issue's '' comments?", 7) + _, err = x.ID(issue.ID).Delete(new(Issue)) + assert.NoError(t, err) + } func TestIssue_ResolveMentions(t *testing.T) { diff --git a/models/issue_watch_test.go b/models/issue_watch_test.go index 762b1486c6461..013ca67e1efc8 100644 --- a/models/issue_watch_test.go +++ b/models/issue_watch_test.go @@ -32,7 +32,7 @@ func TestGetIssueWatch(t *testing.T) { iw, exists, err := GetIssueWatch(2, 2) assert.True(t, exists) assert.NoError(t, err) - assert.EqualValues(t, false, iw.IsWatching) + assert.False(t, iw.IsWatching) _, exists, err = GetIssueWatch(3, 1) assert.False(t, exists) diff --git a/models/issue_xref_test.go b/models/issue_xref_test.go index 936d1124be475..a2d1a4b11e1f1 100644 --- a/models/issue_xref_test.go +++ b/models/issue_xref_test.go @@ -25,7 +25,7 @@ func TestXRef_AddCrossReferences(t *testing.T) { ref := AssertExistsAndLoadBean(t, &Comment{IssueID: itarget.ID, RefIssueID: pr.ID, RefCommentID: 0}).(*Comment) assert.Equal(t, CommentTypePullRef, ref.Type) assert.Equal(t, pr.RepoID, ref.RefRepoID) - assert.Equal(t, true, ref.RefIsPull) + assert.True(t, ref.RefIsPull) assert.Equal(t, references.XRefActionCloses, ref.RefAction) // Comment on PR to reopen issue #1 @@ -34,7 +34,7 @@ func TestXRef_AddCrossReferences(t *testing.T) { ref = AssertExistsAndLoadBean(t, &Comment{IssueID: itarget.ID, RefIssueID: pr.ID, RefCommentID: c.ID}).(*Comment) assert.Equal(t, CommentTypeCommentRef, ref.Type) assert.Equal(t, pr.RepoID, ref.RefRepoID) - assert.Equal(t, true, ref.RefIsPull) + assert.True(t, ref.RefIsPull) assert.Equal(t, references.XRefActionReopens, ref.RefAction) // Issue mentioning issue #1 @@ -43,7 +43,7 @@ func TestXRef_AddCrossReferences(t *testing.T) { ref = AssertExistsAndLoadBean(t, &Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}).(*Comment) assert.Equal(t, CommentTypeIssueRef, ref.Type) assert.Equal(t, pr.RepoID, ref.RefRepoID) - assert.Equal(t, false, ref.RefIsPull) + assert.False(t, ref.RefIsPull) assert.Equal(t, references.XRefActionNone, ref.RefAction) // Issue #4 to test against @@ -55,7 +55,7 @@ func TestXRef_AddCrossReferences(t *testing.T) { ref = AssertExistsAndLoadBean(t, &Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}).(*Comment) assert.Equal(t, CommentTypeIssueRef, ref.Type) assert.Equal(t, i.RepoID, ref.RefRepoID) - assert.Equal(t, false, ref.RefIsPull) + assert.False(t, ref.RefIsPull) assert.Equal(t, references.XRefActionNone, ref.RefAction) // Cross-reference to issue #4 with no permission @@ -125,12 +125,27 @@ func TestXRef_ResolveCrossReferences(t *testing.T) { func testCreateIssue(t *testing.T, repo, doer int64, title, content string, ispull bool) *Issue { r := AssertExistsAndLoadBean(t, &Repository{ID: repo}).(*Repository) d := AssertExistsAndLoadBean(t, &User{ID: doer}).(*User) - i := &Issue{RepoID: r.ID, PosterID: d.ID, Poster: d, Title: title, Content: content, IsPull: ispull} + + idx, err := GetNextResourceIndex("issue_index", r.ID) + assert.NoError(t, err) + i := &Issue{ + RepoID: r.ID, + PosterID: d.ID, + Poster: d, + Title: title, + Content: content, + IsPull: ispull, + Index: idx, + } sess := x.NewSession() defer sess.Close() + assert.NoError(t, sess.Begin()) - _, err := sess.SetExpr("`index`", "coalesce(MAX(`index`),0)+1").Where("repo_id=?", repo).Insert(i) + err = newIssue(sess, d, NewIssueOptions{ + Repo: r, + Issue: i, + }) assert.NoError(t, err) i, err = getIssueByID(sess, i.ID) assert.NoError(t, err) diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index d440722c96f8c..880f55092d361 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -311,6 +311,14 @@ var migrations = []Migration{ NewMigration("Convert avatar url to text", convertAvatarURLToText), // v180 -> v181 NewMigration("Delete credentials from past migrations", deleteMigrationCredentials), + // v181 -> v182 + NewMigration("Always save primary email on email address table", addPrimaryEmail2EmailAddress), + // v182 -> v183 + NewMigration("Add issue resource index table", addIssueResourceIndexTable), + // v183 -> v184 + NewMigration("Create PushMirror table", createPushMirrorTable), + // v184 -> v185 + NewMigration("Rename Task errors to message", renameTaskErrorsToMessage), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v180.go b/models/migrations/v180.go index c2a3ff961a9ec..8a7637debaf14 100644 --- a/models/migrations/v180.go +++ b/models/migrations/v180.go @@ -5,9 +5,6 @@ package migrations import ( - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/migrations/base" - "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" jsoniter "github.com/json-iterator/go" @@ -16,20 +13,38 @@ import ( ) func deleteMigrationCredentials(x *xorm.Engine) (err error) { + // Task represents a task + type Task struct { + ID int64 + DoerID int64 `xorm:"index"` // operator + OwnerID int64 `xorm:"index"` // repo owner id, when creating, the repoID maybe zero + RepoID int64 `xorm:"index"` + Type int + Status int `xorm:"index"` + StartTime int64 + EndTime int64 + PayloadContent string `xorm:"TEXT"` + Errors string `xorm:"TEXT"` // if task failed, saved the error reason + Created int64 `xorm:"created"` + } + + const TaskTypeMigrateRepo = 0 + const TaskStatusStopped = 2 + const batchSize = 100 // only match migration tasks, that are not pending or running cond := builder.Eq{ - "type": structs.TaskTypeMigrateRepo, + "type": TaskTypeMigrateRepo, }.And(builder.Gte{ - "status": structs.TaskStatusStopped, + "status": TaskStatusStopped, }) sess := x.NewSession() defer sess.Close() for start := 0; ; start += batchSize { - tasks := make([]*models.Task, 0, batchSize) + tasks := make([]*Task, 0, batchSize) if err = sess.Limit(batchSize, start).Where(cond, 0).Find(&tasks); err != nil { return } @@ -55,7 +70,41 @@ func deleteMigrationCredentials(x *xorm.Engine) (err error) { } func removeCredentials(payload string) (string, error) { - var opts base.MigrateOptions + // MigrateOptions defines the way a repository gets migrated + // this is for internal usage by migrations module and func who interact with it + type MigrateOptions struct { + // required: true + CloneAddr string `json:"clone_addr" binding:"Required"` + CloneAddrEncrypted string `json:"clone_addr_encrypted,omitempty"` + AuthUsername string `json:"auth_username"` + AuthPassword string `json:"-"` + AuthPasswordEncrypted string `json:"auth_password_encrypted,omitempty"` + AuthToken string `json:"-"` + AuthTokenEncrypted string `json:"auth_token_encrypted,omitempty"` + // required: true + UID int `json:"uid" binding:"Required"` + // required: true + RepoName string `json:"repo_name" binding:"Required"` + Mirror bool `json:"mirror"` + LFS bool `json:"lfs"` + LFSEndpoint string `json:"lfs_endpoint"` + Private bool `json:"private"` + Description string `json:"description"` + OriginalURL string + GitServiceType int + Wiki bool + Issues bool + Milestones bool + Labels bool + Releases bool + Comments bool + PullRequests bool + ReleaseAssets bool + MigrateToRepoID int64 + MirrorInterval string `json:"mirror_interval"` + } + + var opts MigrateOptions json := jsoniter.ConfigCompatibleWithStandardLibrary err := json.Unmarshal([]byte(payload), &opts) if err != nil { @@ -64,7 +113,7 @@ func removeCredentials(payload string) (string, error) { opts.AuthPassword = "" opts.AuthToken = "" - opts.CloneAddr = util.SanitizeURLCredentials(opts.CloneAddr, true) + opts.CloneAddr = util.NewStringURLSanitizer(opts.CloneAddr, true).Replace(opts.CloneAddr) confBytes, err := json.Marshal(opts) if err != nil { diff --git a/models/migrations/v181.go b/models/migrations/v181.go new file mode 100644 index 0000000000000..6ba4edc155952 --- /dev/null +++ b/models/migrations/v181.go @@ -0,0 +1,92 @@ +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package migrations + +import ( + "strings" + + "xorm.io/xorm" +) + +func addPrimaryEmail2EmailAddress(x *xorm.Engine) (err error) { + type User struct { + ID int64 `xorm:"pk autoincr"` + Email string `xorm:"NOT NULL"` + IsActive bool `xorm:"INDEX"` // Activate primary email + } + + type EmailAddress1 struct { + ID int64 `xorm:"pk autoincr"` + UID int64 `xorm:"INDEX NOT NULL"` + Email string `xorm:"UNIQUE NOT NULL"` + LowerEmail string + IsActivated bool + IsPrimary bool `xorm:"DEFAULT(false) NOT NULL"` + } + + // Add lower_email and is_primary columns + if err = x.Table("email_address").Sync2(new(EmailAddress1)); err != nil { + return + } + + if _, err = x.Exec("UPDATE email_address SET lower_email=LOWER(email), is_primary=?", false); err != nil { + return + } + + type EmailAddress struct { + ID int64 `xorm:"pk autoincr"` + UID int64 `xorm:"INDEX NOT NULL"` + Email string `xorm:"UNIQUE NOT NULL"` + LowerEmail string `xorm:"UNIQUE NOT NULL"` + IsActivated bool + IsPrimary bool `xorm:"DEFAULT(false) NOT NULL"` + } + + // change lower_email as unique + if err = x.Sync2(new(EmailAddress)); err != nil { + return + } + + sess := x.NewSession() + defer sess.Close() + + const batchSize = 100 + + for start := 0; ; start += batchSize { + users := make([]*User, 0, batchSize) + if err = sess.Limit(batchSize, start).Find(&users); err != nil { + return + } + if len(users) == 0 { + break + } + + for _, user := range users { + var exist bool + exist, err = sess.Where("email=?", user.Email).Table("email_address").Exist() + if err != nil { + return + } + if !exist { + if _, err = sess.Insert(&EmailAddress{ + UID: user.ID, + Email: user.Email, + LowerEmail: strings.ToLower(user.Email), + IsActivated: user.IsActive, + IsPrimary: true, + }); err != nil { + return + } + } else { + if _, err = sess.Where("email=?", user.Email).Cols("is_primary").Update(&EmailAddress{ + IsPrimary: true, + }); err != nil { + return + } + } + } + } + + return nil +} diff --git a/models/migrations/v181_test.go b/models/migrations/v181_test.go new file mode 100644 index 0000000000000..b392a9b71d7b0 --- /dev/null +++ b/models/migrations/v181_test.go @@ -0,0 +1,54 @@ +// 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 migrations + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_addPrimaryEmail2EmailAddress(t *testing.T) { + type User struct { + ID int64 + Email string + IsActive bool + } + + // Prepare and load the testing database + x, deferable := prepareTestEnv(t, 0, new(User)) + if x == nil || t.Failed() { + defer deferable() + return + } + defer deferable() + + err := addPrimaryEmail2EmailAddress(x) + assert.NoError(t, err) + + type EmailAddress struct { + ID int64 `xorm:"pk autoincr"` + UID int64 `xorm:"INDEX NOT NULL"` + Email string `xorm:"UNIQUE NOT NULL"` + LowerEmail string `xorm:"UNIQUE NOT NULL"` + IsActivated bool + IsPrimary bool `xorm:"DEFAULT(false) NOT NULL"` + } + + var users = make([]User, 0, 20) + err = x.Find(&users) + assert.NoError(t, err) + + for _, user := range users { + var emailAddress EmailAddress + has, err := x.Where("lower_email=?", strings.ToLower(user.Email)).Get(&emailAddress) + assert.NoError(t, err) + assert.True(t, has) + assert.True(t, emailAddress.IsPrimary) + assert.EqualValues(t, user.IsActive, emailAddress.IsActivated) + assert.EqualValues(t, user.ID, emailAddress.UID) + } +} diff --git a/models/migrations/v182.go b/models/migrations/v182.go new file mode 100644 index 0000000000000..dd9a04f27e725 --- /dev/null +++ b/models/migrations/v182.go @@ -0,0 +1,42 @@ +// 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 migrations + +import ( + "xorm.io/xorm" +) + +func addIssueResourceIndexTable(x *xorm.Engine) error { + type ResourceIndex struct { + GroupID int64 `xorm:"index unique(s)"` + MaxIndex int64 `xorm:"index unique(s)"` + } + + sess := x.NewSession() + defer sess.Close() + + if err := sess.Begin(); err != nil { + return err + } + + if err := sess.Table("issue_index").Sync2(new(ResourceIndex)); err != nil { + return err + } + + // Remove data we're goint to rebuild + if _, err := sess.Table("issue_index").Where("1=1").Delete(&ResourceIndex{}); err != nil { + return err + } + + // Create current data for all repositories with issues and PRs + if _, err := sess.Exec("INSERT INTO issue_index (group_id, max_index) " + + "SELECT max_data.repo_id, max_data.max_index " + + "FROM ( SELECT issue.repo_id AS repo_id, max(issue.`index`) AS max_index " + + "FROM issue GROUP BY issue.repo_id) AS max_data"); err != nil { + return err + } + + return sess.Commit() +} diff --git a/models/migrations/v182_test.go b/models/migrations/v182_test.go new file mode 100644 index 0000000000000..6f418f7794613 --- /dev/null +++ b/models/migrations/v182_test.go @@ -0,0 +1,59 @@ +// 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 migrations + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_addIssueResourceIndexTable(t *testing.T) { + // Create the models used in the migration + type Issue struct { + ID int64 `xorm:"pk autoincr"` + RepoID int64 `xorm:"UNIQUE(s)"` + Index int64 `xorm:"UNIQUE(s)"` + } + + // Prepare and load the testing database + x, deferable := prepareTestEnv(t, 0, new(Issue)) + if x == nil || t.Failed() { + defer deferable() + return + } + defer deferable() + + // Run the migration + if err := addIssueResourceIndexTable(x); err != nil { + assert.NoError(t, err) + return + } + + type ResourceIndex struct { + GroupID int64 `xorm:"index unique(s)"` + MaxIndex int64 `xorm:"index unique(s)"` + } + + var start = 0 + const batchSize = 1000 + for { + var indexes = make([]ResourceIndex, 0, batchSize) + err := x.Table("issue_index").Limit(batchSize, start).Find(&indexes) + assert.NoError(t, err) + + for _, idx := range indexes { + var maxIndex int + has, err := x.SQL("SELECT max(`index`) FROM issue WHERE repo_id = ?", idx.GroupID).Get(&maxIndex) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, maxIndex, idx.MaxIndex) + } + if len(indexes) < batchSize { + break + } + start += len(indexes) + } +} diff --git a/models/migrations/v183.go b/models/migrations/v183.go new file mode 100644 index 0000000000000..cc752bf827c1e --- /dev/null +++ b/models/migrations/v183.go @@ -0,0 +1,39 @@ +// 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 migrations + +import ( + "fmt" + "time" + + "code.gitea.io/gitea/modules/timeutil" + + "xorm.io/xorm" +) + +func createPushMirrorTable(x *xorm.Engine) error { + type PushMirror struct { + ID int64 `xorm:"pk autoincr"` + RepoID int64 `xorm:"INDEX"` + RemoteName string + + Interval time.Duration + CreatedUnix timeutil.TimeStamp `xorm:"created"` + LastUpdateUnix timeutil.TimeStamp `xorm:"INDEX last_update"` + LastError string `xorm:"text"` + } + + sess := x.NewSession() + defer sess.Close() + if err := sess.Begin(); err != nil { + return err + } + + if err := sess.Sync2(new(PushMirror)); err != nil { + return fmt.Errorf("Sync2: %v", err) + } + + return sess.Commit() +} diff --git a/models/migrations/v184.go b/models/migrations/v184.go new file mode 100644 index 0000000000000..b7be342b871c4 --- /dev/null +++ b/models/migrations/v184.go @@ -0,0 +1,47 @@ +// 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 migrations + +import ( + "fmt" + + "code.gitea.io/gitea/modules/setting" + + "xorm.io/xorm" +) + +func renameTaskErrorsToMessage(x *xorm.Engine) error { + type Task struct { + Errors string `xorm:"TEXT"` // if task failed, saved the error reason + Type int + Status int `xorm:"index"` + } + + sess := x.NewSession() + defer sess.Close() + if err := sess.Begin(); err != nil { + return err + } + + if err := sess.Sync2(new(Task)); err != nil { + return fmt.Errorf("error on Sync2: %v", err) + } + + switch { + case setting.Database.UseMySQL: + if _, err := sess.Exec("ALTER TABLE `task` CHANGE errors message text"); err != nil { + return err + } + case setting.Database.UseMSSQL: + if _, err := sess.Exec("sp_rename 'task.errors', 'message', 'COLUMN'"); err != nil { + return err + } + default: + if _, err := sess.Exec("ALTER TABLE `task` RENAME COLUMN errors TO message"); err != nil { + return err + } + } + return sess.Commit() +} diff --git a/models/models.go b/models/models.go index b0a9062566ee3..c325fd381159d 100644 --- a/models/models.go +++ b/models/models.go @@ -134,6 +134,8 @@ func init() { new(ProjectIssue), new(Session), new(RepoTransfer), + new(IssueIndex), + new(PushMirror), ) gonicNames := []string{"SSL", "UID"} @@ -171,6 +173,10 @@ func GetNewEngine() (*xorm.Engine, error) { return engine, nil } +func syncTables() error { + return x.StoreEngine("InnoDB").Sync2(tables...) +} + // NewTestEngine sets a new test xorm.Engine func NewTestEngine() (err error) { x, err = GetNewEngine() @@ -181,7 +187,7 @@ func NewTestEngine() (err error) { x.SetMapper(names.GonicMapper{}) x.SetLogger(NewXORMLogger(!setting.IsProd())) x.ShowSQL(!setting.IsProd()) - return x.StoreEngine("InnoDB").Sync2(tables...) + return syncTables() } // SetEngine sets the xorm.Engine @@ -222,7 +228,7 @@ func NewEngine(ctx context.Context, migrateFunc func(*xorm.Engine) error) (err e return fmt.Errorf("migrate: %v", err) } - if err = x.StoreEngine("InnoDB").Sync2(tables...); err != nil { + if err = syncTables(); err != nil { return fmt.Errorf("sync database struct error: %v", err) } diff --git a/models/notification.go b/models/notification.go index 51ab5898090f5..c4c7728ad9f6b 100644 --- a/models/notification.go +++ b/models/notification.go @@ -74,6 +74,7 @@ type FindNotificationOptions struct { RepoID int64 IssueID int64 Status []NotificationStatus + Source []NotificationSource UpdatedAfterUnix int64 UpdatedBeforeUnix int64 } @@ -93,6 +94,9 @@ func (opts *FindNotificationOptions) ToCond() builder.Cond { if len(opts.Status) > 0 { cond = cond.And(builder.In("notification.status", opts.Status)) } + if len(opts.Source) > 0 { + cond = cond.And(builder.In("notification.source", opts.Source)) + } if opts.UpdatedAfterUnix != 0 { cond = cond.And(builder.Gte{"notification.updated_unix": opts.UpdatedAfterUnix}) } @@ -111,13 +115,13 @@ func (opts *FindNotificationOptions) ToSession(e Engine) *xorm.Session { return sess } -func getNotifications(e Engine, options FindNotificationOptions) (nl NotificationList, err error) { +func getNotifications(e Engine, options *FindNotificationOptions) (nl NotificationList, err error) { err = options.ToSession(e).OrderBy("notification.updated_unix DESC").Find(&nl) return } // GetNotifications returns all notifications that fit to the given options. -func GetNotifications(opts FindNotificationOptions) (NotificationList, error) { +func GetNotifications(opts *FindNotificationOptions) (NotificationList, error) { return getNotifications(x, opts) } diff --git a/models/oauth2.go b/models/oauth2.go index cc9de74f84ebc..46da60e02dd65 100644 --- a/models/oauth2.go +++ b/models/oauth2.go @@ -132,6 +132,9 @@ func GetActiveOAuth2Providers() ([]string, map[string]OAuth2Provider, error) { // InitOAuth2 initialize the OAuth2 lib and register all active OAuth2 providers in the library func InitOAuth2() error { + if err := oauth2.InitSigningKey(); err != nil { + return err + } if err := oauth2.Init(x); err != nil { return err } diff --git a/models/oauth2_application.go b/models/oauth2_application.go index 679fdb18f957d..3509dba54e2f8 100644 --- a/models/oauth2_application.go +++ b/models/oauth2_application.go @@ -12,8 +12,8 @@ import ( "strings" "time" + "code.gitea.io/gitea/modules/auth/oauth2" "code.gitea.io/gitea/modules/secret" - "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" @@ -394,7 +394,7 @@ func (grant *OAuth2Grant) TableName() string { return "oauth2_grant" } -// GenerateNewAuthorizationCode generates a new authorization code for a grant and saves it to the databse +// GenerateNewAuthorizationCode generates a new authorization code for a grant and saves it to the database func (grant *OAuth2Grant) GenerateNewAuthorizationCode(redirectURI, codeChallenge, codeChallengeMethod string) (*OAuth2AuthorizationCode, error) { return grant.generateNewAuthorizationCode(x, redirectURI, codeChallenge, codeChallengeMethod) } @@ -540,10 +540,10 @@ type OAuth2Token struct { // ParseOAuth2Token parses a singed jwt string func ParseOAuth2Token(jwtToken string) (*OAuth2Token, error) { parsedToken, err := jwt.ParseWithClaims(jwtToken, &OAuth2Token{}, func(token *jwt.Token) (interface{}, error) { - if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + if token.Method == nil || token.Method.Alg() != oauth2.DefaultSigningKey.SigningMethod().Alg() { return nil, fmt.Errorf("unexpected signing algo: %v", token.Header["alg"]) } - return setting.OAuth2.JWTSecretBytes, nil + return oauth2.DefaultSigningKey.VerifyKey(), nil }) if err != nil { return nil, err @@ -559,19 +559,34 @@ func ParseOAuth2Token(jwtToken string) (*OAuth2Token, error) { // SignToken signs the token with the JWT secret func (token *OAuth2Token) SignToken() (string, error) { token.IssuedAt = time.Now().Unix() - jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS512, token) - return jwtToken.SignedString(setting.OAuth2.JWTSecretBytes) + jwtToken := jwt.NewWithClaims(oauth2.DefaultSigningKey.SigningMethod(), token) + oauth2.DefaultSigningKey.PreProcessToken(jwtToken) + return jwtToken.SignedString(oauth2.DefaultSigningKey.SignKey()) } // OIDCToken represents an OpenID Connect id_token type OIDCToken struct { jwt.StandardClaims Nonce string `json:"nonce,omitempty"` + + // Scope profile + Name string `json:"name,omitempty"` + PreferredUsername string `json:"preferred_username,omitempty"` + Profile string `json:"profile,omitempty"` + Picture string `json:"picture,omitempty"` + Website string `json:"website,omitempty"` + Locale string `json:"locale,omitempty"` + UpdatedAt timeutil.TimeStamp `json:"updated_at,omitempty"` + + // Scope email + Email string `json:"email,omitempty"` + EmailVerified bool `json:"email_verified,omitempty"` } // SignToken signs an id_token with the (symmetric) client secret key -func (token *OIDCToken) SignToken(clientSecret string) (string, error) { +func (token *OIDCToken) SignToken(signingKey oauth2.JWTSigningKey) (string, error) { token.IssuedAt = time.Now().Unix() - jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, token) - return jwtToken.SignedString([]byte(clientSecret)) + jwtToken := jwt.NewWithClaims(signingKey.SigningMethod(), token) + signingKey.PreProcessToken(jwtToken) + return jwtToken.SignedString(signingKey.SignKey()) } diff --git a/models/oauth2_application_test.go b/models/oauth2_application_test.go index 511d0194658a1..7a4bce85c005b 100644 --- a/models/oauth2_application_test.go +++ b/models/oauth2_application_test.go @@ -219,11 +219,11 @@ func TestOAuth2AuthorizationCode_GenerateRedirectURI(t *testing.T) { redirect, err := code.GenerateRedirectURI("thestate") assert.NoError(t, err) - assert.Equal(t, redirect.String(), "https://example.com/callback?code=thecode&state=thestate") + assert.Equal(t, "https://example.com/callback?code=thecode&state=thestate", redirect.String()) redirect, err = code.GenerateRedirectURI("") assert.NoError(t, err) - assert.Equal(t, redirect.String(), "https://example.com/callback?code=thecode") + assert.Equal(t, "https://example.com/callback?code=thecode", redirect.String()) } func TestOAuth2AuthorizationCode_Invalidate(t *testing.T) { diff --git a/models/org.go b/models/org.go index 3474988efc9cf..7f9e3cce5b624 100644 --- a/models/org.go +++ b/models/org.go @@ -425,6 +425,25 @@ func GetOrgsByUserID(userID int64, showAll bool) ([]*User, error) { return getOrgsByUserID(sess, userID, showAll) } +// queryUserOrgIDs returns a condition to return user's organization id +func queryUserOrgIDs(uid int64) *builder.Builder { + return builder.Select("team.org_id"). + From("team_user").InnerJoin("team", "team.id = team_user.team_id"). + Where(builder.Eq{"team_user.uid": uid}) +} + +// MinimalOrg represents a simple orgnization with only needed columns +type MinimalOrg = User + +// GetUserOrgsList returns one user's all orgs list +func GetUserOrgsList(uid int64) ([]*MinimalOrg, error) { + var orgs = make([]*MinimalOrg, 0, 20) + return orgs, x.Select("id, name, full_name, visibility, avatar, avatar_email, use_custom_avatar"). + Table("user"). + In("id", queryUserOrgIDs(uid)). + Find(&orgs) +} + func getOwnedOrgsByUserID(sess *xorm.Session, userID int64) ([]*User, error) { orgs := make([]*User, 0, 10) return orgs, sess. diff --git a/models/org_test.go b/models/org_test.go index 66979714c13eb..bed7a6eb8632f 100644 --- a/models/org_test.go +++ b/models/org_test.go @@ -453,7 +453,7 @@ func TestAddOrgUser(t *testing.T) { assert.NoError(t, AddOrgUser(orgID, userID)) ou := &OrgUser{OrgID: orgID, UID: userID} AssertExistsAndLoadBean(t, ou) - assert.Equal(t, ou.IsPublic, isPublic) + assert.Equal(t, isPublic, ou.IsPublic) org = AssertExistsAndLoadBean(t, &User{ID: orgID}).(*User) assert.EqualValues(t, expectedNumMembers, org.NumMembers) } @@ -589,9 +589,9 @@ func TestHasOrgVisibleTypePublic(t *testing.T) { test1 := HasOrgVisible(org, owner) test2 := HasOrgVisible(org, user3) test3 := HasOrgVisible(org, nil) - assert.Equal(t, test1, true) // owner of org - assert.Equal(t, test2, true) // user not a part of org - assert.Equal(t, test3, true) // logged out user + assert.True(t, test1) // owner of org + assert.True(t, test2) // user not a part of org + assert.True(t, test3) // logged out user } func TestHasOrgVisibleTypeLimited(t *testing.T) { @@ -612,9 +612,9 @@ func TestHasOrgVisibleTypeLimited(t *testing.T) { test1 := HasOrgVisible(org, owner) test2 := HasOrgVisible(org, user3) test3 := HasOrgVisible(org, nil) - assert.Equal(t, test1, true) // owner of org - assert.Equal(t, test2, true) // user not a part of org - assert.Equal(t, test3, false) // logged out user + assert.True(t, test1) // owner of org + assert.True(t, test2) // user not a part of org + assert.False(t, test3) // logged out user } func TestHasOrgVisibleTypePrivate(t *testing.T) { @@ -635,9 +635,9 @@ func TestHasOrgVisibleTypePrivate(t *testing.T) { test1 := HasOrgVisible(org, owner) test2 := HasOrgVisible(org, user3) test3 := HasOrgVisible(org, nil) - assert.Equal(t, test1, true) // owner of org - assert.Equal(t, test2, false) // user not a part of org - assert.Equal(t, test3, false) // logged out user + assert.True(t, test1) // owner of org + assert.False(t, test2) // user not a part of org + assert.False(t, test3) // logged out user } func TestGetUsersWhoCanCreateOrgRepo(t *testing.T) { diff --git a/models/pull.go b/models/pull.go index c58349602cdc5..3717878f4201f 100644 --- a/models/pull.go +++ b/models/pull.go @@ -427,34 +427,23 @@ func (pr *PullRequest) SetMerged() (bool, error) { } // NewPullRequest creates new pull request with labels for repository. -func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []string, pr *PullRequest) (err error) { - // Retry several times in case INSERT fails due to duplicate key for (repo_id, index); see #7887 - i := 0 - for { - if err = newPullRequestAttempt(repo, pull, labelIDs, uuids, pr); err == nil { - return nil - } - if !IsErrNewIssueInsert(err) { - return err - } - if i++; i == issueMaxDupIndexAttempts { - break - } - log.Error("NewPullRequest: error attempting to insert the new issue; will retry. Original error: %v", err) +func NewPullRequest(repo *Repository, issue *Issue, labelIDs []int64, uuids []string, pr *PullRequest) (err error) { + idx, err := GetNextResourceIndex("issue_index", repo.ID) + if err != nil { + return fmt.Errorf("generate issue index failed: %v", err) } - return fmt.Errorf("NewPullRequest: too many errors attempting to insert the new issue. Last error was: %v", err) -} -func newPullRequestAttempt(repo *Repository, pull *Issue, labelIDs []int64, uuids []string, pr *PullRequest) (err error) { + issue.Index = idx + sess := x.NewSession() defer sess.Close() if err = sess.Begin(); err != nil { return err } - if err = newIssue(sess, pull.Poster, NewIssueOptions{ + if err = newIssue(sess, issue.Poster, NewIssueOptions{ Repo: repo, - Issue: pull, + Issue: issue, LabelIDs: labelIDs, Attachments: uuids, IsPull: true, @@ -465,10 +454,9 @@ func newPullRequestAttempt(repo *Repository, pull *Issue, labelIDs []int64, uuid return fmt.Errorf("newIssue: %v", err) } - pr.Index = pull.Index + pr.Index = issue.Index pr.BaseRepo = repo - - pr.IssueID = pull.ID + pr.IssueID = issue.ID if _, err = sess.Insert(pr); err != nil { return fmt.Errorf("insert pull repo: %v", err) } diff --git a/models/release.go b/models/release.go index 13b8f17218c60..1ce88a8210c9d 100644 --- a/models/release.go +++ b/models/release.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/util" "xorm.io/builder" ) @@ -173,6 +174,8 @@ type FindReleasesOptions struct { ListOptions IncludeDrafts bool IncludeTags bool + IsPreRelease util.OptionalBool + IsDraft util.OptionalBool TagNames []string } @@ -189,6 +192,12 @@ func (opts *FindReleasesOptions) toConds(repoID int64) builder.Cond { if len(opts.TagNames) > 0 { cond = cond.And(builder.In("tag_name", opts.TagNames)) } + if !opts.IsPreRelease.IsNone() { + cond = cond.And(builder.Eq{"is_prerelease": opts.IsPreRelease.IsTrue()}) + } + if !opts.IsDraft.IsNone() { + cond = cond.And(builder.Eq{"is_draft": opts.IsDraft.IsTrue()}) + } return cond } @@ -206,6 +215,11 @@ func GetReleasesByRepoID(repoID int64, opts FindReleasesOptions) ([]*Release, er return rels, sess.Find(&rels) } +// CountReleasesByRepoID returns a number of releases matching FindReleaseOptions and RepoID. +func CountReleasesByRepoID(repoID int64, opts FindReleasesOptions) (int64, error) { + return x.Where(opts.toConds(repoID)).Count(new(Release)) +} + // GetLatestReleaseByRepoID returns the latest release for a repository func GetLatestReleaseByRepoID(repoID int64) (*Release, error) { cond := builder.NewCond(). diff --git a/models/repo.go b/models/repo.go index 58a393ae708e7..dc4e03a28a11b 100644 --- a/models/repo.go +++ b/models/repo.go @@ -216,12 +216,13 @@ type Repository struct { NumClosedProjects int `xorm:"NOT NULL DEFAULT 0"` NumOpenProjects int `xorm:"-"` - IsPrivate bool `xorm:"INDEX"` - IsEmpty bool `xorm:"INDEX"` - IsArchived bool `xorm:"INDEX"` - IsMirror bool `xorm:"INDEX"` - *Mirror `xorm:"-"` - Status RepositoryStatus `xorm:"NOT NULL DEFAULT 0"` + IsPrivate bool `xorm:"INDEX"` + IsEmpty bool `xorm:"INDEX"` + IsArchived bool `xorm:"INDEX"` + IsMirror bool `xorm:"INDEX"` + *Mirror `xorm:"-"` + PushMirrors []*PushMirror `xorm:"-"` + Status RepositoryStatus `xorm:"NOT NULL DEFAULT 0"` RenderingMetas map[string]string `xorm:"-"` DocumentRenderingMetas map[string]string `xorm:"-"` @@ -255,7 +256,12 @@ func (repo *Repository) SanitizedOriginalURL() string { if repo.OriginalURL == "" { return "" } - return util.SanitizeURLCredentials(repo.OriginalURL, false) + u, err := url.Parse(repo.OriginalURL) + if err != nil { + return "" + } + u.User = nil + return u.String() } // ColorFormat returns a colored string to represent this repo @@ -657,6 +663,12 @@ func (repo *Repository) GetMirror() (err error) { return err } +// LoadPushMirrors populates the repository push mirrors. +func (repo *Repository) LoadPushMirrors() (err error) { + repo.PushMirrors, err = GetPushMirrorsByRepoID(repo.ID) + return err +} + // GetBaseRepo populates repo.BaseRepo for a fork repository and // returns an error on failure (NOTE: no error is returned for // non-fork repositories, and BaseRepo will be left untouched) @@ -1487,6 +1499,7 @@ func DeleteRepository(doer *User, uid, repoID int64) error { &Notification{RepoID: repoID}, &ProtectedBranch{RepoID: repoID}, &PullRequest{BaseRepoID: repoID}, + &PushMirror{RepoID: repoID}, &Release{RepoID: repoID}, &RepoIndexerStatus{RepoID: repoID}, &RepoRedirect{RedirectRepoID: repoID}, @@ -1510,6 +1523,11 @@ func DeleteRepository(doer *User, uid, repoID int64) error { return err } + // Delete issue index + if err := deleteResouceIndex(sess, "issue_index", repoID); err != nil { + return err + } + if repo.IsFork { if _, err := sess.Exec("UPDATE `repository` SET num_forks=num_forks-1 WHERE id=?", repo.ForkID); err != nil { return fmt.Errorf("decrease fork count: %v", err) diff --git a/models/repo_generate_test.go b/models/repo_generate_test.go index 53ab4fcd3d559..e7a93433a7be3 100644 --- a/models/repo_generate_test.go +++ b/models/repo_generate_test.go @@ -25,7 +25,7 @@ text/*.txt func TestGiteaTemplate(t *testing.T) { gt := GiteaTemplate{Content: giteaTemplate} - assert.Equal(t, len(gt.Globs()), 3) + assert.Len(t, gt.Globs(), 3) tt := []struct { Path string diff --git a/models/repo_mirror.go b/models/repo_mirror.go index 2c37b54aa99ba..cd1f74cb24691 100644 --- a/models/repo_mirror.go +++ b/models/repo_mirror.go @@ -14,6 +14,12 @@ import ( "xorm.io/xorm" ) +// RemoteMirrorer defines base methods for pull/push mirrors. +type RemoteMirrorer interface { + GetRepository() *Repository + GetRemoteName() string +} + // Mirror represents mirror information of a repository. type Mirror struct { ID int64 `xorm:"pk autoincr"` @@ -52,6 +58,16 @@ func (m *Mirror) AfterLoad(session *xorm.Session) { } } +// GetRepository returns the repository. +func (m *Mirror) GetRepository() *Repository { + return m.Repo +} + +// GetRemoteName returns the name of the remote. +func (m *Mirror) GetRemoteName() string { + return "origin" +} + // ScheduleNextUpdate calculates and sets next update time. func (m *Mirror) ScheduleNextUpdate() { if m.Interval != 0 { diff --git a/models/repo_pushmirror.go b/models/repo_pushmirror.go new file mode 100644 index 0000000000000..bdd4198f92b4a --- /dev/null +++ b/models/repo_pushmirror.go @@ -0,0 +1,106 @@ +// 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 models + +import ( + "errors" + "time" + + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/timeutil" + + "xorm.io/xorm" +) + +var ( + // ErrPushMirrorNotExist mirror does not exist error + ErrPushMirrorNotExist = errors.New("PushMirror does not exist") +) + +// PushMirror represents mirror information of a repository. +type PushMirror struct { + ID int64 `xorm:"pk autoincr"` + RepoID int64 `xorm:"INDEX"` + Repo *Repository `xorm:"-"` + RemoteName string + + Interval time.Duration + CreatedUnix timeutil.TimeStamp `xorm:"created"` + LastUpdateUnix timeutil.TimeStamp `xorm:"INDEX last_update"` + LastError string `xorm:"text"` +} + +// AfterLoad is invoked from XORM after setting the values of all fields of this object. +func (m *PushMirror) AfterLoad(session *xorm.Session) { + if m == nil { + return + } + + var err error + m.Repo, err = getRepositoryByID(session, m.RepoID) + if err != nil { + log.Error("getRepositoryByID[%d]: %v", m.ID, err) + } +} + +// GetRepository returns the path of the repository. +func (m *PushMirror) GetRepository() *Repository { + return m.Repo +} + +// GetRemoteName returns the name of the remote. +func (m *PushMirror) GetRemoteName() string { + return m.RemoteName +} + +// InsertPushMirror inserts a push-mirror to database +func InsertPushMirror(m *PushMirror) error { + _, err := x.Insert(m) + return err +} + +// UpdatePushMirror updates the push-mirror +func UpdatePushMirror(m *PushMirror) error { + _, err := x.ID(m.ID).AllCols().Update(m) + return err +} + +// DeletePushMirrorByID deletes a push-mirrors by ID +func DeletePushMirrorByID(ID int64) error { + _, err := x.ID(ID).Delete(&PushMirror{}) + return err +} + +// DeletePushMirrorsByRepoID deletes all push-mirrors by repoID +func DeletePushMirrorsByRepoID(repoID int64) error { + _, err := x.Delete(&PushMirror{RepoID: repoID}) + return err +} + +// GetPushMirrorByID returns push-mirror information. +func GetPushMirrorByID(ID int64) (*PushMirror, error) { + m := &PushMirror{} + has, err := x.ID(ID).Get(m) + if err != nil { + return nil, err + } else if !has { + return nil, ErrPushMirrorNotExist + } + return m, nil +} + +// GetPushMirrorsByRepoID returns push-mirror informations of a repository. +func GetPushMirrorsByRepoID(repoID int64) ([]*PushMirror, error) { + mirrors := make([]*PushMirror, 0, 10) + return mirrors, x.Where("repo_id=?", repoID).Find(&mirrors) +} + +// PushMirrorsIterate iterates all push-mirror repositories. +func PushMirrorsIterate(f func(idx int, bean interface{}) error) error { + return x. + Where("last_update + (`interval` / ?) <= ?", time.Second, time.Now().Unix()). + And("`interval` != 0"). + Iterate(new(PushMirror), f) +} diff --git a/models/repo_pushmirror_test.go b/models/repo_pushmirror_test.go new file mode 100644 index 0000000000000..66c499b1c359d --- /dev/null +++ b/models/repo_pushmirror_test.go @@ -0,0 +1,49 @@ +// 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 models + +import ( + "testing" + "time" + + "code.gitea.io/gitea/modules/timeutil" + + "github.com/stretchr/testify/assert" +) + +func TestPushMirrorsIterate(t *testing.T) { + assert.NoError(t, PrepareTestDatabase()) + + now := timeutil.TimeStampNow() + + InsertPushMirror(&PushMirror{ + RemoteName: "test-1", + LastUpdateUnix: now, + Interval: 1, + }) + + long, _ := time.ParseDuration("24h") + InsertPushMirror(&PushMirror{ + RemoteName: "test-2", + LastUpdateUnix: now, + Interval: long, + }) + + InsertPushMirror(&PushMirror{ + RemoteName: "test-3", + LastUpdateUnix: now, + Interval: 0, + }) + + time.Sleep(1 * time.Millisecond) + + PushMirrorsIterate(func(idx int, bean interface{}) error { + m, ok := bean.(*PushMirror) + assert.True(t, ok) + assert.Equal(t, "test-1", m.RemoteName) + assert.Equal(t, m.RemoteName, m.GetRemoteName()) + return nil + }) +} diff --git a/models/repo_test.go b/models/repo_test.go index 10ba2c99f8973..28eb9baa17db8 100644 --- a/models/repo_test.go +++ b/models/repo_test.go @@ -61,8 +61,8 @@ func TestMetas(t *testing.T) { metas = repo.ComposeMetas() assert.Contains(t, metas, "org") assert.Contains(t, metas, "teams") - assert.Equal(t, metas["org"], "user3") - assert.Equal(t, metas["teams"], ",owners,team1,") + assert.Equal(t, "user3", metas["org"]) + assert.Equal(t, ",owners,team1,", metas["teams"]) } func TestGetRepositoryCount(t *testing.T) { @@ -111,7 +111,7 @@ func TestUpdateRepositoryVisibilityChanged(t *testing.T) { _, err = x.ID(3).Get(&act) assert.NoError(t, err) - assert.Equal(t, true, act.IsPrivate) + assert.True(t, act.IsPrivate) } func TestGetUserFork(t *testing.T) { @@ -199,13 +199,13 @@ func TestRepoGetReviewers(t *testing.T) { reviewers, err := repo1.GetReviewers(2, 2) assert.NoError(t, err) - assert.Equal(t, 4, len(reviewers)) + assert.Len(t, reviewers, 4) // test private repo repo2 := AssertExistsAndLoadBean(t, &Repository{ID: 2}).(*Repository) reviewers, err = repo2.GetReviewers(2, 2) assert.NoError(t, err) - assert.Equal(t, 0, len(reviewers)) + assert.Empty(t, reviewers) } func TestRepoGetReviewerTeams(t *testing.T) { @@ -219,5 +219,5 @@ func TestRepoGetReviewerTeams(t *testing.T) { repo3 := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository) teams, err = repo3.GetReviewerTeams() assert.NoError(t, err) - assert.Equal(t, 2, len(teams)) + assert.Len(t, teams, 2) } diff --git a/models/review.go b/models/review.go index 343621c0fa5ab..316cbe4da6426 100644 --- a/models/review.go +++ b/models/review.go @@ -347,7 +347,7 @@ func IsContentEmptyErr(err error) bool { } // SubmitReview creates a review out of the existing pending review or creates a new one if no pending review exist -func SubmitReview(doer *User, issue *Issue, reviewType ReviewType, content, commitID string, stale bool) (*Review, *Comment, error) { +func SubmitReview(doer *User, issue *Issue, reviewType ReviewType, content, commitID string, stale bool, attachmentUUIDs []string) (*Review, *Comment, error) { sess := x.NewSession() defer sess.Close() if err := sess.Begin(); err != nil { @@ -419,12 +419,13 @@ func SubmitReview(doer *User, issue *Issue, reviewType ReviewType, content, comm } comm, err := createComment(sess, &CreateCommentOptions{ - Type: CommentTypeReview, - Doer: doer, - Content: review.Content, - Issue: issue, - Repo: issue.Repo, - ReviewID: review.ID, + Type: CommentTypeReview, + Doer: doer, + Content: review.Content, + Issue: issue, + Repo: issue.Repo, + ReviewID: review.ID, + Attachments: attachmentUUIDs, }) if err != nil || comm == nil { return nil, nil, err diff --git a/models/ssh_key.go b/models/ssh_key.go index 9f9c33e848f5f..e35fc12e080a2 100644 --- a/models/ssh_key.go +++ b/models/ssh_key.go @@ -38,7 +38,6 @@ import ( const ( tplCommentPrefix = `# gitea public key` - tplCommand = "%s --config=%s serv key-%d" tplPublicKey = tplCommentPrefix + "\n" + `command=%s,no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty %s` + "\n" authorizedPrincipalsFile = "authorized_principals" @@ -88,7 +87,16 @@ func (key *PublicKey) OmitEmail() string { // AuthorizedString returns formatted public key string for authorized_keys file. func (key *PublicKey) AuthorizedString() string { - return fmt.Sprintf(tplPublicKey, util.ShellEscape(fmt.Sprintf(tplCommand, util.ShellEscape(setting.AppPath), util.ShellEscape(setting.CustomConf), key.ID)), key.Content) + sb := &strings.Builder{} + _ = setting.SSH.AuthorizedKeysCommandTemplateTemplate.Execute(sb, map[string]interface{}{ + "AppPath": util.ShellEscape(setting.AppPath), + "AppWorkPath": util.ShellEscape(setting.AppWorkPath), + "CustomConf": util.ShellEscape(setting.CustomConf), + "CustomPath": util.ShellEscape(setting.CustomPath), + "Key": key, + }) + + return fmt.Sprintf(tplPublicKey, util.ShellEscape(sb.String()), key.Content) } func extractTypeFromBase64Key(key string) (string, error) { diff --git a/models/task.go b/models/task.go index a4ab65b5e5e17..5f9ccc6bfaf5f 100644 --- a/models/task.go +++ b/models/task.go @@ -32,10 +32,16 @@ type Task struct { StartTime timeutil.TimeStamp EndTime timeutil.TimeStamp PayloadContent string `xorm:"TEXT"` - Errors string `xorm:"TEXT"` // if task failed, saved the error reason + Message string `xorm:"TEXT"` // if task failed, saved the error reason Created timeutil.TimeStamp `xorm:"created"` } +// TranslatableMessage represents JSON struct that can be translated with a Locale +type TranslatableMessage struct { + Format string + Args []interface{} `json:"omitempty"` +} + // LoadRepo loads repository of the task func (task *Task) LoadRepo() error { return task.loadRepo(x) @@ -234,7 +240,7 @@ func FinishMigrateTask(task *Task) error { } conf.AuthPassword = "" conf.AuthToken = "" - conf.CloneAddr = util.SanitizeURLCredentials(conf.CloneAddr, true) + conf.CloneAddr = util.NewStringURLSanitizer(conf.CloneAddr, true).Replace(conf.CloneAddr) conf.AuthPasswordEncrypted = "" conf.AuthTokenEncrypted = "" conf.CloneAddrEncrypted = "" diff --git a/models/token.go b/models/token.go index 4737dddda3431..357afe44a7c0b 100644 --- a/models/token.go +++ b/models/token.go @@ -57,9 +57,15 @@ func GetAccessTokenBySHA(token string) (*AccessToken, error) { if token == "" { return nil, ErrAccessTokenEmpty{} } - if len(token) < 8 { + // A token is defined as being SHA1 sum these are 40 hexadecimal bytes long + if len(token) != 40 { return nil, ErrAccessTokenNotExist{token} } + for _, x := range []byte(token) { + if x < '0' || (x > '9' && x < 'a') || x > 'f' { + return nil, ErrAccessTokenNotExist{token} + } + } var tokens []AccessToken lastEight := token[len(token)-8:] err := x.Table(&AccessToken{}).Where("token_last_eight = ?", lastEight).Find(&tokens) diff --git a/models/topic_test.go b/models/topic_test.go index b6ef8f565c2ae..25232eb981c24 100644 --- a/models/topic_test.go +++ b/models/topic_test.go @@ -19,31 +19,31 @@ func TestAddTopic(t *testing.T) { topics, err := FindTopics(&FindTopicOptions{}) assert.NoError(t, err) - assert.EqualValues(t, totalNrOfTopics, len(topics)) + assert.Len(t, topics, totalNrOfTopics) topics, err = FindTopics(&FindTopicOptions{ ListOptions: ListOptions{Page: 1, PageSize: 2}, }) assert.NoError(t, err) - assert.EqualValues(t, 2, len(topics)) + assert.Len(t, topics, 2) topics, err = FindTopics(&FindTopicOptions{ RepoID: 1, }) assert.NoError(t, err) - assert.EqualValues(t, repo1NrOfTopics, len(topics)) + assert.Len(t, topics, repo1NrOfTopics) assert.NoError(t, SaveTopics(2, "golang")) repo2NrOfTopics = 1 topics, err = FindTopics(&FindTopicOptions{}) assert.NoError(t, err) - assert.EqualValues(t, totalNrOfTopics, len(topics)) + assert.Len(t, topics, totalNrOfTopics) topics, err = FindTopics(&FindTopicOptions{ RepoID: 2, }) assert.NoError(t, err) - assert.EqualValues(t, repo2NrOfTopics, len(topics)) + assert.Len(t, topics, repo2NrOfTopics) assert.NoError(t, SaveTopics(2, "golang", "gitea")) repo2NrOfTopics = 2 @@ -54,13 +54,13 @@ func TestAddTopic(t *testing.T) { topics, err = FindTopics(&FindTopicOptions{}) assert.NoError(t, err) - assert.EqualValues(t, totalNrOfTopics, len(topics)) + assert.Len(t, topics, totalNrOfTopics) topics, err = FindTopics(&FindTopicOptions{ RepoID: 2, }) assert.NoError(t, err) - assert.EqualValues(t, repo2NrOfTopics, len(topics)) + assert.Len(t, topics, repo2NrOfTopics) } func TestTopicValidator(t *testing.T) { diff --git a/models/unit_tests.go b/models/unit_tests.go index cefdae2cd6ae3..5a145fa2c0238 100644 --- a/models/unit_tests.go +++ b/models/unit_tests.go @@ -103,7 +103,7 @@ func CreateTestEngine(fixturesDir string) error { return err } x.SetMapper(names.GonicMapper{}) - if err = x.StoreEngine("InnoDB").Sync2(tables...); err != nil { + if err = syncTables(); err != nil { return err } switch os.Getenv("GITEA_UNIT_TESTS_VERBOSE") { diff --git a/models/user.go b/models/user.go index c7b44bdb73216..5998341422197 100644 --- a/models/user.go +++ b/models/user.go @@ -74,9 +74,6 @@ const ( ) var ( - // ErrEmailNotExist e-mail does not exist error - ErrEmailNotExist = errors.New("E-mail does not exist") - // ErrEmailNotActivated e-mail address has not been activated error ErrEmailNotActivated = errors.New("E-mail address has not been activated") @@ -115,7 +112,6 @@ type User struct { LoginName string Type UserType OwnedOrgs []*User `xorm:"-"` - Orgs []*User `xorm:"-"` Repos []*Repository `xorm:"-"` Location string Website string @@ -606,58 +602,6 @@ func (u *User) GetOwnedOrganizations() (err error) { return err } -// GetOrganizations returns paginated organizations that user belongs to. -// TODO: does not respect All and show orgs you privately participate -func (u *User) GetOrganizations(opts *SearchOrganizationsOptions) error { - sess := x.NewSession() - defer sess.Close() - - schema, err := x.TableInfo(new(User)) - if err != nil { - return err - } - groupByCols := &strings.Builder{} - for _, col := range schema.Columns() { - fmt.Fprintf(groupByCols, "`%s`.%s,", schema.Name, col.Name) - } - groupByStr := groupByCols.String() - groupByStr = groupByStr[0 : len(groupByStr)-1] - - sess.Select("`user`.*, count(repo_id) as org_count"). - Table("user"). - Join("INNER", "org_user", "`org_user`.org_id=`user`.id"). - Join("LEFT", builder. - Select("id as repo_id, owner_id as repo_owner_id"). - From("repository"). - Where(accessibleRepositoryCondition(u)), "`repository`.repo_owner_id = `org_user`.org_id"). - And("`org_user`.uid=?", u.ID). - GroupBy(groupByStr) - if opts.PageSize != 0 { - sess = opts.setSessionPagination(sess) - } - type OrgCount struct { - User `xorm:"extends"` - OrgCount int - } - orgCounts := make([]*OrgCount, 0, 10) - - if err := sess. - Asc("`user`.name"). - Find(&orgCounts); err != nil { - return err - } - - orgs := make([]*User, len(orgCounts)) - for i, orgCount := range orgCounts { - orgCount.User.NumRepos = orgCount.OrgCount - orgs[i] = &orgCount.User - } - - u.Orgs = orgs - - return nil -} - // DisplayName returns full name if it's not empty, // returns username otherwise. func (u *User) DisplayName() string { @@ -876,15 +820,6 @@ func CreateUser(u *User) (err error) { } u.Email = strings.ToLower(u.Email) - isExist, err = sess. - Where("email=?", u.Email). - Get(new(User)) - if err != nil { - return err - } else if isExist { - return ErrEmailAlreadyUsed{u.Email} - } - if err = ValidateEmail(u.Email); err != nil { return err } @@ -915,6 +850,17 @@ func CreateUser(u *User) (err error) { return err } + // insert email address + if _, err := sess.Insert(&EmailAddress{ + UID: u.ID, + Email: u.Email, + LowerEmail: strings.ToLower(u.Email), + IsActivated: u.IsActive, + IsPrimary: true, + }); err != nil { + return err + } + return sess.Commit() } diff --git a/models/user_heatmap_test.go b/models/user_heatmap_test.go index cc08ac155159e..31e78a19cc1b1 100644 --- a/models/user_heatmap_test.go +++ b/models/user_heatmap_test.go @@ -52,8 +52,8 @@ func TestGetUserHeatmapDataByUser(t *testing.T) { // Get the heatmap and compare heatmap, err := GetUserHeatmapDataByUser(user, doer) assert.NoError(t, err) - assert.Equal(t, len(actions), len(heatmap), "invalid action count: did the test data became too old?") - assert.Equal(t, tc.CountResult, len(heatmap), fmt.Sprintf("testcase %d", i)) + assert.Len(t, heatmap, len(actions), "invalid action count: did the test data became too old?") + assert.Len(t, heatmap, tc.CountResult, fmt.Sprintf("testcase %d", i)) // Test JSON rendering json := jsoniter.ConfigCompatibleWithStandardLibrary diff --git a/models/user_mail.go b/models/user_mail.go index 1bdd6a423cc35..2758dfb8e84f3 100644 --- a/models/user_mail.go +++ b/models/user_mail.go @@ -17,14 +17,22 @@ import ( "xorm.io/builder" ) -// EmailAddress is the list of all email addresses of a user. Can contain the -// primary email address, but is not obligatory. +// EmailAddress is the list of all email addresses of a user. It also contains the +// primary email address which is saved in user table. type EmailAddress struct { ID int64 `xorm:"pk autoincr"` UID int64 `xorm:"INDEX NOT NULL"` Email string `xorm:"UNIQUE NOT NULL"` + LowerEmail string `xorm:"UNIQUE NOT NULL"` IsActivated bool - IsPrimary bool `xorm:"-"` + IsPrimary bool `xorm:"DEFAULT(false) NOT NULL"` +} + +// BeforeInsert will be invoked by XORM before inserting a record +func (email *EmailAddress) BeforeInsert() { + if email.LowerEmail == "" { + email.LowerEmail = strings.ToLower(email.Email) + } } // ValidateEmail check if email is a allowed address @@ -47,34 +55,10 @@ func GetEmailAddresses(uid int64) ([]*EmailAddress, error) { emails := make([]*EmailAddress, 0, 5) if err := x. Where("uid=?", uid). + Asc("id"). Find(&emails); err != nil { return nil, err } - - u, err := GetUserByID(uid) - if err != nil { - return nil, err - } - - isPrimaryFound := false - for _, email := range emails { - if email.Email == u.Email { - isPrimaryFound = true - email.IsPrimary = true - } else { - email.IsPrimary = false - } - } - - // We always want the primary email address displayed, even if it's not in - // the email address table (yet). - if !isPrimaryFound { - emails = append(emails, &EmailAddress{ - Email: u.Email, - IsActivated: u.IsActive, - IsPrimary: true, - }) - } return emails, nil } @@ -97,14 +81,13 @@ func isEmailActive(e Engine, email string, userID, emailID int64) (bool, error) // Can't filter by boolean field unless it's explicit cond := builder.NewCond() - cond = cond.And(builder.Eq{"email": email}, builder.Neq{"id": emailID}) + cond = cond.And(builder.Eq{"lower_email": strings.ToLower(email)}, builder.Neq{"id": emailID}) if setting.Service.RegisterEmailConfirm { // Inactive (unvalidated) addresses don't count as active if email validation is required cond = cond.And(builder.Eq{"is_activated": true}) } - em := EmailAddress{} - + var em EmailAddress if has, err := e.Where(cond).Get(&em); has || err != nil { if has { log.Info("isEmailActive('%s',%d,%d) found duplicate in email ID %d", email, userID, emailID, em.ID) @@ -112,22 +95,6 @@ func isEmailActive(e Engine, email string, userID, emailID int64) (bool, error) return has, err } - // Can't filter by boolean field unless it's explicit - cond = builder.NewCond() - cond = cond.And(builder.Eq{"email": email}, builder.Neq{"id": userID}) - if setting.Service.RegisterEmailConfirm { - cond = cond.And(builder.Eq{"is_active": true}) - } - - us := User{} - - if has, err := e.Where(cond).Get(&us); has || err != nil { - if has { - log.Info("isEmailActive('%s',%d,%d) found duplicate in user ID %d", email, userID, emailID, us.ID) - } - return has, err - } - return false, nil } @@ -136,7 +103,7 @@ func isEmailUsed(e Engine, email string) (bool, error) { return true, nil } - return e.Where("email=?", email).Get(&EmailAddress{}) + return e.Where("lower_email=?", strings.ToLower(email)).Get(&EmailAddress{}) } // IsEmailUsed returns true if the email has been used. @@ -145,7 +112,7 @@ func IsEmailUsed(email string) (bool, error) { } func addEmailAddress(e Engine, email *EmailAddress) error { - email.Email = strings.ToLower(strings.TrimSpace(email.Email)) + email.Email = strings.TrimSpace(email.Email) used, err := isEmailUsed(e, email.Email) if err != nil { return err @@ -174,7 +141,7 @@ func AddEmailAddresses(emails []*EmailAddress) error { // Check if any of them has been used for i := range emails { - emails[i].Email = strings.ToLower(strings.TrimSpace(emails[i].Email)) + emails[i].Email = strings.TrimSpace(emails[i].Email) used, err := IsEmailUsed(emails[i].Email) if err != nil { return err @@ -223,6 +190,10 @@ func (email *EmailAddress) updateActivation(e Engine, activate bool) error { // DeleteEmailAddress deletes an email address of given user. func DeleteEmailAddress(email *EmailAddress) (err error) { + if email.IsPrimary { + return ErrPrimaryEmailCannotDelete{Email: email.Email} + } + var deleted int64 // ask to check UID address := EmailAddress{ @@ -231,8 +202,11 @@ func DeleteEmailAddress(email *EmailAddress) (err error) { if email.ID > 0 { deleted, err = x.ID(email.ID).Delete(&address) } else { + if email.Email != "" && email.LowerEmail == "" { + email.LowerEmail = strings.ToLower(email.Email) + } deleted, err = x. - Where("email=?", email.Email). + Where("lower_email=?", email.LowerEmail). Delete(&address) } @@ -261,7 +235,7 @@ func MakeEmailPrimary(email *EmailAddress) error { if err != nil { return err } else if !has { - return ErrEmailNotExist + return ErrEmailAddressNotExist{Email: email.Email} } if !email.IsActivated { @@ -276,32 +250,31 @@ func MakeEmailPrimary(email *EmailAddress) error { return ErrUserNotExist{email.UID, "", 0} } - // Make sure the former primary email doesn't disappear. - formerPrimaryEmail := &EmailAddress{UID: user.ID, Email: user.Email} - has, err = x.Get(formerPrimaryEmail) - if err != nil { - return err - } - sess := x.NewSession() defer sess.Close() if err = sess.Begin(); err != nil { return err } - if !has { - formerPrimaryEmail.UID = user.ID - formerPrimaryEmail.IsActivated = user.IsActive - if _, err = sess.Insert(formerPrimaryEmail); err != nil { - return err - } - } - + // 1. Update user table user.Email = email.Email if _, err = sess.ID(user.ID).Cols("email").Update(user); err != nil { return err } + // 2. Update old primary email + if _, err = sess.Where("uid=? AND is_primary=?", email.UID, true).Cols("is_primary").Update(&EmailAddress{ + IsPrimary: false, + }); err != nil { + return err + } + + // 3. update new primay email + email.IsPrimary = true + if _, err = sess.ID(email.ID).Cols("is_primary").Update(email); err != nil { + return err + } + return sess.Commit() } @@ -314,10 +287,10 @@ func (s SearchEmailOrderBy) String() string { // Strings for sorting result const ( - SearchEmailOrderByEmail SearchEmailOrderBy = "emails.email ASC, is_primary DESC, sortid ASC" - SearchEmailOrderByEmailReverse SearchEmailOrderBy = "emails.email DESC, is_primary ASC, sortid DESC" - SearchEmailOrderByName SearchEmailOrderBy = "`user`.lower_name ASC, is_primary DESC, sortid ASC" - SearchEmailOrderByNameReverse SearchEmailOrderBy = "`user`.lower_name DESC, is_primary ASC, sortid DESC" + SearchEmailOrderByEmail SearchEmailOrderBy = "email_address.lower_email ASC, email_address.is_primary DESC, email_address.id ASC" + SearchEmailOrderByEmailReverse SearchEmailOrderBy = "email_address.lower_email DESC, email_address.is_primary ASC, email_address.id DESC" + SearchEmailOrderByName SearchEmailOrderBy = "`user`.lower_name ASC, email_address.is_primary DESC, email_address.id ASC" + SearchEmailOrderByNameReverse SearchEmailOrderBy = "`user`.lower_name DESC, email_address.is_primary ASC, email_address.id DESC" ) // SearchEmailOptions are options to search e-mail addresses for the admin panel @@ -343,54 +316,32 @@ type SearchEmailResult struct { // SearchEmails takes options i.e. keyword and part of email name to search, // it returns results in given range and number of total results. func SearchEmails(opts *SearchEmailOptions) ([]*SearchEmailResult, int64, error) { - // Unfortunately, UNION support for SQLite in xorm is currently broken, so we must - // build the SQL ourselves. - where := make([]string, 0, 5) - args := make([]interface{}, 0, 5) - - emailsSQL := "(SELECT id as sortid, uid, email, is_activated, 0 as is_primary " + - "FROM email_address " + - "UNION ALL " + - "SELECT id as sortid, id AS uid, email, is_active AS is_activated, 1 as is_primary " + - "FROM `user` " + - "WHERE type = ?) AS emails" - args = append(args, UserTypeIndividual) - + var cond builder.Cond = builder.Eq{"user.`type`": UserTypeIndividual} if len(opts.Keyword) > 0 { - // Note: % can be injected in the Keyword parameter, but it won't do any harm. - where = append(where, "(lower(`user`.full_name) LIKE ? OR `user`.lower_name LIKE ? OR emails.email LIKE ?)") likeStr := "%" + strings.ToLower(opts.Keyword) + "%" - args = append(args, likeStr) - args = append(args, likeStr) - args = append(args, likeStr) + cond = cond.And(builder.Or( + builder.Like{"lower(`user`.full_name)", likeStr}, + builder.Like{"`user`.lower_name", likeStr}, + builder.Like{"email_address.lower_email", likeStr}, + )) } switch { case opts.IsPrimary.IsTrue(): - where = append(where, "emails.is_primary = ?") - args = append(args, true) + cond = cond.And(builder.Eq{"email_address.is_primary": true}) case opts.IsPrimary.IsFalse(): - where = append(where, "emails.is_primary = ?") - args = append(args, false) + cond = cond.And(builder.Eq{"email_address.is_primary": false}) } switch { case opts.IsActivated.IsTrue(): - where = append(where, "emails.is_activated = ?") - args = append(args, true) + cond = cond.And(builder.Eq{"email_address.is_activated": true}) case opts.IsActivated.IsFalse(): - where = append(where, "emails.is_activated = ?") - args = append(args, false) - } - - var whereStr string - if len(where) > 0 { - whereStr = "WHERE " + strings.Join(where, " AND ") + cond = cond.And(builder.Eq{"email_address.is_activated": false}) } - joinSQL := "FROM " + emailsSQL + " INNER JOIN `user` ON `user`.id = emails.uid " + whereStr - - count, err := x.SQL("SELECT count(*) "+joinSQL, args...).Count() + count, err := x.Join("INNER", "`user`", "`user`.ID = email_address.uid"). + Where(cond).Count(new(EmailAddress)) if err != nil { return nil, 0, fmt.Errorf("Count: %v", err) } @@ -400,36 +351,16 @@ func SearchEmails(opts *SearchEmailOptions) ([]*SearchEmailResult, int64, error) orderby = SearchEmailOrderByEmail.String() } - querySQL := "SELECT emails.uid, emails.email, emails.is_activated, emails.is_primary, " + - "`user`.name, `user`.full_name " + joinSQL + " ORDER BY " + orderby - opts.setDefaultValues() - rows, err := x.SQL(querySQL, args...).Rows(new(SearchEmailResult)) - if err != nil { - return nil, 0, fmt.Errorf("Emails: %v", err) - } - - // Page manually because xorm can't handle Limit() with raw SQL - defer rows.Close() - emails := make([]*SearchEmailResult, 0, opts.PageSize) - skip := (opts.Page - 1) * opts.PageSize - - for rows.Next() { - var email SearchEmailResult - if err := rows.Scan(&email); err != nil { - return nil, 0, err - } - if skip > 0 { - skip-- - continue - } - emails = append(emails, &email) - if len(emails) == opts.PageSize { - break - } - } + err = x.Table("email_address"). + Select("email_address.*, `user`.name, `user`.full_name"). + Join("INNER", "`user`", "`user`.ID = email_address.uid"). + Where(cond). + OrderBy(orderby). + Limit(opts.PageSize, (opts.Page-1)*opts.PageSize). + Find(&emails) return emails, count, err } @@ -442,6 +373,30 @@ func ActivateUserEmail(userID int64, email string, primary, activate bool) (err if err = sess.Begin(); err != nil { return err } + + // Activate/deactivate a user's secondary email address + // First check if there's another user active with the same address + addr := EmailAddress{UID: userID, LowerEmail: strings.ToLower(email)} + if has, err := sess.Get(&addr); err != nil { + return err + } else if !has { + return fmt.Errorf("no such email: %d (%s)", userID, email) + } + if addr.IsActivated == activate { + // Already in the desired state; no action + return nil + } + if activate { + if used, err := isEmailActive(sess, email, 0, addr.ID); err != nil { + return fmt.Errorf("isEmailActive(): %v", err) + } else if used { + return ErrEmailAlreadyUsed{Email: email} + } + } + if err = addr.updateActivation(sess, activate); err != nil { + return fmt.Errorf("updateActivation(): %v", err) + } + if primary { // Activate/deactivate a user's primary email address user := User{ID: userID, Email: email} @@ -454,13 +409,6 @@ func ActivateUserEmail(userID int64, email string, primary, activate bool) (err // Already in the desired state; no action return nil } - if activate { - if used, err := isEmailActive(sess, email, userID, 0); err != nil { - return fmt.Errorf("isEmailActive(): %v", err) - } else if used { - return ErrEmailAlreadyUsed{Email: email} - } - } user.IsActive = activate if user.Rands, err = GetUserSalt(); err != nil { return fmt.Errorf("generate salt: %v", err) @@ -468,29 +416,7 @@ func ActivateUserEmail(userID int64, email string, primary, activate bool) (err if err = updateUserCols(sess, &user, "is_active", "rands"); err != nil { return fmt.Errorf("updateUserCols(): %v", err) } - } else { - // Activate/deactivate a user's secondary email address - // First check if there's another user active with the same address - addr := EmailAddress{UID: userID, Email: email} - if has, err := sess.Get(&addr); err != nil { - return err - } else if !has { - return fmt.Errorf("no such email: %d (%s)", userID, email) - } - if addr.IsActivated == activate { - // Already in the desired state; no action - return nil - } - if activate { - if used, err := isEmailActive(sess, email, 0, addr.ID); err != nil { - return fmt.Errorf("isEmailActive(): %v", err) - } else if used { - return ErrEmailAlreadyUsed{Email: email} - } - } - if err = addr.updateActivation(sess, activate); err != nil { - return fmt.Errorf("updateActivation(): %v", err) - } } + return sess.Commit() } diff --git a/models/user_mail_test.go b/models/user_mail_test.go index 8237ce66426f7..829a38c18dbbf 100644 --- a/models/user_mail_test.go +++ b/models/user_mail_test.go @@ -17,9 +17,9 @@ func TestGetEmailAddresses(t *testing.T) { emails, _ := GetEmailAddresses(int64(1)) if assert.Len(t, emails, 3) { - assert.False(t, emails[0].IsPrimary) + assert.True(t, emails[0].IsPrimary) assert.True(t, emails[2].IsActivated) - assert.True(t, emails[2].IsPrimary) + assert.False(t, emails[2].IsPrimary) } emails, _ = GetEmailAddresses(int64(2)) @@ -45,13 +45,15 @@ func TestAddEmailAddress(t *testing.T) { assert.NoError(t, AddEmailAddress(&EmailAddress{ Email: "user1234567890@example.com", + LowerEmail: "user1234567890@example.com", IsPrimary: true, IsActivated: true, })) // ErrEmailAlreadyUsed err := AddEmailAddress(&EmailAddress{ - Email: "user1234567890@example.com", + Email: "user1234567890@example.com", + LowerEmail: "user1234567890@example.com", }) assert.Error(t, err) assert.True(t, IsErrEmailAlreadyUsed(err)) @@ -64,10 +66,12 @@ func TestAddEmailAddresses(t *testing.T) { emails := make([]*EmailAddress, 2) emails[0] = &EmailAddress{ Email: "user1234@example.com", + LowerEmail: "user1234@example.com", IsActivated: true, } emails[1] = &EmailAddress{ Email: "user5678@example.com", + LowerEmail: "user5678@example.com", IsActivated: true, } assert.NoError(t, AddEmailAddresses(emails)) @@ -82,20 +86,23 @@ func TestDeleteEmailAddress(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, DeleteEmailAddress(&EmailAddress{ - UID: int64(1), - ID: int64(1), - Email: "user11@example.com", + UID: int64(1), + ID: int64(33), + Email: "user1-2@example.com", + LowerEmail: "user1-2@example.com", })) assert.NoError(t, DeleteEmailAddress(&EmailAddress{ - UID: int64(1), - Email: "user12@example.com", + UID: int64(1), + Email: "user1-3@example.com", + LowerEmail: "user1-3@example.com", })) // Email address does not exist err := DeleteEmailAddress(&EmailAddress{ - UID: int64(1), - Email: "user1234567890@example.com", + UID: int64(1), + Email: "user1234567890@example.com", + LowerEmail: "user1234567890@example.com", }) assert.Error(t, err) } @@ -106,13 +113,15 @@ func TestDeleteEmailAddresses(t *testing.T) { // delete multiple email address emails := make([]*EmailAddress, 2) emails[0] = &EmailAddress{ - UID: int64(2), - ID: int64(3), - Email: "user2@example.com", + UID: int64(2), + ID: int64(3), + Email: "user2@example.com", + LowerEmail: "user2@example.com", } emails[1] = &EmailAddress{ - UID: int64(2), - Email: "user21@example.com", + UID: int64(2), + Email: "user2-2@example.com", + LowerEmail: "user2-2@example.com", } assert.NoError(t, DeleteEmailAddresses(emails)) @@ -129,14 +138,14 @@ func TestMakeEmailPrimary(t *testing.T) { } err := MakeEmailPrimary(email) assert.Error(t, err) - assert.Equal(t, ErrEmailNotExist.Error(), err.Error()) + assert.EqualError(t, err, ErrEmailAddressNotExist{email.Email}.Error()) email = &EmailAddress{ Email: "user11@example.com", } err = MakeEmailPrimary(email) assert.Error(t, err) - assert.Equal(t, ErrEmailNotActivated.Error(), err.Error()) + assert.EqualError(t, err, ErrEmailNotActivated.Error()) email = &EmailAddress{ Email: "user9999999@example.com", @@ -168,15 +177,21 @@ func TestActivate(t *testing.T) { emails, _ := GetEmailAddresses(int64(1)) assert.Len(t, emails, 3) assert.True(t, emails[0].IsActivated) + assert.True(t, emails[0].IsPrimary) + assert.False(t, emails[1].IsPrimary) assert.True(t, emails[2].IsActivated) - assert.True(t, emails[2].IsPrimary) + assert.False(t, emails[2].IsPrimary) } func TestListEmails(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) // Must find all users and their emails - opts := &SearchEmailOptions{} + opts := &SearchEmailOptions{ + ListOptions: ListOptions{ + PageSize: 10000, + }, + } emails, count, err := SearchEmails(opts) assert.NoError(t, err) assert.NotEqual(t, int64(0), count) @@ -232,6 +247,6 @@ func TestListEmails(t *testing.T) { } emails, count, err = SearchEmails(opts) assert.NoError(t, err) - assert.Equal(t, 5, len(emails)) - assert.True(t, count > int64(len(emails))) + assert.Len(t, emails, 5) + assert.Greater(t, count, int64(len(emails))) } diff --git a/models/user_openid_test.go b/models/user_openid_test.go index 18f84bef760b2..d04b072279c49 100644 --- a/models/user_openid_test.go +++ b/models/user_openid_test.go @@ -15,15 +15,15 @@ func TestGetUserOpenIDs(t *testing.T) { oids, err := GetUserOpenIDs(int64(1)) if assert.NoError(t, err) && assert.Len(t, oids, 2) { - assert.Equal(t, oids[0].URI, "https://user1.domain1.tld/") + assert.Equal(t, "https://user1.domain1.tld/", oids[0].URI) assert.False(t, oids[0].Show) - assert.Equal(t, oids[1].URI, "http://user1.domain2.tld/") + assert.Equal(t, "http://user1.domain2.tld/", oids[1].URI) assert.True(t, oids[1].Show) } oids, err = GetUserOpenIDs(int64(2)) if assert.NoError(t, err) && assert.Len(t, oids, 1) { - assert.Equal(t, oids[0].URI, "https://domain1.tld/user2/") + assert.Equal(t, "https://domain1.tld/user2/", oids[0].URI) assert.True(t, oids[0].Show) } } @@ -38,12 +38,12 @@ func TestGetUserByOpenID(t *testing.T) { user, err := GetUserByOpenID("https://user1.domain1.tld") if assert.NoError(t, err) { - assert.Equal(t, user.ID, int64(1)) + assert.Equal(t, int64(1), user.ID) } user, err = GetUserByOpenID("https://domain1.tld/user2/") if assert.NoError(t, err) { - assert.Equal(t, user.ID, int64(2)) + assert.Equal(t, int64(2), user.ID) } } diff --git a/models/user_test.go b/models/user_test.go index e0d4e164fcfbb..39a1b3c989c05 100644 --- a/models/user_test.go +++ b/models/user_test.go @@ -386,14 +386,14 @@ func TestGetMaileableUsersByIDs(t *testing.T) { results, err := GetMaileableUsersByIDs([]int64{1, 4}, false) assert.NoError(t, err) - assert.Equal(t, 1, len(results)) + assert.Len(t, results, 1) if len(results) > 1 { assert.Equal(t, results[0].ID, 1) } results, err = GetMaileableUsersByIDs([]int64{1, 4}, true) assert.NoError(t, err) - assert.Equal(t, 2, len(results)) + assert.Len(t, results, 2) if len(results) > 2 { assert.Equal(t, results[0].ID, 1) assert.Equal(t, results[1].ID, 4) @@ -457,7 +457,7 @@ ssh-dss AAAAB3NzaC1kc3MAAACBAOChCC7lf6Uo9n7BmZ6M8St19PZf4Tn59NriyboW2x/DZuYAz3ib if err != nil { continue } - assert.Equal(t, kase.number, len(keys)) + assert.Len(t, keys, kase.number) for _, key := range keys { assert.Contains(t, kase.keyContents, key.Content) diff --git a/modules/auth/oauth2/jwtsigningkey.go b/modules/auth/oauth2/jwtsigningkey.go new file mode 100644 index 0000000000000..75e62a7c43039 --- /dev/null +++ b/modules/auth/oauth2/jwtsigningkey.go @@ -0,0 +1,378 @@ +// 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 oauth2 + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "fmt" + "io/ioutil" + "math/big" + "os" + "path/filepath" + "strings" + + "code.gitea.io/gitea/modules/generate" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" + + "github.com/dgrijalva/jwt-go" + ini "gopkg.in/ini.v1" +) + +// ErrInvalidAlgorithmType represents an invalid algorithm error. +type ErrInvalidAlgorithmType struct { + Algorightm string +} + +func (err ErrInvalidAlgorithmType) Error() string { + return fmt.Sprintf("JWT signing algorithm is not supported: %s", err.Algorightm) +} + +// JWTSigningKey represents a algorithm/key pair to sign JWTs +type JWTSigningKey interface { + IsSymmetric() bool + SigningMethod() jwt.SigningMethod + SignKey() interface{} + VerifyKey() interface{} + ToJWK() (map[string]string, error) + PreProcessToken(*jwt.Token) +} + +type hmacSigningKey struct { + signingMethod jwt.SigningMethod + secret []byte +} + +func (key hmacSigningKey) IsSymmetric() bool { + return true +} + +func (key hmacSigningKey) SigningMethod() jwt.SigningMethod { + return key.signingMethod +} + +func (key hmacSigningKey) SignKey() interface{} { + return key.secret +} + +func (key hmacSigningKey) VerifyKey() interface{} { + return key.secret +} + +func (key hmacSigningKey) ToJWK() (map[string]string, error) { + return map[string]string{ + "kty": "oct", + "alg": key.SigningMethod().Alg(), + }, nil +} + +func (key hmacSigningKey) PreProcessToken(*jwt.Token) {} + +type rsaSingingKey struct { + signingMethod jwt.SigningMethod + key *rsa.PrivateKey + id string +} + +func newRSASingingKey(signingMethod jwt.SigningMethod, key *rsa.PrivateKey) (rsaSingingKey, error) { + kid, err := createPublicKeyFingerprint(key.Public().(*rsa.PublicKey)) + if err != nil { + return rsaSingingKey{}, err + } + + return rsaSingingKey{ + signingMethod, + key, + base64.RawURLEncoding.EncodeToString(kid), + }, nil +} + +func (key rsaSingingKey) IsSymmetric() bool { + return false +} + +func (key rsaSingingKey) SigningMethod() jwt.SigningMethod { + return key.signingMethod +} + +func (key rsaSingingKey) SignKey() interface{} { + return key.key +} + +func (key rsaSingingKey) VerifyKey() interface{} { + return key.key.Public() +} + +func (key rsaSingingKey) ToJWK() (map[string]string, error) { + pubKey := key.key.Public().(*rsa.PublicKey) + + return map[string]string{ + "kty": "RSA", + "alg": key.SigningMethod().Alg(), + "kid": key.id, + "e": base64.RawURLEncoding.EncodeToString(big.NewInt(int64(pubKey.E)).Bytes()), + "n": base64.RawURLEncoding.EncodeToString(pubKey.N.Bytes()), + }, nil +} + +func (key rsaSingingKey) PreProcessToken(token *jwt.Token) { + token.Header["kid"] = key.id +} + +type ecdsaSingingKey struct { + signingMethod jwt.SigningMethod + key *ecdsa.PrivateKey + id string +} + +func newECDSASingingKey(signingMethod jwt.SigningMethod, key *ecdsa.PrivateKey) (ecdsaSingingKey, error) { + kid, err := createPublicKeyFingerprint(key.Public().(*ecdsa.PublicKey)) + if err != nil { + return ecdsaSingingKey{}, err + } + + return ecdsaSingingKey{ + signingMethod, + key, + base64.RawURLEncoding.EncodeToString(kid), + }, nil +} + +func (key ecdsaSingingKey) IsSymmetric() bool { + return false +} + +func (key ecdsaSingingKey) SigningMethod() jwt.SigningMethod { + return key.signingMethod +} + +func (key ecdsaSingingKey) SignKey() interface{} { + return key.key +} + +func (key ecdsaSingingKey) VerifyKey() interface{} { + return key.key.Public() +} + +func (key ecdsaSingingKey) ToJWK() (map[string]string, error) { + pubKey := key.key.Public().(*ecdsa.PublicKey) + + return map[string]string{ + "kty": "EC", + "alg": key.SigningMethod().Alg(), + "kid": key.id, + "crv": pubKey.Params().Name, + "x": base64.RawURLEncoding.EncodeToString(pubKey.X.Bytes()), + "y": base64.RawURLEncoding.EncodeToString(pubKey.Y.Bytes()), + }, nil +} + +func (key ecdsaSingingKey) PreProcessToken(token *jwt.Token) { + token.Header["kid"] = key.id +} + +// createPublicKeyFingerprint creates a fingerprint of the given key. +// The fingerprint is the sha256 sum of the PKIX structure of the key. +func createPublicKeyFingerprint(key interface{}) ([]byte, error) { + bytes, err := x509.MarshalPKIXPublicKey(key) + if err != nil { + return nil, err + } + + checksum := sha256.Sum256(bytes) + + return checksum[:], nil +} + +// CreateJWTSingingKey creates a signing key from an algorithm / key pair. +func CreateJWTSingingKey(algorithm string, key interface{}) (JWTSigningKey, error) { + var signingMethod jwt.SigningMethod + switch algorithm { + case "HS256": + signingMethod = jwt.SigningMethodHS256 + case "HS384": + signingMethod = jwt.SigningMethodHS384 + case "HS512": + signingMethod = jwt.SigningMethodHS512 + + case "RS256": + signingMethod = jwt.SigningMethodRS256 + case "RS384": + signingMethod = jwt.SigningMethodRS384 + case "RS512": + signingMethod = jwt.SigningMethodRS512 + + case "ES256": + signingMethod = jwt.SigningMethodES256 + case "ES384": + signingMethod = jwt.SigningMethodES384 + case "ES512": + signingMethod = jwt.SigningMethodES512 + default: + return nil, ErrInvalidAlgorithmType{algorithm} + } + + switch signingMethod.(type) { + case *jwt.SigningMethodECDSA: + privateKey, ok := key.(*ecdsa.PrivateKey) + if !ok { + return nil, jwt.ErrInvalidKeyType + } + return newECDSASingingKey(signingMethod, privateKey) + case *jwt.SigningMethodRSA: + privateKey, ok := key.(*rsa.PrivateKey) + if !ok { + return nil, jwt.ErrInvalidKeyType + } + return newRSASingingKey(signingMethod, privateKey) + default: + secret, ok := key.([]byte) + if !ok { + return nil, jwt.ErrInvalidKeyType + } + return hmacSigningKey{signingMethod, secret}, nil + } +} + +// DefaultSigningKey is the default signing key for JWTs. +var DefaultSigningKey JWTSigningKey + +// InitSigningKey creates the default signing key from settings or creates a random key. +func InitSigningKey() error { + var err error + var key interface{} + + switch setting.OAuth2.JWTSigningAlgorithm { + case "HS256": + fallthrough + case "HS384": + fallthrough + case "HS512": + key, err = loadOrCreateSymmetricKey() + + case "RS256": + fallthrough + case "RS384": + fallthrough + case "RS512": + fallthrough + case "ES256": + fallthrough + case "ES384": + fallthrough + case "ES512": + key, err = loadOrCreateAsymmetricKey() + + default: + return ErrInvalidAlgorithmType{setting.OAuth2.JWTSigningAlgorithm} + } + + if err != nil { + return fmt.Errorf("Error while loading or creating symmetric key: %v", err) + } + + signingKey, err := CreateJWTSingingKey(setting.OAuth2.JWTSigningAlgorithm, key) + if err != nil { + return err + } + + DefaultSigningKey = signingKey + + return nil +} + +// loadOrCreateSymmetricKey checks if the configured secret is valid. +// If it is not valid a new secret is created and saved in the configuration file. +func loadOrCreateSymmetricKey() (interface{}, error) { + key := make([]byte, 32) + n, err := base64.RawURLEncoding.Decode(key, []byte(setting.OAuth2.JWTSecretBase64)) + if err != nil || n != 32 { + key, err = generate.NewJwtSecret() + if err != nil { + log.Fatal("error generating JWT secret: %v", err) + return nil, err + } + + setting.CreateOrAppendToCustomConf(func(cfg *ini.File) { + secretBase64 := base64.RawURLEncoding.EncodeToString(key) + cfg.Section("oauth2").Key("JWT_SECRET").SetValue(secretBase64) + }) + } + + return key, nil +} + +// loadOrCreateAsymmetricKey checks if the configured private key exists. +// If it does not exist a new random key gets generated and saved on the configured path. +func loadOrCreateAsymmetricKey() (interface{}, error) { + keyPath := setting.OAuth2.JWTSigningPrivateKeyFile + + isExist, err := util.IsExist(keyPath) + if err != nil { + log.Fatal("Unable to check if %s exists. Error: %v", keyPath, err) + } + if !isExist { + err := func() error { + key, err := func() (interface{}, error) { + if strings.HasPrefix(setting.OAuth2.JWTSigningAlgorithm, "RS") { + return rsa.GenerateKey(rand.Reader, 4096) + } + return ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + }() + if err != nil { + return err + } + + bytes, err := x509.MarshalPKCS8PrivateKey(key) + if err != nil { + return err + } + + privateKeyPEM := &pem.Block{Type: "PRIVATE KEY", Bytes: bytes} + + if err := os.MkdirAll(filepath.Dir(keyPath), os.ModePerm); err != nil { + return err + } + + f, err := os.OpenFile(keyPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + return err + } + defer func() { + if err = f.Close(); err != nil { + log.Error("Close: %v", err) + } + }() + + return pem.Encode(f, privateKeyPEM) + }() + if err != nil { + log.Fatal("Error generating private key: %v", err) + return nil, err + } + } + + bytes, err := ioutil.ReadFile(keyPath) + if err != nil { + return nil, err + } + + block, _ := pem.Decode(bytes) + if block == nil { + return nil, fmt.Errorf("no valid PEM data found in %s", keyPath) + } else if block.Type != "PRIVATE KEY" { + return nil, fmt.Errorf("expected PRIVATE KEY, got %s in %s", block.Type, keyPath) + } + + return x509.ParsePKCS8PrivateKey(block.Bytes) +} diff --git a/modules/auth/pam/pam_test.go b/modules/auth/pam/pam_test.go index eafc9bc3dba4b..fa16ff0fe78cd 100644 --- a/modules/auth/pam/pam_test.go +++ b/modules/auth/pam/pam_test.go @@ -15,6 +15,6 @@ import ( func TestPamAuth(t *testing.T) { result, err := Auth("gitea", "user1", "false-pwd") assert.Error(t, err) - assert.EqualValues(t, "Authentication failure", err.Error()) + assert.EqualError(t, err, "Authentication failure") assert.Len(t, result, 0) } diff --git a/modules/auth/sso/session.go b/modules/auth/sso/session.go deleted file mode 100644 index 7a546577d86ee..0000000000000 --- a/modules/auth/sso/session.go +++ /dev/null @@ -1,48 +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 sso - -import ( - "net/http" - - "code.gitea.io/gitea/models" -) - -// Ensure the struct implements the interface. -var ( - _ SingleSignOn = &Session{} -) - -// Session checks if there is a user uid stored in the session and returns the user -// object for that uid. -type Session struct { -} - -// Init does nothing as the Session implementation does not need to allocate any resources -func (s *Session) Init() error { - return nil -} - -// Free does nothing as the Session implementation does not have to release any resources -func (s *Session) Free() error { - return nil -} - -// IsEnabled returns true as this plugin is enabled by default and its not possible to disable -// it from settings. -func (s *Session) IsEnabled() bool { - return true -} - -// VerifyAuthData checks if there is a user uid stored in the session and returns the user -// object for that uid. -// Returns nil if there is no user uid stored in the session. -func (s *Session) VerifyAuthData(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *models.User { - user := SessionUser(sess) - if user != nil { - return user - } - return nil -} diff --git a/modules/auth/sso/user.go b/modules/auth/sso/user.go deleted file mode 100644 index 48eebb1e915f0..0000000000000 --- a/modules/auth/sso/user.go +++ /dev/null @@ -1,33 +0,0 @@ -// 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 sso - -import ( - "net/http" - - "code.gitea.io/gitea/models" -) - -// SignedInUser returns the user object of signed user. -// It returns a bool value to indicate whether user uses basic auth or not. -func SignedInUser(req *http.Request, w http.ResponseWriter, ds DataStore, sess SessionStore) (*models.User, bool) { - if !models.HasEngine { - return nil, false - } - - // Try to sign in with each of the enabled plugins - for _, ssoMethod := range Methods() { - if !ssoMethod.IsEnabled() { - continue - } - user := ssoMethod.VerifyAuthData(req, w, ds, sess) - if user != nil { - _, isBasic := ssoMethod.(*Basic) - return user, isBasic - } - } - - return nil, false -} diff --git a/modules/avatar/avatar.go b/modules/avatar/avatar.go index bb9c2e953b5c8..5411a90796f5c 100644 --- a/modules/avatar/avatar.go +++ b/modules/avatar/avatar.go @@ -10,8 +10,9 @@ import ( "image" "image/color/palette" - // Enable PNG support: - _ "image/png" + _ "image/gif" // for processing gif images + _ "image/jpeg" // for processing jpeg images + _ "image/png" // for processing png images "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" diff --git a/modules/base/tool.go b/modules/base/tool.go index c9530473e2501..775fd709cfffa 100644 --- a/modules/base/tool.go +++ b/modules/base/tool.go @@ -12,10 +12,8 @@ import ( "encoding/hex" "errors" "fmt" - "net/http" "os" "path/filepath" - "regexp" "runtime" "strconv" "strings" @@ -30,15 +28,6 @@ import ( "github.com/dustin/go-humanize" ) -// Use at most this many bytes to determine Content Type. -const sniffLen = 512 - -// SVGMimeType MIME type of SVG images. -const SVGMimeType = "image/svg+xml" - -var svgTagRegex = regexp.MustCompile(`(?si)\A\s*(?:(||>))\s*)*\/]`) -var svgTagInXMLRegex = regexp.MustCompile(`(?si)\A<\?xml\b.*?\?>\s*(?:(||>))\s*)*\/]`) - // EncodeMD5 encodes string to md5 hex value. func EncodeMD5(str string) string { m := md5.New() @@ -276,63 +265,6 @@ func IsLetter(ch rune) bool { return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch) } -// DetectContentType extends http.DetectContentType with more content types. -func DetectContentType(data []byte) string { - ct := http.DetectContentType(data) - - if len(data) > sniffLen { - data = data[:sniffLen] - } - - if setting.UI.SVG.Enabled && - ((strings.Contains(ct, "text/plain") || strings.Contains(ct, "text/html")) && svgTagRegex.Match(data) || - strings.Contains(ct, "text/xml") && svgTagInXMLRegex.Match(data)) { - - // SVG is unsupported. https://github.com/golang/go/issues/15888 - return SVGMimeType - } - return ct -} - -// IsRepresentableAsText returns true if file content can be represented as -// plain text or is empty. -func IsRepresentableAsText(data []byte) bool { - return IsTextFile(data) || IsSVGImageFile(data) -} - -// IsTextFile returns true if file content format is plain text or empty. -func IsTextFile(data []byte) bool { - if len(data) == 0 { - return true - } - return strings.Contains(DetectContentType(data), "text/") -} - -// IsImageFile detects if data is an image format -func IsImageFile(data []byte) bool { - return strings.Contains(DetectContentType(data), "image/") -} - -// IsSVGImageFile detects if data is an SVG image format -func IsSVGImageFile(data []byte) bool { - return strings.Contains(DetectContentType(data), SVGMimeType) -} - -// IsPDFFile detects if data is a pdf format -func IsPDFFile(data []byte) bool { - return strings.Contains(DetectContentType(data), "application/pdf") -} - -// IsVideoFile detects if data is an video format -func IsVideoFile(data []byte) bool { - return strings.Contains(DetectContentType(data), "video/") -} - -// IsAudioFile detects if data is an video format -func IsAudioFile(data []byte) bool { - return strings.Contains(DetectContentType(data), "audio/") -} - // EntryIcon returns the octicon class for displaying files/directories func EntryIcon(entry *git.TreeEntry) string { switch { diff --git a/modules/base/tool_test.go b/modules/base/tool_test.go index fcd3ca296adc8..5280827e8ae35 100644 --- a/modules/base/tool_test.go +++ b/modules/base/tool_test.go @@ -5,7 +5,6 @@ package base import ( - "encoding/base64" "os" "testing" "time" @@ -224,9 +223,9 @@ func TestInt64sToMap(t *testing.T) { func TestInt64sContains(t *testing.T) { assert.Equal(t, map[int64]bool{}, Int64sToMap([]int64{})) - assert.Equal(t, true, Int64sContains([]int64{6, 44324, 4324, 32, 1, 2323}, 1)) - assert.Equal(t, true, Int64sContains([]int64{2323}, 2323)) - assert.Equal(t, false, Int64sContains([]int64{6, 44324, 4324, 32, 1, 2323}, 232)) + assert.True(t, Int64sContains([]int64{6, 44324, 4324, 32, 1, 2323}, 1)) + assert.True(t, Int64sContains([]int64{2323}, 2323)) + assert.False(t, Int64sContains([]int64{6, 44324, 4324, 32, 1, 2323}, 232)) } func TestIsLetter(t *testing.T) { @@ -246,102 +245,11 @@ func TestIsLetter(t *testing.T) { assert.False(t, IsLetter(0x93)) } -func TestDetectContentTypeLongerThanSniffLen(t *testing.T) { - // Pre-condition: Shorter than sniffLen detects SVG. - assert.Equal(t, "image/svg+xml", DetectContentType([]byte(``))) - // Longer than sniffLen detects something else. - assert.Equal(t, "text/plain; charset=utf-8", DetectContentType([]byte(``))) -} - -// IsRepresentableAsText - -func TestIsTextFile(t *testing.T) { - assert.True(t, IsTextFile([]byte{})) - assert.True(t, IsTextFile([]byte("lorem ipsum"))) -} - -func TestIsImageFile(t *testing.T) { - png, _ := base64.StdEncoding.DecodeString("iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAG0lEQVQYlWN4+vTpf3SMDTAMBYXYBLFpHgoKAeiOf0SGE9kbAAAAAElFTkSuQmCC") - assert.True(t, IsImageFile(png)) - assert.False(t, IsImageFile([]byte("plain text"))) -} - -func TestIsSVGImageFile(t *testing.T) { - assert.True(t, IsSVGImageFile([]byte(""))) - assert.True(t, IsSVGImageFile([]byte(" "))) - assert.True(t, IsSVGImageFile([]byte(``))) - assert.True(t, IsSVGImageFile([]byte(""))) - assert.True(t, IsSVGImageFile([]byte(``))) - assert.True(t, IsSVGImageFile([]byte(` - `))) - assert.True(t, IsSVGImageFile([]byte(` - - `))) - assert.True(t, IsSVGImageFile([]byte(` - `))) - assert.True(t, IsSVGImageFile([]byte(` - `))) - assert.True(t, IsSVGImageFile([]byte(` - - `))) - assert.True(t, IsSVGImageFile([]byte(` - - - `))) - assert.True(t, IsSVGImageFile([]byte(` - - `))) - assert.True(t, IsSVGImageFile([]byte(` - - - `))) - assert.False(t, IsSVGImageFile([]byte{})) - assert.False(t, IsSVGImageFile([]byte("svg"))) - assert.False(t, IsSVGImageFile([]byte(""))) - assert.False(t, IsSVGImageFile([]byte("text"))) - assert.False(t, IsSVGImageFile([]byte(""))) - assert.False(t, IsSVGImageFile([]byte(``))) - assert.False(t, IsSVGImageFile([]byte(` - `))) - assert.False(t, IsSVGImageFile([]byte(` - - `))) -} - -func TestIsPDFFile(t *testing.T) { - pdf, _ := base64.StdEncoding.DecodeString("JVBERi0xLjYKJcOkw7zDtsOfCjIgMCBvYmoKPDwvTGVuZ3RoIDMgMCBSL0ZpbHRlci9GbGF0ZURlY29kZT4+CnN0cmVhbQp4nF3NPwsCMQwF8D2f4s2CNYk1baF0EHRwOwg4iJt/NsFb/PpevUE4Mjwe") - assert.True(t, IsPDFFile(pdf)) - assert.False(t, IsPDFFile([]byte("plain text"))) -} - -func TestIsVideoFile(t *testing.T) { - mp4, _ := base64.StdEncoding.DecodeString("AAAAGGZ0eXBtcDQyAAAAAGlzb21tcDQyAAEI721vb3YAAABsbXZoZAAAAADaBlwX2gZcFwAAA+gA") - assert.True(t, IsVideoFile(mp4)) - assert.False(t, IsVideoFile([]byte("plain text"))) -} - -func TestIsAudioFile(t *testing.T) { - mp3, _ := base64.StdEncoding.DecodeString("SUQzBAAAAAABAFRYWFgAAAASAAADbWFqb3JfYnJhbmQAbXA0MgBUWFhYAAAAEQAAA21pbm9yX3Zl") - assert.True(t, IsAudioFile(mp3)) - assert.False(t, IsAudioFile([]byte("plain text"))) -} - // TODO: Test EntryIcon func TestSetupGiteaRoot(t *testing.T) { _ = os.Setenv("GITEA_ROOT", "test") - assert.EqualValues(t, "test", SetupGiteaRoot()) + assert.Equal(t, "test", SetupGiteaRoot()) _ = os.Setenv("GITEA_ROOT", "") assert.NotEqual(t, "test", SetupGiteaRoot()) } diff --git a/modules/context/api.go b/modules/context/api.go index cbd90c50e4b8e..506824674522e 100644 --- a/modules/context/api.go +++ b/modules/context/api.go @@ -14,11 +14,11 @@ import ( "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/setting" "code.gitea.io/gitea/modules/web/middleware" + "code.gitea.io/gitea/services/auth" "gitea.com/go-chi/session" ) @@ -217,6 +217,26 @@ func (ctx *APIContext) CheckForOTP() { } } +// APIAuth converts auth.Auth as a middleware +func APIAuth(authMethod auth.Auth) func(*APIContext) { + return func(ctx *APIContext) { + // Get user from session if logged in. + ctx.User = authMethod.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session) + if ctx.User != nil { + ctx.IsBasicAuth = ctx.Data["AuthedMethod"].(string) == new(auth.Basic).Name() + 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"] = "" + } + } +} + // APIContexter returns apicontext as middleware func APIContexter() func(http.Handler) http.Handler { var csrfOpts = getCsrfOpts() @@ -250,20 +270,6 @@ func APIContexter() func(http.Handler) http.Handler { } } - // 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()) diff --git a/modules/context/context.go b/modules/context/context.go index d45e9ff87cd89..7b3fd2899acd9 100644 --- a/modules/context/context.go +++ b/modules/context/context.go @@ -21,14 +21,15 @@ import ( "time" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth/sso" "code.gitea.io/gitea/modules/base" mc "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/translation" + "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web/middleware" + "code.gitea.io/gitea/services/auth" "gitea.com/go-chi/cache" "gitea.com/go-chi/session" @@ -319,6 +320,11 @@ func (ctx *Context) QueryBool(key string, defaults ...bool) bool { return (*Forms)(ctx.Req).MustBool(key, defaults...) } +// QueryOptionalBool returns request form as OptionalBool with default +func (ctx *Context) QueryOptionalBool(key string, defaults ...util.OptionalBool) util.OptionalBool { + return (*Forms)(ctx.Req).MustOptionalBool(key, defaults...) +} + // HandleText handles HTTP status code func (ctx *Context) HandleText(status int, title string) { if (status/100 == 4) || (status/100 == 5) { @@ -605,6 +611,28 @@ func getCsrfOpts() CsrfOptions { } } +// Auth converts auth.Auth as a middleware +func Auth(authMethod auth.Auth) func(*Context) { + return func(ctx *Context) { + ctx.User = authMethod.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session) + if ctx.User != nil { + ctx.IsBasicAuth = ctx.Data["AuthedMethod"].(string) == new(auth.Basic).Name() + 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"] = "" + + // ensure the session uid is deleted + _ = ctx.Session.Delete("uid") + } + } +} + // Contexter initializes a classic context for a request. func Contexter() func(next http.Handler) http.Handler { var rnd = templates.HTMLRenderer() @@ -690,24 +718,6 @@ func Contexter() func(next http.Handler) http.Handler { } } - // 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"] = "" - - // ensure the session uid is deleted - _ = ctx.Session.Delete("uid") - } - ctx.Resp.Header().Set(`X-Frame-Options`, `SAMEORIGIN`) ctx.Data["CsrfToken"] = html.EscapeString(ctx.csrf.GetToken()) diff --git a/modules/context/form.go b/modules/context/form.go index c7b76c614c27a..e3afad0a9046e 100644 --- a/modules/context/form.go +++ b/modules/context/form.go @@ -13,6 +13,7 @@ import ( "text/template" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/util" ) // Forms a new enhancement of http.Request @@ -225,3 +226,16 @@ func (f *Forms) MustBool(key string, defaults ...bool) bool { } return v } + +// MustOptionalBool returns request form as OptionalBool with default +func (f *Forms) MustOptionalBool(key string, defaults ...util.OptionalBool) util.OptionalBool { + value := (*http.Request)(f).FormValue(key) + if len(value) == 0 { + return util.OptionalBoolNone + } + v, err := strconv.ParseBool((*http.Request)(f).FormValue(key)) + if len(defaults) > 0 && err != nil { + return defaults[0] + } + return util.OptionalBoolOf(v) +} diff --git a/modules/context/repo.go b/modules/context/repo.go index 3e48b34b3d165..72d1cf4c85615 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -360,13 +360,17 @@ func repoAssignment(ctx *Context, repo *models.Repository) { var err error ctx.Repo.Mirror, err = models.GetMirrorByRepoID(repo.ID) if err != nil { - ctx.ServerError("GetMirror", err) + ctx.ServerError("GetMirrorByRepoID", err) return } ctx.Data["MirrorEnablePrune"] = ctx.Repo.Mirror.EnablePrune ctx.Data["MirrorInterval"] = ctx.Repo.Mirror.Interval ctx.Data["Mirror"] = ctx.Repo.Mirror } + if err = repo.LoadPushMirrors(); err != nil { + ctx.ServerError("LoadPushMirrors", err) + return + } ctx.Repo.Repository = repo ctx.Data["RepoName"] = ctx.Repo.Repository.Name diff --git a/modules/convert/convert.go b/modules/convert/convert.go index 109931dbc3433..0b2135c5803dc 100644 --- a/modules/convert/convert.go +++ b/modules/convert/convert.go @@ -8,6 +8,7 @@ package convert import ( "fmt" "strconv" + "strings" "time" "code.gitea.io/gitea/models" @@ -135,6 +136,7 @@ func ToBranchProtection(bp *models.ProtectedBranch) *api.BranchProtection { func ToTag(repo *models.Repository, t *git.Tag) *api.Tag { return &api.Tag{ Name: t.Name, + Message: strings.TrimSpace(t.Message), ID: t.ID.String(), Commit: ToCommitMeta(repo, t), ZipballURL: util.URLJoin(repo.HTMLURL(), "archive", t.Name+".zip"), diff --git a/modules/convert/repository.go b/modules/convert/repository.go index 9a4fbb97caec7..7f3d67137f755 100644 --- a/modules/convert/repository.go +++ b/modules/convert/repository.go @@ -91,7 +91,7 @@ func innerToRepo(repo *models.Repository, mode models.AccessMode, isParent bool) return nil } - numReleases, _ := models.GetReleaseCountByRepoID(repo.ID, models.FindReleasesOptions{IncludeDrafts: false, IncludeTags: true}) + numReleases, _ := models.GetReleaseCountByRepoID(repo.ID, models.FindReleasesOptions{IncludeDrafts: false, IncludeTags: false}) mirrorInterval := "" if repo.IsMirror { diff --git a/modules/convert/user.go b/modules/convert/user.go index 088ede5add507..07a4efd41a708 100644 --- a/modules/convert/user.go +++ b/modules/convert/user.go @@ -25,6 +25,15 @@ func ToUser(user, doer *models.User) *api.User { return toUser(user, signed, authed) } +// ToUsers convert list of models.User to list of api.User +func ToUsers(doer *models.User, users []*models.User) []*api.User { + result := make([]*api.User, len(users)) + for i := range users { + result[i] = ToUser(users[i], doer) + } + return result +} + // ToUserWithAccessMode convert models.User to api.User // AccessMode is not none show add some more information func ToUserWithAccessMode(user *models.User, accessMode models.AccessMode) *api.User { @@ -48,6 +57,10 @@ func toUser(user *models.User, signed, authed bool) *api.User { Location: user.Location, Website: user.Website, Description: user.Description, + // counter's + Followers: user.NumFollowers, + Following: user.NumFollowing, + StarredRepos: user.NumStars, } // hide primary email if API caller is anonymous or user keep email private if signed && (!user.KeepEmailPrivate || authed) { diff --git a/modules/emoji/emoji.go b/modules/emoji/emoji.go index 01fb764ce368e..85df2d697307e 100644 --- a/modules/emoji/emoji.go +++ b/modules/emoji/emoji.go @@ -6,6 +6,7 @@ package emoji import ( + "io" "sort" "strings" "sync" @@ -145,6 +146,8 @@ func (n *rememberSecondWriteWriter) Write(p []byte) (int, error) { if n.writecount == 2 { n.idx = n.pos n.end = n.pos + len(p) + n.pos += len(p) + return len(p), io.EOF } n.pos += len(p) return len(p), nil @@ -155,6 +158,8 @@ func (n *rememberSecondWriteWriter) WriteString(s string) (int, error) { if n.writecount == 2 { n.idx = n.pos n.end = n.pos + len(s) + n.pos += len(s) + return len(s), io.EOF } n.pos += len(s) return len(s), nil diff --git a/modules/generate/generate.go b/modules/generate/generate.go index 96589d3fb9ac5..4ed2a503b0041 100644 --- a/modules/generate/generate.go +++ b/modules/generate/generate.go @@ -38,14 +38,23 @@ func NewInternalToken() (string, error) { return internalToken, nil } -// NewJwtSecret generate a new value intended to be used by LFS_JWT_SECRET. -func NewJwtSecret() (string, error) { - JWTSecretBytes := make([]byte, 32) - _, err := io.ReadFull(rand.Reader, JWTSecretBytes) +// NewJwtSecret generates a new value intended to be used for JWT secrets. +func NewJwtSecret() ([]byte, error) { + bytes := make([]byte, 32) + _, err := io.ReadFull(rand.Reader, bytes) + if err != nil { + return nil, err + } + return bytes, nil +} + +// NewJwtSecretBase64 generates a new base64 encoded value intended to be used for JWT secrets. +func NewJwtSecretBase64() (string, error) { + bytes, err := NewJwtSecret() if err != nil { return "", err } - return base64.RawURLEncoding.EncodeToString(JWTSecretBytes), nil + return base64.RawURLEncoding.EncodeToString(bytes), nil } // NewSecretKey generate a new value intended to be used by SECRET_KEY. diff --git a/modules/git/blob.go b/modules/git/blob.go index 674a6a9592778..732356e5b2f96 100644 --- a/modules/git/blob.go +++ b/modules/git/blob.go @@ -10,6 +10,8 @@ import ( "encoding/base64" "io" "io/ioutil" + + "code.gitea.io/gitea/modules/typesniffer" ) // This file contains common functions between the gogit and !gogit variants for git Blobs @@ -82,3 +84,14 @@ func (b *Blob) GetBlobContentBase64() (string, error) { } return string(out), nil } + +// GuessContentType guesses the content type of the blob. +func (b *Blob) GuessContentType() (typesniffer.SniffedType, error) { + r, err := b.DataAsync() + if err != nil { + return typesniffer.SniffedType{}, err + } + defer r.Close() + + return typesniffer.DetectContentTypeFromReader(r) +} diff --git a/modules/git/commit.go b/modules/git/commit.go index 027642720d0ab..f4d6075fe2d22 100644 --- a/modules/git/commit.go +++ b/modules/git/commit.go @@ -11,13 +11,7 @@ import ( "container/list" "errors" "fmt" - "image" - "image/color" - _ "image/gif" // for processing gif images - _ "image/jpeg" // for processing jpeg images - _ "image/png" // for processing png images "io" - "net/http" "os/exec" "strconv" "strings" @@ -81,70 +75,6 @@ func (c *Commit) ParentCount() int { return len(c.Parents) } -func isImageFile(data []byte) (string, bool) { - contentType := http.DetectContentType(data) - if strings.Contains(contentType, "image/") { - return contentType, true - } - return contentType, false -} - -// IsImageFile is a file image type -func (c *Commit) IsImageFile(name string) bool { - blob, err := c.GetBlobByPath(name) - if err != nil { - return false - } - - dataRc, err := blob.DataAsync() - if err != nil { - return false - } - defer dataRc.Close() - buf := make([]byte, 1024) - n, _ := dataRc.Read(buf) - buf = buf[:n] - _, isImage := isImageFile(buf) - return isImage -} - -// ImageMetaData represents metadata of an image file -type ImageMetaData struct { - ColorModel color.Model - Width int - Height int - ByteSize int64 -} - -// ImageInfo returns information about the dimensions of an image -func (c *Commit) ImageInfo(name string) (*ImageMetaData, error) { - if !c.IsImageFile(name) { - return nil, nil - } - - blob, err := c.GetBlobByPath(name) - if err != nil { - return nil, err - } - reader, err := blob.DataAsync() - if err != nil { - return nil, err - } - defer reader.Close() - config, _, err := image.DecodeConfig(reader) - if err != nil { - return nil, err - } - - metadata := ImageMetaData{ - ColorModel: config.ColorModel, - Width: config.Width, - Height: config.Height, - ByteSize: blob.Size(), - } - return &metadata, nil -} - // GetCommitByPath return the commit of relative path object. func (c *Commit) GetCommitByPath(relpath string) (*Commit, error) { return c.repo.getCommitByPathWithID(c.ID, relpath) diff --git a/modules/git/commit_info_gogit.go b/modules/git/commit_info_gogit.go index 6d95e22d0c7c8..a8006dcef2e64 100644 --- a/modules/git/commit_info_gogit.go +++ b/modules/git/commit_info_gogit.go @@ -7,6 +7,7 @@ package git import ( + "context" "path" "github.com/emirpasic/gods/trees/binaryheap" @@ -16,7 +17,7 @@ import ( ) // GetCommitsInfo gets information of all commits that are corresponding to these entries -func (tes Entries) GetCommitsInfo(commit *Commit, treePath string, cache *LastCommitCache) ([]CommitInfo, *Commit, error) { +func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath string, cache *LastCommitCache) ([]CommitInfo, *Commit, error) { entryPaths := make([]string, len(tes)+1) // Get the commit for the treePath itself entryPaths[0] = "" @@ -42,7 +43,7 @@ func (tes Entries) GetCommitsInfo(commit *Commit, treePath string, cache *LastCo return nil, nil, err } if len(unHitPaths) > 0 { - revs2, err := GetLastCommitForPaths(c, treePath, unHitPaths) + revs2, err := GetLastCommitForPaths(ctx, c, treePath, unHitPaths) if err != nil { return nil, nil, err } @@ -55,7 +56,7 @@ func (tes Entries) GetCommitsInfo(commit *Commit, treePath string, cache *LastCo } } } else { - revs, err = GetLastCommitForPaths(c, treePath, entryPaths) + revs, err = GetLastCommitForPaths(ctx, c, treePath, entryPaths) } if err != nil { return nil, nil, err @@ -173,7 +174,7 @@ func getLastCommitForPathsByCache(commitID, treePath string, paths []string, cac } // GetLastCommitForPaths returns last commit information -func GetLastCommitForPaths(c cgobject.CommitNode, treePath string, paths []string) (map[string]*object.Commit, error) { +func GetLastCommitForPaths(ctx context.Context, c cgobject.CommitNode, treePath string, paths []string) (map[string]*object.Commit, error) { // We do a tree traversal with nodes sorted by commit time heap := binaryheap.NewWith(func(a, b interface{}) int { if a.(*commitAndPaths).commit.CommitTime().Before(b.(*commitAndPaths).commit.CommitTime()) { @@ -192,6 +193,11 @@ func GetLastCommitForPaths(c cgobject.CommitNode, treePath string, paths []strin heap.Push(&commitAndPaths{c, paths, initialHashes}) for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } cIn, ok := heap.Pop() if !ok { break diff --git a/modules/git/commit_info_nogogit.go b/modules/git/commit_info_nogogit.go index 485271f145152..f34bef9f018c8 100644 --- a/modules/git/commit_info_nogogit.go +++ b/modules/git/commit_info_nogogit.go @@ -9,6 +9,7 @@ package git import ( "bufio" "bytes" + "context" "fmt" "io" "math" @@ -18,7 +19,7 @@ import ( ) // GetCommitsInfo gets information of all commits that are corresponding to these entries -func (tes Entries) GetCommitsInfo(commit *Commit, treePath string, cache *LastCommitCache) ([]CommitInfo, *Commit, error) { +func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath string, cache *LastCommitCache) ([]CommitInfo, *Commit, error) { entryPaths := make([]string, len(tes)+1) // Get the commit for the treePath itself entryPaths[0] = "" @@ -31,13 +32,13 @@ func (tes Entries) GetCommitsInfo(commit *Commit, treePath string, cache *LastCo var revs map[string]*Commit if cache != nil { var unHitPaths []string - revs, unHitPaths, err = getLastCommitForPathsByCache(commit.ID.String(), treePath, entryPaths, cache) + revs, unHitPaths, err = getLastCommitForPathsByCache(ctx, commit.ID.String(), treePath, entryPaths, cache) if err != nil { return nil, nil, err } if len(unHitPaths) > 0 { sort.Strings(unHitPaths) - commits, err := GetLastCommitForPaths(commit, treePath, unHitPaths) + commits, err := GetLastCommitForPaths(ctx, commit, treePath, unHitPaths) if err != nil { return nil, nil, err } @@ -53,7 +54,7 @@ func (tes Entries) GetCommitsInfo(commit *Commit, treePath string, cache *LastCo sort.Strings(entryPaths) revs = map[string]*Commit{} var foundCommits []*Commit - foundCommits, err = GetLastCommitForPaths(commit, treePath, entryPaths) + foundCommits, err = GetLastCommitForPaths(ctx, commit, treePath, entryPaths) for i, found := range foundCommits { revs[entryPaths[i]] = found } @@ -101,7 +102,7 @@ func (tes Entries) GetCommitsInfo(commit *Commit, treePath string, cache *LastCo return commitsInfo, treeCommit, nil } -func getLastCommitForPathsByCache(commitID, treePath string, paths []string, cache *LastCommitCache) (map[string]*Commit, []string, error) { +func getLastCommitForPathsByCache(ctx context.Context, commitID, treePath string, paths []string, cache *LastCommitCache) (map[string]*Commit, []string, error) { wr, rd, cancel := cache.repo.CatFileBatch() defer cancel() @@ -124,7 +125,7 @@ func getLastCommitForPathsByCache(commitID, treePath string, paths []string, cac } // GetLastCommitForPaths returns last commit information -func GetLastCommitForPaths(commit *Commit, treePath string, paths []string) ([]*Commit, error) { +func GetLastCommitForPaths(ctx context.Context, commit *Commit, treePath string, paths []string) ([]*Commit, error) { // We read backwards from the commit to obtain all of the commits // We'll do this by using rev-list to provide us with parent commits in order @@ -136,7 +137,7 @@ func GetLastCommitForPaths(commit *Commit, treePath string, paths []string) ([]* go func() { stderr := strings.Builder{} - err := NewCommand("rev-list", "--format=%T", commit.ID.String()).RunInDirPipeline(commit.repo.Path, revListWriter, &stderr) + err := NewCommand("rev-list", "--format=%T", commit.ID.String()).SetParentContext(ctx).RunInDirPipeline(commit.repo.Path, revListWriter, &stderr) if err != nil { _ = revListWriter.CloseWithError(ConcatenateError(err, (&stderr).String())) } else { @@ -202,6 +203,11 @@ revListLoop: treeReadingLoop: for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } _, _, size, err := ReadBatchLine(batchReader) if err != nil { return nil, err @@ -321,6 +327,9 @@ revListLoop: } } } + if scan.Err() != nil { + return nil, scan.Err() + } commitsMap := make(map[string]*Commit, len(commits)) commitsMap[commit.ID.String()] = commit diff --git a/modules/git/commit_info_test.go b/modules/git/commit_info_test.go index 3966419bc146f..0608801f9df45 100644 --- a/modules/git/commit_info_test.go +++ b/modules/git/commit_info_test.go @@ -5,6 +5,7 @@ package git import ( + "context" "os" "path/filepath" "testing" @@ -69,7 +70,7 @@ func testGetCommitsInfo(t *testing.T, repo1 *Repository) { assert.NoError(t, err) entries, err := tree.ListEntries() assert.NoError(t, err) - commitsInfo, treeCommit, err := entries.GetCommitsInfo(commit, testCase.Path, nil) + commitsInfo, treeCommit, err := entries.GetCommitsInfo(context.Background(), commit, testCase.Path, nil) assert.NoError(t, err) if err != nil { t.FailNow() @@ -136,7 +137,7 @@ func BenchmarkEntries_GetCommitsInfo(b *testing.B) { b.ResetTimer() b.Run(benchmark.name, func(b *testing.B) { for i := 0; i < b.N; i++ { - _, _, err := entries.GetCommitsInfo(commit, "", nil) + _, _, err := entries.GetCommitsInfo(context.Background(), commit, "", nil) if err != nil { b.Fatal(err) } diff --git a/modules/git/last_commit_cache_gogit.go b/modules/git/last_commit_cache_gogit.go index 65d6299bef387..16fb1c988cca8 100644 --- a/modules/git/last_commit_cache_gogit.go +++ b/modules/git/last_commit_cache_gogit.go @@ -7,6 +7,7 @@ package git import ( + "context" "path" "github.com/go-git/go-git/v5/plumbing/object" @@ -60,7 +61,7 @@ func (c *LastCommitCache) Get(ref, entryPath string) (interface{}, error) { } // CacheCommit will cache the commit from the gitRepository -func (c *LastCommitCache) CacheCommit(commit *Commit) error { +func (c *LastCommitCache) CacheCommit(ctx context.Context, commit *Commit) error { commitNodeIndex, _ := commit.repo.CommitNodeIndex() @@ -69,10 +70,10 @@ func (c *LastCommitCache) CacheCommit(commit *Commit) error { return err } - return c.recursiveCache(index, &commit.Tree, "", 1) + return c.recursiveCache(ctx, index, &commit.Tree, "", 1) } -func (c *LastCommitCache) recursiveCache(index cgobject.CommitNode, tree *Tree, treePath string, level int) error { +func (c *LastCommitCache) recursiveCache(ctx context.Context, index cgobject.CommitNode, tree *Tree, treePath string, level int) error { if level == 0 { return nil } @@ -89,7 +90,7 @@ func (c *LastCommitCache) recursiveCache(index cgobject.CommitNode, tree *Tree, entryMap[entry.Name()] = entry } - commits, err := GetLastCommitForPaths(index, treePath, entryPaths) + commits, err := GetLastCommitForPaths(ctx, index, treePath, entryPaths) if err != nil { return err } @@ -103,7 +104,7 @@ func (c *LastCommitCache) recursiveCache(index cgobject.CommitNode, tree *Tree, if err != nil { return err } - if err := c.recursiveCache(index, subTree, entry, level-1); err != nil { + if err := c.recursiveCache(ctx, index, subTree, entry, level-1); err != nil { return err } } diff --git a/modules/git/last_commit_cache_nogogit.go b/modules/git/last_commit_cache_nogogit.go index 9808216a8f870..3cbb0cca32e05 100644 --- a/modules/git/last_commit_cache_nogogit.go +++ b/modules/git/last_commit_cache_nogogit.go @@ -8,6 +8,7 @@ package git import ( "bufio" + "context" "path" ) @@ -61,11 +62,11 @@ func (c *LastCommitCache) Get(ref, entryPath string, wr WriteCloserError, rd *bu } // CacheCommit will cache the commit from the gitRepository -func (c *LastCommitCache) CacheCommit(commit *Commit) error { - return c.recursiveCache(commit, &commit.Tree, "", 1) +func (c *LastCommitCache) CacheCommit(ctx context.Context, commit *Commit) error { + return c.recursiveCache(ctx, commit, &commit.Tree, "", 1) } -func (c *LastCommitCache) recursiveCache(commit *Commit, tree *Tree, treePath string, level int) error { +func (c *LastCommitCache) recursiveCache(ctx context.Context, commit *Commit, tree *Tree, treePath string, level int) error { if level == 0 { return nil } @@ -82,7 +83,7 @@ func (c *LastCommitCache) recursiveCache(commit *Commit, tree *Tree, treePath st entryMap[entry.Name()] = entry } - commits, err := GetLastCommitForPaths(commit, treePath, entryPaths) + commits, err := GetLastCommitForPaths(ctx, commit, treePath, entryPaths) if err != nil { return err } @@ -97,7 +98,7 @@ func (c *LastCommitCache) recursiveCache(commit *Commit, tree *Tree, treePath st if err != nil { return err } - if err := c.recursiveCache(commit, subTree, entry, level-1); err != nil { + if err := c.recursiveCache(ctx, commit, subTree, entry, level-1); err != nil { return err } } diff --git a/modules/git/notes_gogit.go b/modules/git/notes_gogit.go index 173d29cee6b44..534a5d517153a 100644 --- a/modules/git/notes_gogit.go +++ b/modules/git/notes_gogit.go @@ -7,13 +7,14 @@ package git import ( + "context" "io/ioutil" "github.com/go-git/go-git/v5/plumbing/object" ) // GetNote retrieves the git-notes data for a given commit. -func GetNote(repo *Repository, commitID string, note *Note) error { +func GetNote(ctx context.Context, repo *Repository, commitID string, note *Note) error { notes, err := repo.GetCommit(NotesRef) if err != nil { return err @@ -62,7 +63,7 @@ func GetNote(repo *Repository, commitID string, note *Note) error { return err } - lastCommits, err := GetLastCommitForPaths(commitNode, "", []string{path}) + lastCommits, err := GetLastCommitForPaths(ctx, commitNode, "", []string{path}) if err != nil { return err } diff --git a/modules/git/notes_nogogit.go b/modules/git/notes_nogogit.go index d5d194b23f1b0..2b927249954a7 100644 --- a/modules/git/notes_nogogit.go +++ b/modules/git/notes_nogogit.go @@ -7,12 +7,13 @@ package git import ( + "context" "io/ioutil" "strings" ) // GetNote retrieves the git-notes data for a given commit. -func GetNote(repo *Repository, commitID string, note *Note) error { +func GetNote(ctx context.Context, repo *Repository, commitID string, note *Note) error { notes, err := repo.GetCommit(NotesRef) if err != nil { return err @@ -63,7 +64,7 @@ func GetNote(repo *Repository, commitID string, note *Note) error { path = path[idx+1:] } - lastCommits, err := GetLastCommitForPaths(notes, treePath, []string{path}) + lastCommits, err := GetLastCommitForPaths(ctx, notes, treePath, []string{path}) if err != nil { return err } diff --git a/modules/git/notes_test.go b/modules/git/notes_test.go index b7939e691355a..f66a191e6ae27 100644 --- a/modules/git/notes_test.go +++ b/modules/git/notes_test.go @@ -5,6 +5,7 @@ package git import ( + "context" "path/filepath" "testing" @@ -18,7 +19,7 @@ func TestGetNotes(t *testing.T) { defer bareRepo1.Close() note := Note{} - err = GetNote(bareRepo1, "95bb4d39648ee7e325106df01a621c530863a653", ¬e) + err = GetNote(context.Background(), bareRepo1, "95bb4d39648ee7e325106df01a621c530863a653", ¬e) assert.NoError(t, err) assert.Equal(t, []byte("Note contents\n"), note.Message) assert.Equal(t, "Vladimir Panteleev", note.Commit.Author.Name) @@ -31,10 +32,10 @@ func TestGetNestedNotes(t *testing.T) { defer repo.Close() note := Note{} - err = GetNote(repo, "3e668dbfac39cbc80a9ff9c61eb565d944453ba4", ¬e) + err = GetNote(context.Background(), repo, "3e668dbfac39cbc80a9ff9c61eb565d944453ba4", ¬e) assert.NoError(t, err) assert.Equal(t, []byte("Note 2"), note.Message) - err = GetNote(repo, "ba0a96fa63532d6c5087ecef070b0250ed72fa47", ¬e) + err = GetNote(context.Background(), repo, "ba0a96fa63532d6c5087ecef070b0250ed72fa47", ¬e) assert.NoError(t, err) assert.Equal(t, []byte("Note 1"), note.Message) } diff --git a/modules/git/parse_nogogit_test.go b/modules/git/parse_nogogit_test.go index a9e7dcc7f8b15..502c38d4e8d9f 100644 --- a/modules/git/parse_nogogit_test.go +++ b/modules/git/parse_nogogit_test.go @@ -58,7 +58,7 @@ func TestParseTreeEntries(t *testing.T) { for _, testCase := range testCases { entries, err := ParseTreeEntries([]byte(testCase.Input)) assert.NoError(t, err) - assert.EqualValues(t, len(testCase.Expected), len(entries)) + assert.Len(t, entries, len(testCase.Expected)) for i, entry := range entries { assert.EqualValues(t, testCase.Expected[i].ID, entry.ID) assert.EqualValues(t, testCase.Expected[i].name, entry.name) diff --git a/modules/git/remote.go b/modules/git/remote.go new file mode 100644 index 0000000000000..7ba2b35a5ed32 --- /dev/null +++ b/modules/git/remote.go @@ -0,0 +1,31 @@ +// 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 git + +import "net/url" + +// GetRemoteAddress returns the url of a specific remote of the repository. +func GetRemoteAddress(repoPath, remoteName string) (*url.URL, error) { + err := LoadGitVersion() + if err != nil { + return nil, err + } + var cmd *Command + if CheckGitVersionAtLeast("2.7") == nil { + cmd = NewCommand("remote", "get-url", remoteName) + } else { + cmd = NewCommand("config", "--get", "remote."+remoteName+".url") + } + + result, err := cmd.RunInDir(repoPath) + if err != nil { + return nil, err + } + + if len(result) > 0 { + result = result[:len(result)-1] + } + return url.Parse(result) +} diff --git a/modules/git/repo.go b/modules/git/repo.go index 515899ab04984..e06cd439353b3 100644 --- a/modules/git/repo.go +++ b/modules/git/repo.go @@ -182,10 +182,12 @@ func Pull(repoPath string, opts PullRemoteOptions) error { // PushOptions options when push to remote type PushOptions struct { - Remote string - Branch string - Force bool - Env []string + Remote string + Branch string + Force bool + Mirror bool + Env []string + Timeout time.Duration } // Push pushs local commits to given remote branch. @@ -194,10 +196,20 @@ func Push(repoPath string, opts PushOptions) error { if opts.Force { cmd.AddArguments("-f") } - cmd.AddArguments("--", opts.Remote, opts.Branch) + if opts.Mirror { + cmd.AddArguments("--mirror") + } + cmd.AddArguments("--", opts.Remote) + if len(opts.Branch) > 0 { + cmd.AddArguments(opts.Branch) + } var outbuf, errbuf strings.Builder - err := cmd.RunInDirTimeoutEnvPipeline(opts.Env, -1, repoPath, &outbuf, &errbuf) + if opts.Timeout == 0 { + opts.Timeout = -1 + } + + err := cmd.RunInDirTimeoutEnvPipeline(opts.Env, opts.Timeout, repoPath, &outbuf, &errbuf) if err != nil { if strings.Contains(errbuf.String(), "non-fast-forward") { return &ErrPushOutOfDate{ diff --git a/modules/git/repo_commit_test.go b/modules/git/repo_commit_test.go index 3eedaa6b6e9b2..8f8acbdfed67e 100644 --- a/modules/git/repo_commit_test.go +++ b/modules/git/repo_commit_test.go @@ -72,9 +72,9 @@ func TestIsCommitInBranch(t *testing.T) { result, err := bareRepo1.IsCommitInBranch("2839944139e0de9737a044f78b0e4b40d989a9e3", "branch1") assert.NoError(t, err) - assert.Equal(t, true, result) + assert.True(t, result) result, err = bareRepo1.IsCommitInBranch("2839944139e0de9737a044f78b0e4b40d989a9e3", "branch2") assert.NoError(t, err) - assert.Equal(t, false, result) + assert.False(t, result) } diff --git a/modules/git/repo_language_stats_nogogit.go b/modules/git/repo_language_stats_nogogit.go index 0130d0a300871..abbf5e943ba43 100644 --- a/modules/git/repo_language_stats_nogogit.go +++ b/modules/git/repo_language_stats_nogogit.go @@ -25,11 +25,7 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err defer cancel() writeID := func(id string) error { - _, err := batchStdinWriter.Write([]byte(id)) - if err != nil { - return err - } - _, err = batchStdinWriter.Write([]byte{'\n'}) + _, err := batchStdinWriter.Write([]byte(id + "\n")) return err } @@ -85,10 +81,10 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err } sizeToRead := size - discard := int64(0) + discard := int64(1) if size > fileSizeLimit { sizeToRead = fileSizeLimit - discard = size - fileSizeLimit + discard = size - fileSizeLimit + 1 } _, err = contentBuf.ReadFrom(io.LimitReader(batchReader, sizeToRead)) diff --git a/modules/git/tree_entry_test.go b/modules/git/tree_entry_test.go index 16cfbc4fc3adf..3382de41028d6 100644 --- a/modules/git/tree_entry_test.go +++ b/modules/git/tree_entry_test.go @@ -71,33 +71,33 @@ func TestFollowLink(t *testing.T) { // should be able to dereference to target target, err := lnk.FollowLink() assert.NoError(t, err) - assert.Equal(t, target.Name(), "hello") + assert.Equal(t, "hello", target.Name()) assert.False(t, target.IsLink()) - assert.Equal(t, target.ID.String(), "b14df6442ea5a1b382985a6549b85d435376c351") + assert.Equal(t, "b14df6442ea5a1b382985a6549b85d435376c351", target.ID.String()) // should error when called on normal file target, err = commit.Tree.GetTreeEntryByPath("file1.txt") assert.NoError(t, err) _, err = target.FollowLink() - assert.Equal(t, err.Error(), "file1.txt: not a symlink") + assert.EqualError(t, err, "file1.txt: not a symlink") // should error for broken links target, err = commit.Tree.GetTreeEntryByPath("foo/broken_link") assert.NoError(t, err) assert.True(t, target.IsLink()) _, err = target.FollowLink() - assert.Equal(t, err.Error(), "broken_link: broken link") + assert.EqualError(t, err, "broken_link: broken link") // should error for external links target, err = commit.Tree.GetTreeEntryByPath("foo/outside_repo") assert.NoError(t, err) assert.True(t, target.IsLink()) _, err = target.FollowLink() - assert.Equal(t, err.Error(), "outside_repo: points outside of repo") + assert.EqualError(t, err, "outside_repo: points outside of repo") // testing fix for short link bug target, err = commit.Tree.GetTreeEntryByPath("foo/link_short") assert.NoError(t, err) _, err = target.FollowLink() - assert.Equal(t, err.Error(), "link_short: broken link") + assert.EqualError(t, err, "link_short: broken link") } diff --git a/modules/graceful/server.go b/modules/graceful/server.go index 26195c63227d2..704aa8a2b71f1 100644 --- a/modules/graceful/server.go +++ b/modules/graceful/server.go @@ -17,6 +17,7 @@ import ( "time" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" ) var ( @@ -26,11 +27,12 @@ var ( DefaultWriteTimeOut time.Duration // DefaultMaxHeaderBytes default max header bytes DefaultMaxHeaderBytes int + // PerWriteWriteTimeout timeout for writes + PerWriteWriteTimeout = 30 * time.Second + // PerWriteWriteTimeoutKbTime is a timeout taking account of how much there is to be written + PerWriteWriteTimeoutKbTime = 10 * time.Second ) -// PerWriteWriteTimeout timeout for writes -const PerWriteWriteTimeout = 5 * time.Second - func init() { DefaultMaxHeaderBytes = 0 // use http.DefaultMaxHeaderBytes - which currently is 1 << 20 (1MB) } @@ -40,14 +42,16 @@ type ServeFunction = func(net.Listener) error // Server represents our graceful server type Server struct { - network string - address string - listener net.Listener - wg sync.WaitGroup - state state - lock *sync.RWMutex - BeforeBegin func(network, address string) - OnShutdown func() + network string + address string + listener net.Listener + wg sync.WaitGroup + state state + lock *sync.RWMutex + BeforeBegin func(network, address string) + OnShutdown func() + PerWriteTimeout time.Duration + PerWritePerKbTimeout time.Duration } // NewServer creates a server on network at provided address @@ -58,11 +62,13 @@ func NewServer(network, address, name string) *Server { log.Info("Starting new %s server: %s:%s on PID: %d", name, network, address, os.Getpid()) } srv := &Server{ - wg: sync.WaitGroup{}, - state: stateInit, - lock: &sync.RWMutex{}, - network: network, - address: address, + wg: sync.WaitGroup{}, + state: stateInit, + lock: &sync.RWMutex{}, + network: network, + address: address, + PerWriteTimeout: setting.PerWriteTimeout, + PerWritePerKbTimeout: setting.PerWritePerKbTimeout, } srv.BeforeBegin = func(network, addr string) { @@ -224,9 +230,11 @@ func (wl *wrappedListener) Accept() (net.Conn, error) { closed := int32(0) c = wrappedConn{ - Conn: c, - server: wl.server, - closed: &closed, + Conn: c, + server: wl.server, + closed: &closed, + perWriteTimeout: wl.server.PerWriteTimeout, + perWritePerKbTimeout: wl.server.PerWritePerKbTimeout, } wl.server.wg.Add(1) @@ -249,13 +257,23 @@ func (wl *wrappedListener) File() (*os.File, error) { type wrappedConn struct { net.Conn - server *Server - closed *int32 + server *Server + closed *int32 + deadline time.Time + perWriteTimeout time.Duration + perWritePerKbTimeout time.Duration } func (w wrappedConn) Write(p []byte) (n int, err error) { - if PerWriteWriteTimeout > 0 { - _ = w.Conn.SetWriteDeadline(time.Now().Add(PerWriteWriteTimeout)) + if w.perWriteTimeout > 0 { + minTimeout := time.Duration(len(p)/1024) * w.perWritePerKbTimeout + minDeadline := time.Now().Add(minTimeout).Add(w.perWriteTimeout) + + w.deadline = w.deadline.Add(minTimeout) + if minDeadline.After(w.deadline) { + w.deadline = minDeadline + } + _ = w.Conn.SetWriteDeadline(w.deadline) } return w.Conn.Write(p) } diff --git a/modules/highlight/highlight.go b/modules/highlight/highlight.go index a46499691e570..e22e9d5b3266a 100644 --- a/modules/highlight/highlight.go +++ b/modules/highlight/highlight.go @@ -8,6 +8,7 @@ package highlight import ( "bufio" "bytes" + "fmt" gohtml "html" "path/filepath" "strings" @@ -20,6 +21,7 @@ import ( "github.com/alecthomas/chroma/formatters/html" "github.com/alecthomas/chroma/lexers" "github.com/alecthomas/chroma/styles" + lru "github.com/hashicorp/golang-lru" ) // don't index files larger than this many bytes for performance purposes @@ -30,6 +32,8 @@ var ( highlightMapping = map[string]string{} once sync.Once + + cache *lru.ARCCache ) // NewContext loads custom highlight map from local config @@ -39,6 +43,13 @@ func NewContext() { for i := range keys { highlightMapping[keys[i].Name()] = keys[i].Value() } + + // The size 512 is simply a conservative rule of thumb + c, err := lru.NewARC(512) + if err != nil { + panic(fmt.Sprintf("failed to initialize LRU cache for highlighter: %s", err)) + } + cache = c }) } @@ -73,11 +84,18 @@ func Code(fileName, code string) string { lexer = lexers.Get(val) } + if lexer == nil { + if l, ok := cache.Get(fileName); ok { + lexer = l.(chroma.Lexer) + } + } + if lexer == nil { lexer = lexers.Match(fileName) if lexer == nil { lexer = lexers.Fallback } + cache.Add(fileName, lexer) } iterator, err := lexer.Tokenise(nil, string(code)) diff --git a/modules/indexer/code/bleve.go b/modules/indexer/code/bleve.go index 1d6aa51bc2213..17128052f4912 100644 --- a/modules/indexer/code/bleve.go +++ b/modules/indexer/code/bleve.go @@ -16,12 +16,12 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/analyze" - "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/typesniffer" "code.gitea.io/gitea/modules/util" "github.com/blevesearch/bleve/v2" @@ -211,7 +211,7 @@ func (b *BleveIndexer) addUpdate(batchWriter git.WriteCloserError, batchReader * fileContents, err := ioutil.ReadAll(io.LimitReader(batchReader, size)) if err != nil { return err - } else if !base.IsTextFile(fileContents) { + } else if !typesniffer.DetectContentType(fileContents).IsText() { // FIXME: UTF-16 files will probably fail here return nil } diff --git a/modules/indexer/code/elastic_search.go b/modules/indexer/code/elastic_search.go index 982b36e8df404..16d4a1821a2f7 100644 --- a/modules/indexer/code/elastic_search.go +++ b/modules/indexer/code/elastic_search.go @@ -16,12 +16,12 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/analyze" - "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/typesniffer" "github.com/go-enry/go-enry/v2" jsoniter "github.com/json-iterator/go" @@ -210,7 +210,7 @@ func (b *ElasticSearchIndexer) addUpdate(batchWriter git.WriteCloserError, batch fileContents, err := ioutil.ReadAll(io.LimitReader(batchReader, size)) if err != nil { return nil, err - } else if !base.IsTextFile(fileContents) { + } else if !typesniffer.DetectContentType(fileContents).IsText() { // FIXME: UTF-16 files will probably fail here return nil, nil } diff --git a/modules/indexer/code/indexer_test.go b/modules/indexer/code/indexer_test.go index 8fcb7a0e8a677..01717bd288a75 100644 --- a/modules/indexer/code/indexer_test.go +++ b/modules/indexer/code/indexer_test.go @@ -67,7 +67,7 @@ func testIndexer(name string, t *testing.T, indexer Indexer) { total, res, langs, err := indexer.Search(kw.RepoIDs, "", kw.Keyword, 1, 10, false) assert.NoError(t, err) assert.EqualValues(t, len(kw.IDs), total) - assert.EqualValues(t, kw.Langs, len(langs)) + assert.Len(t, langs, kw.Langs) var ids = make([]int64, 0, len(res)) for _, hit := range res { diff --git a/modules/lfs/client.go b/modules/lfs/client.go index ae35919d770b5..0a21440f73d09 100644 --- a/modules/lfs/client.go +++ b/modules/lfs/client.go @@ -10,9 +10,17 @@ import ( "net/url" ) +// DownloadCallback gets called for every requested LFS object to process its content +type DownloadCallback func(p Pointer, content io.ReadCloser, objectError error) error + +// UploadCallback gets called for every requested LFS object to provide its content +type UploadCallback func(p Pointer, objectError error) (io.ReadCloser, error) + // Client is used to communicate with a LFS source type Client interface { - Download(ctx context.Context, oid string, size int64) (io.ReadCloser, error) + BatchSize() int + Download(ctx context.Context, objects []Pointer, callback DownloadCallback) error + Upload(ctx context.Context, objects []Pointer, callback UploadCallback) error } // NewClient creates a LFS client diff --git a/modules/lfs/client_test.go b/modules/lfs/client_test.go index d4eb00546948c..1040b39925606 100644 --- a/modules/lfs/client_test.go +++ b/modules/lfs/client_test.go @@ -6,7 +6,6 @@ package lfs import ( "net/url" - "testing" "github.com/stretchr/testify/assert" diff --git a/modules/lfs/filesystem_client.go b/modules/lfs/filesystem_client.go index 3a51564a821b8..dc72981a9ec9f 100644 --- a/modules/lfs/filesystem_client.go +++ b/modules/lfs/filesystem_client.go @@ -19,6 +19,11 @@ type FilesystemClient struct { lfsdir string } +// BatchSize returns the preferred size of batchs to process +func (c *FilesystemClient) BatchSize() int { + return 1 +} + func newFilesystemClient(endpoint *url.URL) *FilesystemClient { path, _ := util.FileURLToPath(endpoint) @@ -33,18 +38,56 @@ func (c *FilesystemClient) objectPath(oid string) string { return filepath.Join(c.lfsdir, oid[0:2], oid[2:4], oid) } -// Download reads the specific LFS object from the target repository -func (c *FilesystemClient) Download(ctx context.Context, oid string, size int64) (io.ReadCloser, error) { - objectPath := c.objectPath(oid) +// Download reads the specific LFS object from the target path +func (c *FilesystemClient) Download(ctx context.Context, objects []Pointer, callback DownloadCallback) error { + for _, object := range objects { + p := Pointer{object.Oid, object.Size} - if _, err := os.Stat(objectPath); os.IsNotExist(err) { - return nil, err - } + objectPath := c.objectPath(p.Oid) + + f, err := os.Open(objectPath) + if err != nil { + return err + } - file, err := os.Open(objectPath) - if err != nil { - return nil, err + if err := callback(p, f, nil); err != nil { + return err + } } + return nil +} + +// Upload writes the specific LFS object to the target path +func (c *FilesystemClient) Upload(ctx context.Context, objects []Pointer, callback UploadCallback) error { + for _, object := range objects { + p := Pointer{object.Oid, object.Size} + + objectPath := c.objectPath(p.Oid) - return file, nil + if err := os.MkdirAll(filepath.Dir(objectPath), os.ModePerm); err != nil { + return err + } + + content, err := callback(p, nil) + if err != nil { + return err + } + + err = func() error { + defer content.Close() + + f, err := os.Create(objectPath) + if err != nil { + return err + } + + _, err = io.Copy(f, content) + + return err + }() + if err != nil { + return err + } + } + return nil } diff --git a/modules/lfs/http_client.go b/modules/lfs/http_client.go index fb45defda1cfd..e799b80831ead 100644 --- a/modules/lfs/http_client.go +++ b/modules/lfs/http_client.go @@ -7,17 +7,19 @@ package lfs import ( "bytes" "context" - "encoding/json" "errors" "fmt" - "io" "net/http" "net/url" "strings" "code.gitea.io/gitea/modules/log" + + jsoniter "github.com/json-iterator/go" ) +const batchSize = 20 + // HTTPClient is used to communicate with the LFS server // https://github.com/git-lfs/git-lfs/blob/main/docs/api/batch.md type HTTPClient struct { @@ -26,6 +28,11 @@ type HTTPClient struct { transfers map[string]TransferAdapter } +// BatchSize returns the preferred size of batchs to process +func (c *HTTPClient) BatchSize() int { + return batchSize +} + func newHTTPClient(endpoint *url.URL) *HTTPClient { hc := &http.Client{} @@ -55,21 +62,25 @@ func (c *HTTPClient) transferNames() []string { } func (c *HTTPClient) batch(ctx context.Context, operation string, objects []Pointer) (*BatchResponse, error) { + log.Trace("BATCH operation with objects: %v", objects) + url := fmt.Sprintf("%s/objects/batch", c.endpoint) request := &BatchRequest{operation, c.transferNames(), nil, objects} payload := new(bytes.Buffer) - err := json.NewEncoder(payload).Encode(request) + err := jsoniter.NewEncoder(payload).Encode(request) if err != nil { - return nil, fmt.Errorf("lfs.HTTPClient.batch json.Encode: %w", err) + log.Error("Error encoding json: %v", err) + return nil, err } - log.Trace("lfs.HTTPClient.batch NewRequestWithContext: %s", url) + log.Trace("Calling: %s", url) req, err := http.NewRequestWithContext(ctx, "POST", url, payload) if err != nil { - return nil, fmt.Errorf("lfs.HTTPClient.batch http.NewRequestWithContext: %w", err) + log.Error("Error creating request: %v", err) + return nil, err } req.Header.Set("Content-type", MediaType) req.Header.Set("Accept", MediaType) @@ -81,18 +92,20 @@ func (c *HTTPClient) batch(ctx context.Context, operation string, objects []Poin return nil, ctx.Err() default: } - return nil, fmt.Errorf("lfs.HTTPClient.batch http.Do: %w", err) + log.Error("Error while processing request: %v", err) + return nil, err } defer res.Body.Close() if res.StatusCode != http.StatusOK { - return nil, fmt.Errorf("lfs.HTTPClient.batch: Unexpected servers response: %s", res.Status) + return nil, fmt.Errorf("Unexpected server response: %s", res.Status) } var response BatchResponse - err = json.NewDecoder(res.Body).Decode(&response) + err = jsoniter.NewDecoder(res.Body).Decode(&response) if err != nil { - return nil, fmt.Errorf("lfs.HTTPClient.batch json.Decode: %w", err) + log.Error("Error decoding json: %v", err) + return nil, err } if len(response.Transfer) == 0 { @@ -103,27 +116,99 @@ func (c *HTTPClient) batch(ctx context.Context, operation string, objects []Poin } // Download reads the specific LFS object from the LFS server -func (c *HTTPClient) Download(ctx context.Context, oid string, size int64) (io.ReadCloser, error) { - var objects []Pointer - objects = append(objects, Pointer{oid, size}) +func (c *HTTPClient) Download(ctx context.Context, objects []Pointer, callback DownloadCallback) error { + return c.performOperation(ctx, objects, callback, nil) +} + +// Upload sends the specific LFS object to the LFS server +func (c *HTTPClient) Upload(ctx context.Context, objects []Pointer, callback UploadCallback) error { + return c.performOperation(ctx, objects, nil, callback) +} - result, err := c.batch(ctx, "download", objects) +func (c *HTTPClient) performOperation(ctx context.Context, objects []Pointer, dc DownloadCallback, uc UploadCallback) error { + if len(objects) == 0 { + return nil + } + + operation := "download" + if uc != nil { + operation = "upload" + } + + result, err := c.batch(ctx, operation, objects) if err != nil { - return nil, err + return err } transferAdapter, ok := c.transfers[result.Transfer] if !ok { - return nil, fmt.Errorf("lfs.HTTPClient.Download Transferadapter not found: %s", result.Transfer) + return fmt.Errorf("TransferAdapter not found: %s", result.Transfer) } - if len(result.Objects) == 0 { - return nil, errors.New("lfs.HTTPClient.Download: No objects in result") - } + for _, object := range result.Objects { + if object.Error != nil { + objectError := errors.New(object.Error.Message) + log.Trace("Error on object %v: %v", object.Pointer, objectError) + if uc != nil { + if _, err := uc(object.Pointer, objectError); err != nil { + return err + } + } else { + if err := dc(object.Pointer, nil, objectError); err != nil { + return err + } + } + continue + } - content, err := transferAdapter.Download(ctx, result.Objects[0]) - if err != nil { - return nil, err + if uc != nil { + if len(object.Actions) == 0 { + log.Trace("%v already present on server", object.Pointer) + continue + } + + link, ok := object.Actions["upload"] + if !ok { + log.Debug("%+v", object) + return errors.New("Missing action 'upload'") + } + + content, err := uc(object.Pointer, nil) + if err != nil { + return err + } + + err = transferAdapter.Upload(ctx, link, object.Pointer, content) + + content.Close() + + if err != nil { + return err + } + + link, ok = object.Actions["verify"] + if ok { + if err := transferAdapter.Verify(ctx, link, object.Pointer); err != nil { + return err + } + } + } else { + link, ok := object.Actions["download"] + if !ok { + log.Debug("%+v", object) + return errors.New("Missing action 'download'") + } + + content, err := transferAdapter.Download(ctx, link) + if err != nil { + return err + } + + if err := dc(object.Pointer, content, nil); err != nil { + return err + } + } } - return content, nil + + return nil } diff --git a/modules/lfs/http_client_test.go b/modules/lfs/http_client_test.go index 043aa0214e862..0f633ede54cde 100644 --- a/modules/lfs/http_client_test.go +++ b/modules/lfs/http_client_test.go @@ -7,13 +7,13 @@ package lfs import ( "bytes" "context" - "encoding/json" "io" "io/ioutil" "net/http" "strings" "testing" + jsoniter "github.com/json-iterator/go" "github.com/stretchr/testify/assert" ) @@ -30,69 +30,253 @@ func (a *DummyTransferAdapter) Name() string { return "dummy" } -func (a *DummyTransferAdapter) Download(ctx context.Context, r *ObjectResponse) (io.ReadCloser, error) { +func (a *DummyTransferAdapter) Download(ctx context.Context, l *Link) (io.ReadCloser, error) { return ioutil.NopCloser(bytes.NewBufferString("dummy")), nil } -func TestHTTPClientDownload(t *testing.T) { - oid := "fb8f7d8435968c4f82a726a92395be4d16f2f63116caf36c8ad35c60831ab041" - size := int64(6) +func (a *DummyTransferAdapter) Upload(ctx context.Context, l *Link, p Pointer, r io.Reader) error { + return nil +} + +func (a *DummyTransferAdapter) Verify(ctx context.Context, l *Link, p Pointer) error { + return nil +} + +func lfsTestRoundtripHandler(req *http.Request) *http.Response { + var batchResponse *BatchResponse + url := req.URL.String() - roundTripHandler := func(req *http.Request) *http.Response { - url := req.URL.String() - if strings.Contains(url, "status-not-ok") { - return &http.Response{StatusCode: http.StatusBadRequest} + if strings.Contains(url, "status-not-ok") { + return &http.Response{StatusCode: http.StatusBadRequest} + } else if strings.Contains(url, "invalid-json-response") { + return &http.Response{StatusCode: http.StatusOK, Body: ioutil.NopCloser(bytes.NewBufferString("invalid json"))} + } else if strings.Contains(url, "valid-batch-request-download") { + batchResponse = &BatchResponse{ + Transfer: "dummy", + Objects: []*ObjectResponse{ + { + Actions: map[string]*Link{ + "download": {}, + }, + }, + }, + } + } else if strings.Contains(url, "valid-batch-request-upload") { + batchResponse = &BatchResponse{ + Transfer: "dummy", + Objects: []*ObjectResponse{ + { + Actions: map[string]*Link{ + "upload": {}, + }, + }, + }, } - if strings.Contains(url, "invalid-json-response") { - return &http.Response{StatusCode: http.StatusOK, Body: ioutil.NopCloser(bytes.NewBufferString("invalid json"))} + } else if strings.Contains(url, "response-no-objects") { + batchResponse = &BatchResponse{Transfer: "dummy"} + } else if strings.Contains(url, "unknown-transfer-adapter") { + batchResponse = &BatchResponse{Transfer: "unknown_adapter"} + } else if strings.Contains(url, "error-in-response-objects") { + batchResponse = &BatchResponse{ + Transfer: "dummy", + Objects: []*ObjectResponse{ + { + Error: &ObjectError{ + Code: 404, + Message: "Object not found", + }, + }, + }, } - if strings.Contains(url, "valid-batch-request-download") { - assert.Equal(t, "POST", req.Method) - assert.Equal(t, MediaType, req.Header.Get("Content-type"), "case %s: error should match", url) - assert.Equal(t, MediaType, req.Header.Get("Accept"), "case %s: error should match", url) + } else if strings.Contains(url, "empty-actions-map") { + batchResponse = &BatchResponse{ + Transfer: "dummy", + Objects: []*ObjectResponse{ + { + Actions: map[string]*Link{}, + }, + }, + } + } else if strings.Contains(url, "download-actions-map") { + batchResponse = &BatchResponse{ + Transfer: "dummy", + Objects: []*ObjectResponse{ + { + Actions: map[string]*Link{ + "download": {}, + }, + }, + }, + } + } else if strings.Contains(url, "upload-actions-map") { + batchResponse = &BatchResponse{ + Transfer: "dummy", + Objects: []*ObjectResponse{ + { + Actions: map[string]*Link{ + "upload": {}, + }, + }, + }, + } + } else if strings.Contains(url, "verify-actions-map") { + batchResponse = &BatchResponse{ + Transfer: "dummy", + Objects: []*ObjectResponse{ + { + Actions: map[string]*Link{ + "verify": {}, + }, + }, + }, + } + } else if strings.Contains(url, "unknown-actions-map") { + batchResponse = &BatchResponse{ + Transfer: "dummy", + Objects: []*ObjectResponse{ + { + Actions: map[string]*Link{ + "unknown": {}, + }, + }, + }, + } + } else { + return nil + } - var batchRequest BatchRequest - err := json.NewDecoder(req.Body).Decode(&batchRequest) - assert.NoError(t, err) + payload := new(bytes.Buffer) + jsoniter.NewEncoder(payload).Encode(batchResponse) - assert.Equal(t, "download", batchRequest.Operation) - assert.Equal(t, 1, len(batchRequest.Objects)) - assert.Equal(t, oid, batchRequest.Objects[0].Oid) - assert.Equal(t, size, batchRequest.Objects[0].Size) + return &http.Response{StatusCode: http.StatusOK, Body: ioutil.NopCloser(payload)} +} - batchResponse := &BatchResponse{ - Transfer: "dummy", - Objects: make([]*ObjectResponse, 1), - } +func TestHTTPClientDownload(t *testing.T) { + p := Pointer{Oid: "fb8f7d8435968c4f82a726a92395be4d16f2f63116caf36c8ad35c60831ab041", Size: 6} - payload := new(bytes.Buffer) - json.NewEncoder(payload).Encode(batchResponse) + hc := &http.Client{Transport: RoundTripFunc(func(req *http.Request) *http.Response { + assert.Equal(t, "POST", req.Method) + assert.Equal(t, MediaType, req.Header.Get("Content-type")) + assert.Equal(t, MediaType, req.Header.Get("Accept")) - return &http.Response{StatusCode: http.StatusOK, Body: ioutil.NopCloser(payload)} - } - if strings.Contains(url, "invalid-response-no-objects") { - batchResponse := &BatchResponse{Transfer: "dummy"} + var batchRequest BatchRequest + err := jsoniter.NewDecoder(req.Body).Decode(&batchRequest) + assert.NoError(t, err) - payload := new(bytes.Buffer) - json.NewEncoder(payload).Encode(batchResponse) + assert.Equal(t, "download", batchRequest.Operation) + assert.Equal(t, 1, len(batchRequest.Objects)) + assert.Equal(t, p.Oid, batchRequest.Objects[0].Oid) + assert.Equal(t, p.Size, batchRequest.Objects[0].Size) - return &http.Response{StatusCode: http.StatusOK, Body: ioutil.NopCloser(payload)} - } - if strings.Contains(url, "unknown-transfer-adapter") { - batchResponse := &BatchResponse{Transfer: "unknown_adapter"} + return lfsTestRoundtripHandler(req) + })} + dummy := &DummyTransferAdapter{} - payload := new(bytes.Buffer) - json.NewEncoder(payload).Encode(batchResponse) + var cases = []struct { + endpoint string + expectederror string + }{ + // case 0 + { + endpoint: "https://status-not-ok.io", + expectederror: "Unexpected server response: ", + }, + // case 1 + { + endpoint: "https://invalid-json-response.io", + expectederror: "invalid json", + }, + // case 2 + { + endpoint: "https://valid-batch-request-download.io", + expectederror: "", + }, + // case 3 + { + endpoint: "https://response-no-objects.io", + expectederror: "", + }, + // case 4 + { + endpoint: "https://unknown-transfer-adapter.io", + expectederror: "TransferAdapter not found: ", + }, + // case 5 + { + endpoint: "https://error-in-response-objects.io", + expectederror: "Object not found", + }, + // case 6 + { + endpoint: "https://empty-actions-map.io", + expectederror: "Missing action 'download'", + }, + // case 7 + { + endpoint: "https://download-actions-map.io", + expectederror: "", + }, + // case 8 + { + endpoint: "https://upload-actions-map.io", + expectederror: "Missing action 'download'", + }, + // case 9 + { + endpoint: "https://verify-actions-map.io", + expectederror: "Missing action 'download'", + }, + // case 10 + { + endpoint: "https://unknown-actions-map.io", + expectederror: "Missing action 'download'", + }, + } - return &http.Response{StatusCode: http.StatusOK, Body: ioutil.NopCloser(payload)} + for n, c := range cases { + client := &HTTPClient{ + client: hc, + endpoint: c.endpoint, + transfers: make(map[string]TransferAdapter), } + client.transfers["dummy"] = dummy - t.Errorf("Unknown test case: %s", url) - - return nil + err := client.Download(context.Background(), []Pointer{p}, func(p Pointer, content io.ReadCloser, objectError error) error { + if objectError != nil { + return objectError + } + b, err := io.ReadAll(content) + assert.NoError(t, err) + assert.Equal(t, []byte("dummy"), b) + return nil + }) + if len(c.expectederror) > 0 { + assert.True(t, strings.Contains(err.Error(), c.expectederror), "case %d: '%s' should contain '%s'", n, err.Error(), c.expectederror) + } else { + assert.NoError(t, err, "case %d", n) + } } +} + +func TestHTTPClientUpload(t *testing.T) { + p := Pointer{Oid: "fb8f7d8435968c4f82a726a92395be4d16f2f63116caf36c8ad35c60831ab041", Size: 6} + + hc := &http.Client{Transport: RoundTripFunc(func(req *http.Request) *http.Response { + assert.Equal(t, "POST", req.Method) + assert.Equal(t, MediaType, req.Header.Get("Content-type")) + assert.Equal(t, MediaType, req.Header.Get("Accept")) + + var batchRequest BatchRequest + err := jsoniter.NewDecoder(req.Body).Decode(&batchRequest) + assert.NoError(t, err) - hc := &http.Client{Transport: RoundTripFunc(roundTripHandler)} + assert.Equal(t, "upload", batchRequest.Operation) + assert.Equal(t, 1, len(batchRequest.Objects)) + assert.Equal(t, p.Oid, batchRequest.Objects[0].Oid) + assert.Equal(t, p.Size, batchRequest.Objects[0].Size) + + return lfsTestRoundtripHandler(req) + })} dummy := &DummyTransferAdapter{} var cases = []struct { @@ -102,27 +286,57 @@ func TestHTTPClientDownload(t *testing.T) { // case 0 { endpoint: "https://status-not-ok.io", - expectederror: "Unexpected servers response: ", + expectederror: "Unexpected server response: ", }, // case 1 { endpoint: "https://invalid-json-response.io", - expectederror: "json.Decode: ", + expectederror: "invalid json", }, // case 2 { - endpoint: "https://valid-batch-request-download.io", + endpoint: "https://valid-batch-request-upload.io", expectederror: "", }, // case 3 { - endpoint: "https://invalid-response-no-objects.io", - expectederror: "No objects in result", + endpoint: "https://response-no-objects.io", + expectederror: "", }, // case 4 { endpoint: "https://unknown-transfer-adapter.io", - expectederror: "Transferadapter not found: ", + expectederror: "TransferAdapter not found: ", + }, + // case 5 + { + endpoint: "https://error-in-response-objects.io", + expectederror: "Object not found", + }, + // case 6 + { + endpoint: "https://empty-actions-map.io", + expectederror: "", + }, + // case 7 + { + endpoint: "https://download-actions-map.io", + expectederror: "Missing action 'upload'", + }, + // case 8 + { + endpoint: "https://upload-actions-map.io", + expectederror: "", + }, + // case 9 + { + endpoint: "https://verify-actions-map.io", + expectederror: "Missing action 'upload'", + }, + // case 10 + { + endpoint: "https://unknown-actions-map.io", + expectederror: "Missing action 'upload'", }, } @@ -134,7 +348,9 @@ func TestHTTPClientDownload(t *testing.T) { } client.transfers["dummy"] = dummy - _, err := client.Download(context.Background(), oid, size) + err := client.Upload(context.Background(), []Pointer{p}, func(p Pointer, objectError error) (io.ReadCloser, error) { + return ioutil.NopCloser(new(bytes.Buffer)), objectError + }) if len(c.expectederror) > 0 { assert.True(t, strings.Contains(err.Error(), c.expectederror), "case %d: '%s' should contain '%s'", n, err.Error(), c.expectederror) } else { diff --git a/modules/lfs/pointer_test.go b/modules/lfs/pointer_test.go index 0ed6df2c6da12..9cd8b15c9e048 100644 --- a/modules/lfs/pointer_test.go +++ b/modules/lfs/pointer_test.go @@ -15,16 +15,16 @@ import ( func TestStringContent(t *testing.T) { p := Pointer{Oid: "4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393", Size: 1234} expected := "version https://git-lfs.github.com/spec/v1\noid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393\nsize 1234\n" - assert.Equal(t, p.StringContent(), expected) + assert.Equal(t, expected, p.StringContent()) } func TestRelativePath(t *testing.T) { p := Pointer{Oid: "4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393"} expected := path.Join("4d", "7a", "214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393") - assert.Equal(t, p.RelativePath(), expected) + assert.Equal(t, expected, p.RelativePath()) p2 := Pointer{Oid: "4d7a"} - assert.Equal(t, p2.RelativePath(), "4d7a") + assert.Equal(t, "4d7a", p2.RelativePath()) } func TestIsValid(t *testing.T) { @@ -48,8 +48,8 @@ func TestGeneratePointer(t *testing.T) { p, err := GeneratePointer(strings.NewReader("Gitea")) assert.NoError(t, err) assert.True(t, p.IsValid()) - assert.Equal(t, p.Oid, "94cb57646c54a297c9807697e80a30946f79a4b82cb079d2606847825b1812cc") - assert.Equal(t, p.Size, int64(5)) + assert.Equal(t, "94cb57646c54a297c9807697e80a30946f79a4b82cb079d2606847825b1812cc", p.Oid) + assert.Equal(t, int64(5), p.Size) } func TestReadPointerFromBuffer(t *testing.T) { @@ -84,20 +84,20 @@ func TestReadPointerFromBuffer(t *testing.T) { p, err = ReadPointerFromBuffer([]byte("version https://git-lfs.github.com/spec/v1\noid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393\nsize 1234\n")) assert.NoError(t, err) assert.True(t, p.IsValid()) - assert.Equal(t, p.Oid, "4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393") - assert.Equal(t, p.Size, int64(1234)) + assert.Equal(t, "4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393", p.Oid) + assert.Equal(t, int64(1234), p.Size) p, err = ReadPointerFromBuffer([]byte("version https://git-lfs.github.com/spec/v1\noid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393\nsize 1234\ntest")) assert.NoError(t, err) assert.True(t, p.IsValid()) - assert.Equal(t, p.Oid, "4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393") - assert.Equal(t, p.Size, int64(1234)) + assert.Equal(t, "4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393", p.Oid) + assert.Equal(t, int64(1234), p.Size) } func TestReadPointer(t *testing.T) { p, err := ReadPointer(strings.NewReader("version https://git-lfs.github.com/spec/v1\noid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393\nsize 1234\n")) assert.NoError(t, err) assert.True(t, p.IsValid()) - assert.Equal(t, p.Oid, "4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393") - assert.Equal(t, p.Size, int64(1234)) + assert.Equal(t, "4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393", p.Oid) + assert.Equal(t, int64(1234), p.Size) } diff --git a/modules/lfs/shared.go b/modules/lfs/shared.go index 70b76d7512d56..8343d12e1d603 100644 --- a/modules/lfs/shared.go +++ b/modules/lfs/shared.go @@ -45,18 +45,18 @@ type BatchResponse struct { // ObjectResponse is object metadata as seen by clients of the LFS server. type ObjectResponse struct { Pointer - Actions map[string]*Link `json:"actions"` + Actions map[string]*Link `json:"actions,omitempty"` Error *ObjectError `json:"error,omitempty"` } -// Link provides a structure used to build a hypermedia representation of an HTTP link. +// Link provides a structure with informations about how to access a object. type Link struct { Href string `json:"href"` Header map[string]string `json:"header,omitempty"` - ExpiresAt time.Time `json:"expires_at,omitempty"` + ExpiresAt *time.Time `json:"expires_at,omitempty"` } -// ObjectError defines the JSON structure returned to the client in case of an error +// ObjectError defines the JSON structure returned to the client in case of an error. type ObjectError struct { Code int `json:"code"` Message string `json:"message"` @@ -67,3 +67,10 @@ type PointerBlob struct { Hash string Pointer } + +// ErrorResponse describes the error to the client. +type ErrorResponse struct { + Message string + DocumentationURL string `json:"documentation_url,omitempty"` + RequestID string `json:"request_id,omitempty"` +} diff --git a/modules/lfs/transferadapter.go b/modules/lfs/transferadapter.go index ea3aff0000b9a..8c40ab8c04469 100644 --- a/modules/lfs/transferadapter.go +++ b/modules/lfs/transferadapter.go @@ -5,18 +5,24 @@ package lfs import ( + "bytes" "context" "errors" "fmt" "io" "net/http" + + "code.gitea.io/gitea/modules/log" + + jsoniter "github.com/json-iterator/go" ) // TransferAdapter represents an adapter for downloading/uploading LFS objects type TransferAdapter interface { Name() string - Download(ctx context.Context, r *ObjectResponse) (io.ReadCloser, error) - //Upload(ctx context.Context, reader io.Reader) error + Download(ctx context.Context, l *Link) (io.ReadCloser, error) + Upload(ctx context.Context, l *Link, p Pointer, r io.Reader) error + Verify(ctx context.Context, l *Link, p Pointer) error } // BasicTransferAdapter implements the "basic" adapter @@ -30,29 +36,101 @@ func (a *BasicTransferAdapter) Name() string { } // Download reads the download location and downloads the data -func (a *BasicTransferAdapter) Download(ctx context.Context, r *ObjectResponse) (io.ReadCloser, error) { - download, ok := r.Actions["download"] - if !ok { - return nil, errors.New("lfs.BasicTransferAdapter.Download: Action 'download' not found") +func (a *BasicTransferAdapter) Download(ctx context.Context, l *Link) (io.ReadCloser, error) { + resp, err := a.performRequest(ctx, "GET", l, nil, nil) + if err != nil { + return nil, err } + return resp.Body, nil +} - req, err := http.NewRequestWithContext(ctx, "GET", download.Href, nil) +// Upload sends the content to the LFS server +func (a *BasicTransferAdapter) Upload(ctx context.Context, l *Link, p Pointer, r io.Reader) error { + _, err := a.performRequest(ctx, "PUT", l, r, func(req *http.Request) { + if len(req.Header.Get("Content-Type")) == 0 { + req.Header.Set("Content-Type", "application/octet-stream") + } + + if req.Header.Get("Transfer-Encoding") == "chunked" { + req.TransferEncoding = []string{"chunked"} + } + + req.ContentLength = p.Size + }) if err != nil { - return nil, fmt.Errorf("lfs.BasicTransferAdapter.Download http.NewRequestWithContext: %w", err) + return err } - for key, value := range download.Header { + return nil +} + +// Verify calls the verify handler on the LFS server +func (a *BasicTransferAdapter) Verify(ctx context.Context, l *Link, p Pointer) error { + b, err := jsoniter.Marshal(p) + if err != nil { + log.Error("Error encoding json: %v", err) + return err + } + + _, err = a.performRequest(ctx, "POST", l, bytes.NewReader(b), func(req *http.Request) { + req.Header.Set("Content-Type", MediaType) + }) + if err != nil { + return err + } + return nil +} + +func (a *BasicTransferAdapter) performRequest(ctx context.Context, method string, l *Link, body io.Reader, callback func(*http.Request)) (*http.Response, error) { + log.Trace("Calling: %s %s", method, l.Href) + + req, err := http.NewRequestWithContext(ctx, method, l.Href, body) + if err != nil { + log.Error("Error creating request: %v", err) + return nil, err + } + for key, value := range l.Header { req.Header.Set(key, value) } + req.Header.Set("Accept", MediaType) + + if callback != nil { + callback(req) + } res, err := a.client.Do(req) if err != nil { select { case <-ctx.Done(): - return nil, ctx.Err() + return res, ctx.Err() default: } - return nil, fmt.Errorf("lfs.BasicTransferAdapter.Download http.Do: %w", err) + log.Error("Error while processing request: %v", err) + return res, err + } + + if res.StatusCode != http.StatusOK { + return res, handleErrorResponse(res) + } + + return res, nil +} + +func handleErrorResponse(resp *http.Response) error { + defer resp.Body.Close() + + er, err := decodeReponseError(resp.Body) + if err != nil { + return fmt.Errorf("Request failed with status %s", resp.Status) } + log.Trace("ErrorRespone: %v", er) + return errors.New(er.Message) +} - return res.Body, nil +func decodeReponseError(r io.Reader) (ErrorResponse, error) { + var er ErrorResponse + err := jsoniter.NewDecoder(r).Decode(&er) + if err != nil { + log.Error("Error decoding json: %v", err) + } + return er, err } diff --git a/modules/lfs/transferadapter_test.go b/modules/lfs/transferadapter_test.go index 0eabd3faeee1b..7dfdad417ea5b 100644 --- a/modules/lfs/transferadapter_test.go +++ b/modules/lfs/transferadapter_test.go @@ -7,11 +7,13 @@ package lfs import ( "bytes" "context" + "io" "io/ioutil" "net/http" "strings" "testing" + jsoniter "github.com/json-iterator/go" "github.com/stretchr/testify/assert" ) @@ -21,58 +23,151 @@ func TestBasicTransferAdapterName(t *testing.T) { assert.Equal(t, "basic", a.Name()) } -func TestBasicTransferAdapterDownload(t *testing.T) { +func TestBasicTransferAdapter(t *testing.T) { + p := Pointer{Oid: "b5a2c96250612366ea272ffac6d9744aaf4b45aacd96aa7cfcb931ee3b558259", Size: 5} + roundTripHandler := func(req *http.Request) *http.Response { + assert.Equal(t, MediaType, req.Header.Get("Accept")) + assert.Equal(t, "test-value", req.Header.Get("test-header")) + url := req.URL.String() - if strings.Contains(url, "valid-download-request") { + if strings.Contains(url, "download-request") { assert.Equal(t, "GET", req.Method) - assert.Equal(t, "test-value", req.Header.Get("test-header")) return &http.Response{StatusCode: http.StatusOK, Body: ioutil.NopCloser(bytes.NewBufferString("dummy"))} - } + } else if strings.Contains(url, "upload-request") { + assert.Equal(t, "PUT", req.Method) + assert.Equal(t, "application/octet-stream", req.Header.Get("Content-Type")) + + b, err := io.ReadAll(req.Body) + assert.NoError(t, err) + assert.Equal(t, "dummy", string(b)) - t.Errorf("Unknown test case: %s", url) + return &http.Response{StatusCode: http.StatusOK} + } else if strings.Contains(url, "verify-request") { + assert.Equal(t, "POST", req.Method) + assert.Equal(t, MediaType, req.Header.Get("Content-Type")) - return nil + var vp Pointer + err := jsoniter.NewDecoder(req.Body).Decode(&vp) + assert.NoError(t, err) + assert.Equal(t, p.Oid, vp.Oid) + assert.Equal(t, p.Size, vp.Size) + + return &http.Response{StatusCode: http.StatusOK} + } else if strings.Contains(url, "error-response") { + er := &ErrorResponse{ + Message: "Object not found", + } + payload := new(bytes.Buffer) + jsoniter.NewEncoder(payload).Encode(er) + + return &http.Response{StatusCode: http.StatusNotFound, Body: ioutil.NopCloser(payload)} + } else { + t.Errorf("Unknown test case: %s", url) + return nil + } } hc := &http.Client{Transport: RoundTripFunc(roundTripHandler)} a := &BasicTransferAdapter{hc} - var cases = []struct { - response *ObjectResponse - expectederror string - }{ - // case 0 - { - response: &ObjectResponse{}, - expectederror: "Action 'download' not found", - }, - // case 1 - { - response: &ObjectResponse{ - Actions: map[string]*Link{"upload": nil}, + t.Run("Download", func(t *testing.T) { + cases := []struct { + link *Link + expectederror string + }{ + // case 0 + { + link: &Link{ + Href: "https://download-request.io", + Header: map[string]string{"test-header": "test-value"}, + }, + expectederror: "", }, - expectederror: "Action 'download' not found", - }, - // case 2 - { - response: &ObjectResponse{ - Actions: map[string]*Link{"download": { - Href: "https://valid-download-request.io", + // case 1 + { + link: &Link{ + Href: "https://error-response.io", Header: map[string]string{"test-header": "test-value"}, - }}, + }, + expectederror: "Object not found", }, - expectederror: "", - }, - } + } - for n, c := range cases { - _, err := a.Download(context.Background(), c.response) - if len(c.expectederror) > 0 { - assert.True(t, strings.Contains(err.Error(), c.expectederror), "case %d: '%s' should contain '%s'", n, err.Error(), c.expectederror) - } else { - assert.NoError(t, err, "case %d", n) + for n, c := range cases { + _, err := a.Download(context.Background(), c.link) + if len(c.expectederror) > 0 { + assert.True(t, strings.Contains(err.Error(), c.expectederror), "case %d: '%s' should contain '%s'", n, err.Error(), c.expectederror) + } else { + assert.NoError(t, err, "case %d", n) + } } - } + }) + + t.Run("Upload", func(t *testing.T) { + cases := []struct { + link *Link + expectederror string + }{ + // case 0 + { + link: &Link{ + Href: "https://upload-request.io", + Header: map[string]string{"test-header": "test-value"}, + }, + expectederror: "", + }, + // case 1 + { + link: &Link{ + Href: "https://error-response.io", + Header: map[string]string{"test-header": "test-value"}, + }, + expectederror: "Object not found", + }, + } + + for n, c := range cases { + err := a.Upload(context.Background(), c.link, p, bytes.NewBufferString("dummy")) + if len(c.expectederror) > 0 { + assert.True(t, strings.Contains(err.Error(), c.expectederror), "case %d: '%s' should contain '%s'", n, err.Error(), c.expectederror) + } else { + assert.NoError(t, err, "case %d", n) + } + } + }) + + t.Run("Verify", func(t *testing.T) { + cases := []struct { + link *Link + expectederror string + }{ + // case 0 + { + link: &Link{ + Href: "https://verify-request.io", + Header: map[string]string{"test-header": "test-value"}, + }, + expectederror: "", + }, + // case 1 + { + link: &Link{ + Href: "https://error-response.io", + Header: map[string]string{"test-header": "test-value"}, + }, + expectederror: "Object not found", + }, + } + + for n, c := range cases { + err := a.Verify(context.Background(), c.link, p) + if len(c.expectederror) > 0 { + assert.True(t, strings.Contains(err.Error(), c.expectederror), "case %d: '%s' should contain '%s'", n, err.Error(), c.expectederror) + } else { + assert.NoError(t, err, "case %d", n) + } + } + }) } diff --git a/modules/log/console_test.go b/modules/log/console_test.go index e7ed07123b06b..4da87b48a309e 100644 --- a/modules/log/console_test.go +++ b/modules/log/console_test.go @@ -48,7 +48,7 @@ func TestConsoleLoggerMinimalConfig(t *testing.T) { assert.Equal(t, prefix, realCW.Prefix) assert.Equal(t, "", string(written)) cw.Close() - assert.Equal(t, false, closed) + assert.False(t, closed) } } @@ -97,20 +97,20 @@ func TestConsoleLogger(t *testing.T) { expected := fmt.Sprintf("%s%s %s:%d:%s [%c] %s\n", prefix, dateString, event.filename, event.line, event.caller, strings.ToUpper(event.level.String())[0], event.msg) cw.LogEvent(&event) assert.Equal(t, expected, string(written)) - assert.Equal(t, false, closed) + assert.False(t, closed) written = written[:0] event.level = DEBUG expected = "" cw.LogEvent(&event) assert.Equal(t, expected, string(written)) - assert.Equal(t, false, closed) + assert.False(t, closed) event.level = TRACE expected = "" cw.LogEvent(&event) assert.Equal(t, expected, string(written)) - assert.Equal(t, false, closed) + assert.False(t, closed) nonMatchEvent := Event{ level: INFO, @@ -124,15 +124,15 @@ func TestConsoleLogger(t *testing.T) { expected = "" cw.LogEvent(&nonMatchEvent) assert.Equal(t, expected, string(written)) - assert.Equal(t, false, closed) + assert.False(t, closed) event.level = WARN expected = fmt.Sprintf("%s%s %s:%d:%s [%c] %s\n", prefix, dateString, event.filename, event.line, event.caller, strings.ToUpper(event.level.String())[0], event.msg) cw.LogEvent(&event) assert.Equal(t, expected, string(written)) - assert.Equal(t, false, closed) + assert.False(t, closed) written = written[:0] cw.Close() - assert.Equal(t, false, closed) + assert.False(t, closed) } diff --git a/modules/log/file_test.go b/modules/log/file_test.go index af6fbcb29d11b..7bc5f90037a76 100644 --- a/modules/log/file_test.go +++ b/modules/log/file_test.go @@ -30,7 +30,7 @@ func TestFileLoggerFails(t *testing.T) { fileLogger := NewFileLogger() //realFileLogger, ok := fileLogger.(*FileLogger) - //assert.Equal(t, true, ok) + //assert.True(t, ok) // Fail if there is bad json err = fileLogger.Init("{") @@ -161,7 +161,7 @@ func TestCompressFileLogger(t *testing.T) { fileLogger := NewFileLogger() realFileLogger, ok := fileLogger.(*FileLogger) - assert.Equal(t, true, ok) + assert.True(t, ok) location, _ := time.LoadLocation("EST") diff --git a/modules/log/log_test.go b/modules/log/log_test.go index 810505dea5b47..d14daa5a206d0 100644 --- a/modules/log/log_test.go +++ b/modules/log/log_test.go @@ -26,9 +26,9 @@ func baseConsoleTest(t *testing.T, logger *MultiChannelledLogger) (chan []byte, channelledLog := m.GetEventLogger("console") assert.NotEmpty(t, channelledLog) realChanLog, ok := channelledLog.(*ChannelledLog) - assert.Equal(t, true, ok) + assert.True(t, ok) realCL, ok := realChanLog.loggerProvider.(*ConsoleLogger) - assert.Equal(t, true, ok) + assert.True(t, ok) assert.Equal(t, INFO, realCL.Level) realCL.out = c @@ -38,20 +38,20 @@ func baseConsoleTest(t *testing.T, logger *MultiChannelledLogger) (chan []byte, logger.Log(0, INFO, format, args...) line := <-written assert.Contains(t, string(line), fmt.Sprintf(format, args...)) - assert.Equal(t, false, <-closed) + assert.False(t, <-closed) format = "test2: %s" logger.Warn(format, args...) line = <-written assert.Contains(t, string(line), fmt.Sprintf(format, args...)) - assert.Equal(t, false, <-closed) + assert.False(t, <-closed) format = "testerror: %s" logger.Error(format, args...) line = <-written assert.Contains(t, string(line), fmt.Sprintf(format, args...)) - assert.Equal(t, false, <-closed) + assert.False(t, <-closed) return written, closed } @@ -63,7 +63,7 @@ func TestNewLoggerUnexported(t *testing.T) { out := logger.MultiChannelledLog.GetEventLogger("console") assert.NotEmpty(t, out) chanlog, ok := out.(*ChannelledLog) - assert.Equal(t, true, ok) + assert.True(t, ok) assert.Equal(t, "console", chanlog.provider) assert.Equal(t, INFO, logger.GetLevel()) baseConsoleTest(t, logger) @@ -74,11 +74,11 @@ func TestNewLoggger(t *testing.T) { logger := NewLogger(0, "console", "console", fmt.Sprintf(`{"level":"%s"}`, level.String())) assert.Equal(t, INFO, GetLevel()) - assert.Equal(t, false, IsTrace()) - assert.Equal(t, false, IsDebug()) - assert.Equal(t, true, IsInfo()) - assert.Equal(t, true, IsWarn()) - assert.Equal(t, true, IsError()) + assert.False(t, IsTrace()) + assert.False(t, IsDebug()) + assert.True(t, IsInfo()) + assert.True(t, IsWarn()) + assert.True(t, IsError()) written, closed := baseConsoleTest(t, logger) @@ -88,17 +88,17 @@ func TestNewLoggger(t *testing.T) { Log(0, INFO, format, args...) line := <-written assert.Contains(t, string(line), fmt.Sprintf(format, args...)) - assert.Equal(t, false, <-closed) + assert.False(t, <-closed) Info(format, args...) line = <-written assert.Contains(t, string(line), fmt.Sprintf(format, args...)) - assert.Equal(t, false, <-closed) + assert.False(t, <-closed) go DelLogger("console") line = <-written assert.Equal(t, "", string(line)) - assert.Equal(t, true, <-closed) + assert.True(t, <-closed) } func TestNewLogggerRecreate(t *testing.T) { @@ -106,11 +106,11 @@ func TestNewLogggerRecreate(t *testing.T) { NewLogger(0, "console", "console", fmt.Sprintf(`{"level":"%s"}`, level.String())) assert.Equal(t, INFO, GetLevel()) - assert.Equal(t, false, IsTrace()) - assert.Equal(t, false, IsDebug()) - assert.Equal(t, true, IsInfo()) - assert.Equal(t, true, IsWarn()) - assert.Equal(t, true, IsError()) + assert.False(t, IsTrace()) + assert.False(t, IsDebug()) + assert.True(t, IsInfo()) + assert.True(t, IsWarn()) + assert.True(t, IsError()) format := "test: %s" args := []interface{}{"A"} @@ -120,11 +120,11 @@ func TestNewLogggerRecreate(t *testing.T) { NewLogger(0, "console", "console", fmt.Sprintf(`{"level":"%s"}`, level.String())) assert.Equal(t, INFO, GetLevel()) - assert.Equal(t, false, IsTrace()) - assert.Equal(t, false, IsDebug()) - assert.Equal(t, true, IsInfo()) - assert.Equal(t, true, IsWarn()) - assert.Equal(t, true, IsError()) + assert.False(t, IsTrace()) + assert.False(t, IsDebug()) + assert.True(t, IsInfo()) + assert.True(t, IsWarn()) + assert.True(t, IsError()) Log(0, INFO, format, args...) @@ -150,5 +150,5 @@ func TestNewNamedLogger(t *testing.T) { go DelNamedLogger("test") line := <-written assert.Equal(t, "", string(line)) - assert.Equal(t, true, <-closed) + assert.True(t, <-closed) } diff --git a/modules/log/smtp_test.go b/modules/log/smtp_test.go index 216d55521549a..c8758bf6bece4 100644 --- a/modules/log/smtp_test.go +++ b/modules/log/smtp_test.go @@ -26,7 +26,7 @@ func TestSMTPLogger(t *testing.T) { logger := NewSMTPLogger() smtpLogger, ok := logger.(*SMTPLogger) - assert.Equal(t, true, ok) + assert.True(t, ok) err := logger.Init(fmt.Sprintf("{\"prefix\":\"%s\",\"level\":\"%s\",\"flags\":%d,\"username\":\"%s\",\"password\":\"%s\",\"host\":\"%s\",\"subject\":\"%s\",\"sendTos\":[\"%s\",\"%s\"]}", prefix, level.String(), flags, username, password, host, subject, sendTos[0], sendTos[1])) assert.NoError(t, err) diff --git a/modules/log/writer_test.go b/modules/log/writer_test.go index 886dd58fb38fd..99a5fd3406899 100644 --- a/modules/log/writer_test.go +++ b/modules/log/writer_test.go @@ -64,44 +64,44 @@ func TestBaseLogger(t *testing.T) { expected := fmt.Sprintf("%s%s %s:%d:%s [%c] %s\n", prefix, dateString, event.filename, event.line, event.caller, strings.ToUpper(event.level.String())[0], event.msg) b.LogEvent(&event) assert.Equal(t, expected, string(written)) - assert.Equal(t, false, closed) + assert.False(t, closed) written = written[:0] event.level = DEBUG expected = "" b.LogEvent(&event) assert.Equal(t, expected, string(written)) - assert.Equal(t, false, closed) + assert.False(t, closed) event.level = TRACE expected = "" b.LogEvent(&event) assert.Equal(t, expected, string(written)) - assert.Equal(t, false, closed) + assert.False(t, closed) event.level = WARN expected = fmt.Sprintf("%s%s %s:%d:%s [%c] %s\n", prefix, dateString, event.filename, event.line, event.caller, strings.ToUpper(event.level.String())[0], event.msg) b.LogEvent(&event) assert.Equal(t, expected, string(written)) - assert.Equal(t, false, closed) + assert.False(t, closed) written = written[:0] event.level = ERROR expected = fmt.Sprintf("%s%s %s:%d:%s [%c] %s\n", prefix, dateString, event.filename, event.line, event.caller, strings.ToUpper(event.level.String())[0], event.msg) b.LogEvent(&event) assert.Equal(t, expected, string(written)) - assert.Equal(t, false, closed) + assert.False(t, closed) written = written[:0] event.level = CRITICAL expected = fmt.Sprintf("%s%s %s:%d:%s [%c] %s\n", prefix, dateString, event.filename, event.line, event.caller, strings.ToUpper(event.level.String())[0], event.msg) b.LogEvent(&event) assert.Equal(t, expected, string(written)) - assert.Equal(t, false, closed) + assert.False(t, closed) written = written[:0] b.Close() - assert.Equal(t, true, closed) + assert.True(t, closed) } func TestBaseLoggerDated(t *testing.T) { @@ -142,46 +142,46 @@ func TestBaseLoggerDated(t *testing.T) { expected := fmt.Sprintf("%s%s %s:%d [%s] %s", prefix, dateString, "FILENAME", event.line, strings.ToUpper(event.level.String()), event.msg) b.LogEvent(&event) assert.Equal(t, expected, string(written)) - assert.Equal(t, false, closed) + assert.False(t, closed) written = written[:0] event.level = INFO expected = "" b.LogEvent(&event) assert.Equal(t, expected, string(written)) - assert.Equal(t, false, closed) + assert.False(t, closed) written = written[:0] event.level = ERROR expected = fmt.Sprintf("%s%s %s:%d [%s] %s", prefix, dateString, "FILENAME", event.line, strings.ToUpper(event.level.String()), event.msg) b.LogEvent(&event) assert.Equal(t, expected, string(written)) - assert.Equal(t, false, closed) + assert.False(t, closed) written = written[:0] event.level = DEBUG expected = "" b.LogEvent(&event) assert.Equal(t, expected, string(written)) - assert.Equal(t, false, closed) + assert.False(t, closed) written = written[:0] event.level = CRITICAL expected = fmt.Sprintf("%s%s %s:%d [%s] %s", prefix, dateString, "FILENAME", event.line, strings.ToUpper(event.level.String()), event.msg) b.LogEvent(&event) assert.Equal(t, expected, string(written)) - assert.Equal(t, false, closed) + assert.False(t, closed) written = written[:0] event.level = TRACE expected = "" b.LogEvent(&event) assert.Equal(t, expected, string(written)) - assert.Equal(t, false, closed) + assert.False(t, closed) written = written[:0] b.Close() - assert.Equal(t, true, closed) + assert.True(t, closed) } func TestBaseLoggerMultiLineNoFlagsRegexp(t *testing.T) { @@ -222,20 +222,20 @@ func TestBaseLoggerMultiLineNoFlagsRegexp(t *testing.T) { expected := "TEST\n\tMESSAGE\n\tTEST\n" b.LogEvent(&event) assert.Equal(t, expected, string(written)) - assert.Equal(t, false, closed) + assert.False(t, closed) written = written[:0] event.filename = "ELSEWHERE" b.LogEvent(&event) assert.Equal(t, "", string(written)) - assert.Equal(t, false, closed) + assert.False(t, closed) written = written[:0] event.caller = "FILENAME" b.LogEvent(&event) assert.Equal(t, expected, string(written)) - assert.Equal(t, false, closed) + assert.False(t, closed) written = written[:0] event = Event{ @@ -249,9 +249,8 @@ func TestBaseLoggerMultiLineNoFlagsRegexp(t *testing.T) { expected = "TEST\n\tFILENAME\n\tTEST\n" b.LogEvent(&event) assert.Equal(t, expected, string(written)) - assert.Equal(t, false, closed) + assert.False(t, closed) written = written[:0] - } func TestBrokenRegexp(t *testing.T) { @@ -273,5 +272,5 @@ func TestBrokenRegexp(t *testing.T) { b.NewWriterLogger(c) assert.Empty(t, b.regexp) b.Close() - assert.Equal(t, true, closed) + assert.True(t, closed) } diff --git a/modules/markup/html.go b/modules/markup/html.go index 4d1b49e241550..2a83b8716e0fa 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -89,6 +89,7 @@ func isLinkStr(link string) bool { return validLinksPattern.MatchString(link) } +// FIXME: This function is not concurrent safe func getIssueFullPattern() *regexp.Regexp { if issueFullPattern == nil { issueFullPattern = regexp.MustCompile(regexp.QuoteMeta(setting.AppURL) + @@ -364,24 +365,19 @@ func visitNode(ctx *RenderContext, procs []processor, node *html.Node, visitText } case html.ElementNode: if node.Data == "img" { - attrs := node.Attr - for idx, attr := range attrs { + for _, attr := range node.Attr { if attr.Key != "src" { continue } - link := []byte(attr.Val) - if len(link) > 0 && !IsLink(link) { + if len(attr.Val) > 0 && !isLinkStr(attr.Val) && !strings.HasPrefix(attr.Val, "data:image/") { prefix := ctx.URLPrefix if ctx.IsWiki { prefix = util.URLJoin(prefix, "wiki", "raw") } prefix = strings.Replace(prefix, "/src/", "/media/", 1) - lnk := string(link) - lnk = util.URLJoin(prefix, lnk) - link = []byte(lnk) + attr.Val = util.URLJoin(prefix, attr.Val) } - node.Attr[idx].Val = string(link) } } else if node.Data == "a" { visitText = false @@ -571,26 +567,38 @@ func replaceContentList(node *html.Node, i, j int, newNodes []*html.Node) { } func mentionProcessor(ctx *RenderContext, node *html.Node) { - // We replace only the first mention; other mentions will be addressed later - found, loc := references.FindFirstMentionBytes([]byte(node.Data)) - if !found { - return - } - mention := node.Data[loc.Start:loc.End] - var teams string - teams, ok := ctx.Metas["teams"] - // FIXME: util.URLJoin may not be necessary here: - // - setting.AppURL is defined to have a terminal '/' so unless mention[1:] - // is an AppSubURL link we can probably fallback to concatenation. - // team mention should follow @orgName/teamName style - if ok && strings.Contains(mention, "/") { - mentionOrgAndTeam := strings.Split(mention, "/") - if mentionOrgAndTeam[0][1:] == ctx.Metas["org"] && strings.Contains(teams, ","+strings.ToLower(mentionOrgAndTeam[1])+",") { - replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(setting.AppURL, "org", ctx.Metas["org"], "teams", mentionOrgAndTeam[1]), mention, "mention")) + start := 0 + next := node.NextSibling + for node != nil && node != next && start < len(node.Data) { + // We replace only the first mention; other mentions will be addressed later + found, loc := references.FindFirstMentionBytes([]byte(node.Data[start:])) + if !found { + return } - return + loc.Start += start + loc.End += start + mention := node.Data[loc.Start:loc.End] + var teams string + teams, ok := ctx.Metas["teams"] + // FIXME: util.URLJoin may not be necessary here: + // - setting.AppURL is defined to have a terminal '/' so unless mention[1:] + // is an AppSubURL link we can probably fallback to concatenation. + // team mention should follow @orgName/teamName style + if ok && strings.Contains(mention, "/") { + mentionOrgAndTeam := strings.Split(mention, "/") + if mentionOrgAndTeam[0][1:] == ctx.Metas["org"] && strings.Contains(teams, ","+strings.ToLower(mentionOrgAndTeam[1])+",") { + replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(setting.AppURL, "org", ctx.Metas["org"], "teams", mentionOrgAndTeam[1]), mention, "mention")) + node = node.NextSibling.NextSibling + start = 0 + continue + } + start = loc.End + continue + } + replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(setting.AppURL, mention[1:]), mention, "mention")) + node = node.NextSibling.NextSibling + start = 0 } - replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(setting.AppURL, mention[1:]), mention, "mention")) } func shortLinkProcessor(ctx *RenderContext, node *html.Node) { @@ -598,188 +606,196 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) { } func shortLinkProcessorFull(ctx *RenderContext, node *html.Node, noLink bool) { - m := shortLinkPattern.FindStringSubmatchIndex(node.Data) - if m == nil { - return - } + next := node.NextSibling + for node != nil && node != next { + m := shortLinkPattern.FindStringSubmatchIndex(node.Data) + if m == nil { + return + } - content := node.Data[m[2]:m[3]] - tail := node.Data[m[4]:m[5]] - props := make(map[string]string) - - // MediaWiki uses [[link|text]], while GitHub uses [[text|link]] - // It makes page handling terrible, but we prefer GitHub syntax - // And fall back to MediaWiki only when it is obvious from the look - // Of text and link contents - sl := strings.Split(content, "|") - for _, v := range sl { - if equalPos := strings.IndexByte(v, '='); equalPos == -1 { - // There is no equal in this argument; this is a mandatory arg - if props["name"] == "" { - if isLinkStr(v) { - // If we clearly see it is a link, we save it so - - // But first we need to ensure, that if both mandatory args provided - // look like links, we stick to GitHub syntax - if props["link"] != "" { - props["name"] = props["link"] - } + content := node.Data[m[2]:m[3]] + tail := node.Data[m[4]:m[5]] + props := make(map[string]string) + + // MediaWiki uses [[link|text]], while GitHub uses [[text|link]] + // It makes page handling terrible, but we prefer GitHub syntax + // And fall back to MediaWiki only when it is obvious from the look + // Of text and link contents + sl := strings.Split(content, "|") + for _, v := range sl { + if equalPos := strings.IndexByte(v, '='); equalPos == -1 { + // There is no equal in this argument; this is a mandatory arg + if props["name"] == "" { + if isLinkStr(v) { + // If we clearly see it is a link, we save it so + + // But first we need to ensure, that if both mandatory args provided + // look like links, we stick to GitHub syntax + if props["link"] != "" { + props["name"] = props["link"] + } - props["link"] = strings.TrimSpace(v) + props["link"] = strings.TrimSpace(v) + } else { + props["name"] = v + } } else { - props["name"] = v + props["link"] = strings.TrimSpace(v) } } else { - props["link"] = strings.TrimSpace(v) - } - } else { - // There is an equal; optional argument. - - sep := strings.IndexByte(v, '=') - key, val := v[:sep], html.UnescapeString(v[sep+1:]) - - // When parsing HTML, x/net/html will change all quotes which are - // not used for syntax into UTF-8 quotes. So checking val[0] won't - // be enough, since that only checks a single byte. - if len(val) > 1 { - if (strings.HasPrefix(val, "“") && strings.HasSuffix(val, "”")) || - (strings.HasPrefix(val, "‘") && strings.HasSuffix(val, "’")) { - const lenQuote = len("‘") - val = val[lenQuote : len(val)-lenQuote] - } else if (strings.HasPrefix(val, "\"") && strings.HasSuffix(val, "\"")) || - (strings.HasPrefix(val, "'") && strings.HasSuffix(val, "'")) { - val = val[1 : len(val)-1] - } else if strings.HasPrefix(val, "'") && strings.HasSuffix(val, "’") { - const lenQuote = len("‘") - val = val[1 : len(val)-lenQuote] + // There is an equal; optional argument. + + sep := strings.IndexByte(v, '=') + key, val := v[:sep], html.UnescapeString(v[sep+1:]) + + // When parsing HTML, x/net/html will change all quotes which are + // not used for syntax into UTF-8 quotes. So checking val[0] won't + // be enough, since that only checks a single byte. + if len(val) > 1 { + if (strings.HasPrefix(val, "“") && strings.HasSuffix(val, "”")) || + (strings.HasPrefix(val, "‘") && strings.HasSuffix(val, "’")) { + const lenQuote = len("‘") + val = val[lenQuote : len(val)-lenQuote] + } else if (strings.HasPrefix(val, "\"") && strings.HasSuffix(val, "\"")) || + (strings.HasPrefix(val, "'") && strings.HasSuffix(val, "'")) { + val = val[1 : len(val)-1] + } else if strings.HasPrefix(val, "'") && strings.HasSuffix(val, "’") { + const lenQuote = len("‘") + val = val[1 : len(val)-lenQuote] + } } + props[key] = val } - props[key] = val } - } - var name, link string - if props["link"] != "" { - link = props["link"] - } else if props["name"] != "" { - link = props["name"] - } - if props["title"] != "" { - name = props["title"] - } else if props["name"] != "" { - name = props["name"] - } else { - name = link - } - - name += tail - image := false - switch ext := filepath.Ext(link); ext { - // fast path: empty string, ignore - case "": - break - case ".jpg", ".jpeg", ".png", ".tif", ".tiff", ".webp", ".gif", ".bmp", ".ico", ".svg": - image = true - } - - childNode := &html.Node{} - linkNode := &html.Node{ - FirstChild: childNode, - LastChild: childNode, - Type: html.ElementNode, - Data: "a", - DataAtom: atom.A, - } - childNode.Parent = linkNode - absoluteLink := isLinkStr(link) - if !absoluteLink { - if image { - link = strings.ReplaceAll(link, " ", "+") + var name, link string + if props["link"] != "" { + link = props["link"] + } else if props["name"] != "" { + link = props["name"] + } + if props["title"] != "" { + name = props["title"] + } else if props["name"] != "" { + name = props["name"] } else { - link = strings.ReplaceAll(link, " ", "-") + name = link } - if !strings.Contains(link, "/") { - link = url.PathEscape(link) + + name += tail + image := false + switch ext := filepath.Ext(link); ext { + // fast path: empty string, ignore + case "": + // leave image as false + case ".jpg", ".jpeg", ".png", ".tif", ".tiff", ".webp", ".gif", ".bmp", ".ico", ".svg": + image = true } - } - urlPrefix := ctx.URLPrefix - if image { + + childNode := &html.Node{} + linkNode := &html.Node{ + FirstChild: childNode, + LastChild: childNode, + Type: html.ElementNode, + Data: "a", + DataAtom: atom.A, + } + childNode.Parent = linkNode + absoluteLink := isLinkStr(link) if !absoluteLink { - if IsSameDomain(urlPrefix) { - urlPrefix = strings.Replace(urlPrefix, "/src/", "/raw/", 1) + if image { + link = strings.ReplaceAll(link, " ", "+") + } else { + link = strings.ReplaceAll(link, " ", "-") } - if ctx.IsWiki { - link = util.URLJoin("wiki", "raw", link) + if !strings.Contains(link, "/") { + link = url.PathEscape(link) } - link = util.URLJoin(urlPrefix, link) - } - title := props["title"] - if title == "" { - title = props["alt"] - } - if title == "" { - title = path.Base(name) - } - alt := props["alt"] - if alt == "" { - alt = name } + urlPrefix := ctx.URLPrefix + if image { + if !absoluteLink { + if IsSameDomain(urlPrefix) { + urlPrefix = strings.Replace(urlPrefix, "/src/", "/raw/", 1) + } + if ctx.IsWiki { + link = util.URLJoin("wiki", "raw", link) + } + link = util.URLJoin(urlPrefix, link) + } + title := props["title"] + if title == "" { + title = props["alt"] + } + if title == "" { + title = path.Base(name) + } + alt := props["alt"] + if alt == "" { + alt = name + } - // make the childNode an image - if we can, we also place the alt - childNode.Type = html.ElementNode - childNode.Data = "img" - childNode.DataAtom = atom.Img - childNode.Attr = []html.Attribute{ - {Key: "src", Val: link}, - {Key: "title", Val: title}, - {Key: "alt", Val: alt}, - } - if alt == "" { - childNode.Attr = childNode.Attr[:2] - } - } else { - if !absoluteLink { - if ctx.IsWiki { - link = util.URLJoin("wiki", link) + // make the childNode an image - if we can, we also place the alt + childNode.Type = html.ElementNode + childNode.Data = "img" + childNode.DataAtom = atom.Img + childNode.Attr = []html.Attribute{ + {Key: "src", Val: link}, + {Key: "title", Val: title}, + {Key: "alt", Val: alt}, } - link = util.URLJoin(urlPrefix, link) + if alt == "" { + childNode.Attr = childNode.Attr[:2] + } + } else { + if !absoluteLink { + if ctx.IsWiki { + link = util.URLJoin("wiki", link) + } + link = util.URLJoin(urlPrefix, link) + } + childNode.Type = html.TextNode + childNode.Data = name } - childNode.Type = html.TextNode - childNode.Data = name - } - if noLink { - linkNode = childNode - } else { - linkNode.Attr = []html.Attribute{{Key: "href", Val: link}} + if noLink { + linkNode = childNode + } else { + linkNode.Attr = []html.Attribute{{Key: "href", Val: link}} + } + replaceContent(node, m[0], m[1], linkNode) + node = node.NextSibling.NextSibling } - replaceContent(node, m[0], m[1], linkNode) } func fullIssuePatternProcessor(ctx *RenderContext, node *html.Node) { if ctx.Metas == nil { return } - m := getIssueFullPattern().FindStringSubmatchIndex(node.Data) - if m == nil { - return - } - link := node.Data[m[0]:m[1]] - id := "#" + node.Data[m[2]:m[3]] - - // extract repo and org name from matched link like - // http://localhost:3000/gituser/myrepo/issues/1 - linkParts := strings.Split(path.Clean(link), "/") - matchOrg := linkParts[len(linkParts)-4] - matchRepo := linkParts[len(linkParts)-3] - - if matchOrg == ctx.Metas["user"] && matchRepo == ctx.Metas["repo"] { - // TODO if m[4]:m[5] is not nil, then link is to a comment, - // and we should indicate that in the text somehow - replaceContent(node, m[0], m[1], createLink(link, id, "ref-issue")) - - } else { - orgRepoID := matchOrg + "/" + matchRepo + id - replaceContent(node, m[0], m[1], createLink(link, orgRepoID, "ref-issue")) + + next := node.NextSibling + for node != nil && node != next { + m := getIssueFullPattern().FindStringSubmatchIndex(node.Data) + if m == nil { + return + } + link := node.Data[m[0]:m[1]] + id := "#" + node.Data[m[2]:m[3]] + + // extract repo and org name from matched link like + // http://localhost:3000/gituser/myrepo/issues/1 + linkParts := strings.Split(path.Clean(link), "/") + matchOrg := linkParts[len(linkParts)-4] + matchRepo := linkParts[len(linkParts)-3] + + if matchOrg == ctx.Metas["user"] && matchRepo == ctx.Metas["repo"] { + // TODO if m[4]:m[5] is not nil, then link is to a comment, + // and we should indicate that in the text somehow + replaceContent(node, m[0], m[1], createLink(link, id, "ref-issue")) + } else { + orgRepoID := matchOrg + "/" + matchRepo + id + replaceContent(node, m[0], m[1], createLink(link, orgRepoID, "ref-issue")) + } + node = node.NextSibling.NextSibling } } @@ -787,70 +803,74 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) { if ctx.Metas == nil { return } - var ( found bool ref *references.RenderizableReference ) - _, exttrack := ctx.Metas["format"] - alphanum := ctx.Metas["style"] == IssueNameStyleAlphanumeric - - // Repos with external issue trackers might still need to reference local PRs - // We need to concern with the first one that shows up in the text, whichever it is - found, ref = references.FindRenderizableReferenceNumeric(node.Data, exttrack && alphanum) - if exttrack && alphanum { - if found2, ref2 := references.FindRenderizableReferenceAlphanumeric(node.Data); found2 { - if !found || ref2.RefLocation.Start < ref.RefLocation.Start { - found = true - ref = ref2 + next := node.NextSibling + for node != nil && node != next { + _, exttrack := ctx.Metas["format"] + alphanum := ctx.Metas["style"] == IssueNameStyleAlphanumeric + + // Repos with external issue trackers might still need to reference local PRs + // We need to concern with the first one that shows up in the text, whichever it is + found, ref = references.FindRenderizableReferenceNumeric(node.Data, exttrack && alphanum) + if exttrack && alphanum { + if found2, ref2 := references.FindRenderizableReferenceAlphanumeric(node.Data); found2 { + if !found || ref2.RefLocation.Start < ref.RefLocation.Start { + found = true + ref = ref2 + } } } - } - if !found { - return - } - - var link *html.Node - reftext := node.Data[ref.RefLocation.Start:ref.RefLocation.End] - if exttrack && !ref.IsPull { - ctx.Metas["index"] = ref.Issue - link = createLink(com.Expand(ctx.Metas["format"], ctx.Metas), reftext, "ref-issue") - } else { - // Path determines the type of link that will be rendered. It's unknown at this point whether - // the linked item is actually a PR or an issue. Luckily it's of no real consequence because - // Gitea will redirect on click as appropriate. - path := "issues" - if ref.IsPull { - path = "pulls" + if !found { + return } - if ref.Owner == "" { - link = createLink(util.URLJoin(setting.AppURL, ctx.Metas["user"], ctx.Metas["repo"], path, ref.Issue), reftext, "ref-issue") + + var link *html.Node + reftext := node.Data[ref.RefLocation.Start:ref.RefLocation.End] + if exttrack && !ref.IsPull { + ctx.Metas["index"] = ref.Issue + link = createLink(com.Expand(ctx.Metas["format"], ctx.Metas), reftext, "ref-issue") } else { - link = createLink(util.URLJoin(setting.AppURL, ref.Owner, ref.Name, path, ref.Issue), reftext, "ref-issue") + // Path determines the type of link that will be rendered. It's unknown at this point whether + // the linked item is actually a PR or an issue. Luckily it's of no real consequence because + // Gitea will redirect on click as appropriate. + path := "issues" + if ref.IsPull { + path = "pulls" + } + if ref.Owner == "" { + link = createLink(util.URLJoin(setting.AppURL, ctx.Metas["user"], ctx.Metas["repo"], path, ref.Issue), reftext, "ref-issue") + } else { + link = createLink(util.URLJoin(setting.AppURL, ref.Owner, ref.Name, path, ref.Issue), reftext, "ref-issue") + } } - } - if ref.Action == references.XRefActionNone { - replaceContent(node, ref.RefLocation.Start, ref.RefLocation.End, link) - return - } + if ref.Action == references.XRefActionNone { + replaceContent(node, ref.RefLocation.Start, ref.RefLocation.End, link) + node = node.NextSibling.NextSibling + continue + } - // Decorate action keywords if actionable - var keyword *html.Node - if references.IsXrefActionable(ref, exttrack, alphanum) { - keyword = createKeyword(node.Data[ref.ActionLocation.Start:ref.ActionLocation.End]) - } else { - keyword = &html.Node{ + // Decorate action keywords if actionable + var keyword *html.Node + if references.IsXrefActionable(ref, exttrack, alphanum) { + keyword = createKeyword(node.Data[ref.ActionLocation.Start:ref.ActionLocation.End]) + } else { + keyword = &html.Node{ + Type: html.TextNode, + Data: node.Data[ref.ActionLocation.Start:ref.ActionLocation.End], + } + } + spaces := &html.Node{ Type: html.TextNode, - Data: node.Data[ref.ActionLocation.Start:ref.ActionLocation.End], + Data: node.Data[ref.ActionLocation.End:ref.RefLocation.Start], } + replaceContentList(node, ref.ActionLocation.Start, ref.RefLocation.End, []*html.Node{keyword, spaces, link}) + node = node.NextSibling.NextSibling.NextSibling.NextSibling } - spaces := &html.Node{ - Type: html.TextNode, - Data: node.Data[ref.ActionLocation.End:ref.RefLocation.Start], - } - replaceContentList(node, ref.ActionLocation.Start, ref.RefLocation.End, []*html.Node{keyword, spaces, link}) } // fullSha1PatternProcessor renders SHA containing URLs @@ -858,86 +878,112 @@ func fullSha1PatternProcessor(ctx *RenderContext, node *html.Node) { if ctx.Metas == nil { return } - m := anySHA1Pattern.FindStringSubmatchIndex(node.Data) - if m == nil { - return - } - urlFull := node.Data[m[0]:m[1]] - text := base.ShortSha(node.Data[m[2]:m[3]]) + next := node.NextSibling + for node != nil && node != next { + m := anySHA1Pattern.FindStringSubmatchIndex(node.Data) + if m == nil { + return + } - // 3rd capture group matches a optional path - subpath := "" - if m[5] > 0 { - subpath = node.Data[m[4]:m[5]] - } + urlFull := node.Data[m[0]:m[1]] + text := base.ShortSha(node.Data[m[2]:m[3]]) - // 4th capture group matches a optional url hash - hash := "" - if m[7] > 0 { - hash = node.Data[m[6]:m[7]][1:] - } + // 3rd capture group matches a optional path + subpath := "" + if m[5] > 0 { + subpath = node.Data[m[4]:m[5]] + } - start := m[0] - end := m[1] + // 4th capture group matches a optional url hash + hash := "" + if m[7] > 0 { + hash = node.Data[m[6]:m[7]][1:] + } - // If url ends in '.', it's very likely that it is not part of the - // actual url but used to finish a sentence. - if strings.HasSuffix(urlFull, ".") { - end-- - urlFull = urlFull[:len(urlFull)-1] - if hash != "" { - hash = hash[:len(hash)-1] - } else if subpath != "" { - subpath = subpath[:len(subpath)-1] + start := m[0] + end := m[1] + + // If url ends in '.', it's very likely that it is not part of the + // actual url but used to finish a sentence. + if strings.HasSuffix(urlFull, ".") { + end-- + urlFull = urlFull[:len(urlFull)-1] + if hash != "" { + hash = hash[:len(hash)-1] + } else if subpath != "" { + subpath = subpath[:len(subpath)-1] + } } - } - if subpath != "" { - text += subpath - } + if subpath != "" { + text += subpath + } - if hash != "" { - text += " (" + hash + ")" - } + if hash != "" { + text += " (" + hash + ")" + } - replaceContent(node, start, end, createCodeLink(urlFull, text, "commit")) + replaceContent(node, start, end, createCodeLink(urlFull, text, "commit")) + node = node.NextSibling.NextSibling + } } // emojiShortCodeProcessor for rendering text like :smile: into emoji func emojiShortCodeProcessor(ctx *RenderContext, node *html.Node) { - m := EmojiShortCodeRegex.FindStringSubmatchIndex(node.Data) - if m == nil { - return - } - - alias := node.Data[m[0]:m[1]] - alias = strings.ReplaceAll(alias, ":", "") - converted := emoji.FromAlias(alias) - if converted == nil { - // check if this is a custom reaction - s := strings.Join(setting.UI.Reactions, " ") + "gitea" - if strings.Contains(s, alias) { - replaceContent(node, m[0], m[1], createCustomEmoji(alias, "emoji")) + start := 0 + next := node.NextSibling + for node != nil && node != next && start < len(node.Data) { + m := EmojiShortCodeRegex.FindStringSubmatchIndex(node.Data[start:]) + if m == nil { return } - return - } + m[0] += start + m[1] += start + + start = m[1] + + alias := node.Data[m[0]:m[1]] + alias = strings.ReplaceAll(alias, ":", "") + converted := emoji.FromAlias(alias) + if converted == nil { + // check if this is a custom reaction + s := strings.Join(setting.UI.Reactions, " ") + "gitea" + if strings.Contains(s, alias) { + replaceContent(node, m[0], m[1], createCustomEmoji(alias, "emoji")) + node = node.NextSibling.NextSibling + start = 0 + continue + } + continue + } - replaceContent(node, m[0], m[1], createEmoji(converted.Emoji, "emoji", converted.Description)) + replaceContent(node, m[0], m[1], createEmoji(converted.Emoji, "emoji", converted.Description)) + node = node.NextSibling.NextSibling + start = 0 + } } // emoji processor to match emoji and add emoji class func emojiProcessor(ctx *RenderContext, node *html.Node) { - m := emoji.FindEmojiSubmatchIndex(node.Data) - if m == nil { - return - } - - codepoint := node.Data[m[0]:m[1]] - val := emoji.FromCode(codepoint) - if val != nil { - replaceContent(node, m[0], m[1], createEmoji(codepoint, "emoji", val.Description)) + start := 0 + next := node.NextSibling + for node != nil && node != next && start < len(node.Data) { + m := emoji.FindEmojiSubmatchIndex(node.Data[start:]) + if m == nil { + return + } + m[0] += start + m[1] += start + + codepoint := node.Data[m[0]:m[1]] + start = m[1] + val := emoji.FromCode(codepoint) + if val != nil { + replaceContent(node, m[0], m[1], createEmoji(codepoint, "emoji", val.Description)) + node = node.NextSibling.NextSibling + start = 0 + } } } @@ -947,49 +993,70 @@ func sha1CurrentPatternProcessor(ctx *RenderContext, node *html.Node) { if ctx.Metas == nil || ctx.Metas["user"] == "" || ctx.Metas["repo"] == "" || ctx.Metas["repoPath"] == "" { return } - m := sha1CurrentPattern.FindStringSubmatchIndex(node.Data) - if m == nil { - return - } - hash := node.Data[m[2]:m[3]] - // The regex does not lie, it matches the hash pattern. - // However, a regex cannot know if a hash actually exists or not. - // We could assume that a SHA1 hash should probably contain alphas AND numerics - // but that is not always the case. - // Although unlikely, deadbeef and 1234567 are valid short forms of SHA1 hash - // as used by git and github for linking and thus we have to do similar. - // Because of this, we check to make sure that a matched hash is actually - // a commit in the repository before making it a link. - if _, err := git.NewCommand("rev-parse", "--verify", hash).RunInDirBytes(ctx.Metas["repoPath"]); err != nil { - if !strings.Contains(err.Error(), "fatal: Needed a single revision") { - log.Debug("sha1CurrentPatternProcessor git rev-parse: %v", err) + + start := 0 + next := node.NextSibling + for node != nil && node != next && start < len(node.Data) { + m := sha1CurrentPattern.FindStringSubmatchIndex(node.Data[start:]) + if m == nil { + return + } + m[2] += start + m[3] += start + + hash := node.Data[m[2]:m[3]] + // The regex does not lie, it matches the hash pattern. + // However, a regex cannot know if a hash actually exists or not. + // We could assume that a SHA1 hash should probably contain alphas AND numerics + // but that is not always the case. + // Although unlikely, deadbeef and 1234567 are valid short forms of SHA1 hash + // as used by git and github for linking and thus we have to do similar. + // Because of this, we check to make sure that a matched hash is actually + // a commit in the repository before making it a link. + if _, err := git.NewCommand("rev-parse", "--verify", hash).RunInDirBytes(ctx.Metas["repoPath"]); err != nil { + if !strings.Contains(err.Error(), "fatal: Needed a single revision") { + log.Debug("sha1CurrentPatternProcessor git rev-parse: %v", err) + } + start = m[3] + continue } - return - } - replaceContent(node, m[2], m[3], - createCodeLink(util.URLJoin(setting.AppURL, ctx.Metas["user"], ctx.Metas["repo"], "commit", hash), base.ShortSha(hash), "commit")) + replaceContent(node, m[2], m[3], + createCodeLink(util.URLJoin(setting.AppURL, ctx.Metas["user"], ctx.Metas["repo"], "commit", hash), base.ShortSha(hash), "commit")) + start = 0 + node = node.NextSibling.NextSibling + } } // emailAddressProcessor replaces raw email addresses with a mailto: link. func emailAddressProcessor(ctx *RenderContext, node *html.Node) { - m := emailRegex.FindStringSubmatchIndex(node.Data) - if m == nil { - return + next := node.NextSibling + for node != nil && node != next { + m := emailRegex.FindStringSubmatchIndex(node.Data) + if m == nil { + return + } + + mail := node.Data[m[2]:m[3]] + replaceContent(node, m[2], m[3], createLink("mailto:"+mail, mail, "mailto")) + node = node.NextSibling.NextSibling } - mail := node.Data[m[2]:m[3]] - replaceContent(node, m[2], m[3], createLink("mailto:"+mail, mail, "mailto")) } // linkProcessor creates links for any HTTP or HTTPS URL not captured by // markdown. func linkProcessor(ctx *RenderContext, node *html.Node) { - m := common.LinkRegex.FindStringIndex(node.Data) - if m == nil { - return + next := node.NextSibling + for node != nil && node != next { + m := common.LinkRegex.FindStringIndex(node.Data) + if m == nil { + return + } + + uri := node.Data[m[0]:m[1]] + replaceContent(node, m[0], m[1], createLink(uri, uri, "link")) + node = node.NextSibling.NextSibling } - uri := node.Data[m[0]:m[1]] - replaceContent(node, m[0], m[1], createLink(uri, uri, "link")) } func genDefaultLinkProcessor(defaultLink string) processor { @@ -1013,12 +1080,17 @@ func genDefaultLinkProcessor(defaultLink string) processor { // descriptionLinkProcessor creates links for DescriptionHTML func descriptionLinkProcessor(ctx *RenderContext, node *html.Node) { - m := common.LinkRegex.FindStringIndex(node.Data) - if m == nil { - return + next := node.NextSibling + for node != nil && node != next { + m := common.LinkRegex.FindStringIndex(node.Data) + if m == nil { + return + } + + uri := node.Data[m[0]:m[1]] + replaceContent(node, m[0], m[1], createDescriptionLink(uri, uri)) + node = node.NextSibling.NextSibling } - uri := node.Data[m[0]:m[1]] - replaceContent(node, m[0], m[1], createDescriptionLink(uri, uri)) } func createDescriptionLink(href, content string) *html.Node { diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index fa8c848601fe6..4cdd5798c8a9f 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -444,3 +444,39 @@ func Test_ParseClusterFuzz(t *testing.T) { assert.NoError(t, err) assert.NotContains(t, res.String(), "` + + var res strings.Builder + err := PostProcess(&RenderContext{ + URLPrefix: "https://example.com", + Metas: localMetas, + }, strings.NewReader(data), &res) + assert.NoError(t, err) + assert.Equal(t, data, res.String()) +} + +func BenchmarkEmojiPostprocess(b *testing.B) { + data := "🥰 " + for len(data) < 1<<16 { + data += data + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + var res strings.Builder + err := PostProcess(&RenderContext{ + URLPrefix: "https://example.com", + Metas: localMetas, + }, strings.NewReader(data), &res) + assert.NoError(b, err) + } +} diff --git a/modules/markup/markdown/meta_test.go b/modules/markup/markdown/meta_test.go index a585f0382f816..f525777a54c15 100644 --- a/modules/markup/markdown/meta_test.go +++ b/modules/markup/markdown/meta_test.go @@ -18,7 +18,7 @@ func TestExtractMetadata(t *testing.T) { var meta structs.IssueTemplate body, err := ExtractMetadata(fmt.Sprintf("%s\n%s\n%s\n%s", sepTest, frontTest, sepTest, bodyTest), &meta) assert.NoError(t, err) - assert.Equal(t, body, bodyTest) + assert.Equal(t, bodyTest, body) assert.Equal(t, metaTest, meta) assert.True(t, meta.Valid()) }) @@ -39,7 +39,7 @@ func TestExtractMetadata(t *testing.T) { var meta structs.IssueTemplate body, err := ExtractMetadata(fmt.Sprintf("%s\n%s\n%s", sepTest, frontTest, sepTest), &meta) assert.NoError(t, err) - assert.Equal(t, body, "") + assert.Equal(t, "", body) assert.Equal(t, metaTest, meta) assert.True(t, meta.Valid()) }) diff --git a/modules/markup/renderer.go b/modules/markup/renderer.go index 7cc81574ba0e1..5d35bd5a67715 100644 --- a/modules/markup/renderer.go +++ b/modules/markup/renderer.go @@ -48,6 +48,7 @@ type RenderContext struct { type Renderer interface { Name() string // markup format name Extensions() []string + NeedPostProcess() bool Render(ctx *RenderContext, input io.Reader, output io.Writer) error } @@ -94,7 +95,7 @@ func RenderString(ctx *RenderContext, content string) (string, error) { return buf.String(), nil } -func render(ctx *RenderContext, parser Renderer, input io.Reader, output io.Writer) error { +func render(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Writer) error { var wg sync.WaitGroup var err error pr, pw := io.Pipe() @@ -103,29 +104,38 @@ func render(ctx *RenderContext, parser Renderer, input io.Reader, output io.Writ _ = pw.Close() }() - pr2, pw2 := io.Pipe() - defer func() { - _ = pr2.Close() - _ = pw2.Close() - }() - - wg.Add(1) - go func() { - buf := SanitizeReader(pr2) - _, err = io.Copy(output, buf) - _ = pr2.Close() - wg.Done() - }() - - wg.Add(1) - go func() { - err = PostProcess(ctx, pr, pw2) - _ = pr.Close() - _ = pw2.Close() - wg.Done() - }() - - if err1 := parser.Render(ctx, input, pw); err1 != nil { + if renderer.NeedPostProcess() { + pr2, pw2 := io.Pipe() + defer func() { + _ = pr2.Close() + _ = pw2.Close() + }() + + wg.Add(1) + go func() { + buf := SanitizeReader(pr2) + _, err = io.Copy(output, buf) + _ = pr2.Close() + wg.Done() + }() + + wg.Add(1) + go func() { + err = PostProcess(ctx, pr, pw2) + _ = pr.Close() + _ = pw2.Close() + wg.Done() + }() + } else { + wg.Add(1) + go func() { + buf := SanitizeReader(pr) + _, err = io.Copy(output, buf) + _ = pr.Close() + wg.Done() + }() + } + if err1 := renderer.Render(ctx, input, pw); err1 != nil { return err1 } _ = pw.Close() diff --git a/modules/markup/sanitizer.go b/modules/markup/sanitizer.go index 0e05ddb085e29..5611bd06ad00c 100644 --- a/modules/markup/sanitizer.go +++ b/modules/markup/sanitizer.go @@ -50,9 +50,6 @@ func ReplaceSanitizer() { sanitizer.policy.AllowURLSchemes(setting.Markdown.CustomURLSchemes...) } - // Allow keyword markup - sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`^` + keywordClass + `$`)).OnElements("span") - // Allow classes for anchors sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`ref-issue`)).OnElements("a") @@ -68,8 +65,8 @@ func ReplaceSanitizer() { // Allow classes for emojis sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`emoji`)).OnElements("img") - // Allow icons, emojis, and chroma syntax on span - sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`^((icon(\s+[\p{L}\p{N}_-]+)+)|(emoji))$|^([a-z][a-z0-9]{0,2})$`)).OnElements("span") + // Allow icons, emojis, chroma syntax and keyword markup on span + sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`^((icon(\s+[\p{L}\p{N}_-]+)+)|(emoji))$|^([a-z][a-z0-9]{0,2})$|^` + keywordClass + `$`)).OnElements("span") // Allow data tables sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`data-table`)).OnElements("table") @@ -131,13 +128,3 @@ func SanitizeReader(r io.Reader) *bytes.Buffer { NewSanitizer() return sanitizer.policy.SanitizeReader(r) } - -// SanitizeBytes takes a []byte slice that contains a HTML fragment or document and applies policy whitelist. -func SanitizeBytes(b []byte) []byte { - if len(b) == 0 { - // nothing to sanitize - return b - } - NewSanitizer() - return sanitizer.policy.SanitizeBytes(b) -} diff --git a/modules/markup/sanitizer_test.go b/modules/markup/sanitizer_test.go index 9e173015d6611..64189e143523d 100644 --- a/modules/markup/sanitizer_test.go +++ b/modules/markup/sanitizer_test.go @@ -49,7 +49,6 @@ func Test_Sanitizer(t *testing.T) { for i := 0; i < len(testCases); i += 2 { assert.Equal(t, testCases[i+1], Sanitize(testCases[i])) - assert.Equal(t, testCases[i+1], string(SanitizeBytes([]byte(testCases[i])))) } } diff --git a/modules/migrations/base/messenger.go b/modules/migrations/base/messenger.go new file mode 100644 index 0000000000000..a92f59ef7fae0 --- /dev/null +++ b/modules/migrations/base/messenger.go @@ -0,0 +1,11 @@ +// 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 base + +// Messenger is a formatting function similar to i18n.Tr +type Messenger func(key string, args ...interface{}) + +// NilMessenger represents an empty formatting function +func NilMessenger(string, ...interface{}) {} diff --git a/modules/migrations/base/retry_downloader.go b/modules/migrations/base/retry_downloader.go index eeb3cabbc18fb..82a038b98ba70 100644 --- a/modules/migrations/base/retry_downloader.go +++ b/modules/migrations/base/retry_downloader.go @@ -31,217 +31,166 @@ func NewRetryDownloader(ctx context.Context, downloader Downloader, retryTimes, } } -// SetContext set context -func (d *RetryDownloader) SetContext(ctx context.Context) { - d.ctx = ctx - d.Downloader.SetContext(ctx) -} - -// GetRepoInfo returns a repository information with retry -func (d *RetryDownloader) GetRepoInfo() (*Repository, error) { +func (d *RetryDownloader) retry(work func() error) error { var ( times = d.RetryTimes - repo *Repository err error ) for ; times > 0; times-- { - if repo, err = d.Downloader.GetRepoInfo(); err == nil { - return repo, nil + if err = work(); err == nil { + return nil } if IsErrNotSupported(err) { - return nil, err + return err } select { case <-d.ctx.Done(): - return nil, d.ctx.Err() + return d.ctx.Err() case <-time.After(time.Second * time.Duration(d.RetryDelay)): } } - return nil, err + return err +} + +// SetContext set context +func (d *RetryDownloader) SetContext(ctx context.Context) { + d.ctx = ctx + d.Downloader.SetContext(ctx) +} + +// GetRepoInfo returns a repository information with retry +func (d *RetryDownloader) GetRepoInfo() (*Repository, error) { + var ( + repo *Repository + err error + ) + + err = d.retry(func() error { + repo, err = d.Downloader.GetRepoInfo() + return err + }) + + return repo, err } // GetTopics returns a repository's topics with retry func (d *RetryDownloader) GetTopics() ([]string, error) { var ( - times = d.RetryTimes topics []string err error ) - for ; times > 0; times-- { - if topics, err = d.Downloader.GetTopics(); err == nil { - return topics, nil - } - if IsErrNotSupported(err) { - return nil, err - } - select { - case <-d.ctx.Done(): - return nil, d.ctx.Err() - case <-time.After(time.Second * time.Duration(d.RetryDelay)): - } - } - return nil, err + + err = d.retry(func() error { + topics, err = d.Downloader.GetTopics() + return err + }) + + return topics, err } // GetMilestones returns a repository's milestones with retry func (d *RetryDownloader) GetMilestones() ([]*Milestone, error) { var ( - times = d.RetryTimes milestones []*Milestone err error ) - for ; times > 0; times-- { - if milestones, err = d.Downloader.GetMilestones(); err == nil { - return milestones, nil - } - if IsErrNotSupported(err) { - return nil, err - } - select { - case <-d.ctx.Done(): - return nil, d.ctx.Err() - case <-time.After(time.Second * time.Duration(d.RetryDelay)): - } - } - return nil, err + + err = d.retry(func() error { + milestones, err = d.Downloader.GetMilestones() + return err + }) + + return milestones, err } // GetReleases returns a repository's releases with retry func (d *RetryDownloader) GetReleases() ([]*Release, error) { var ( - times = d.RetryTimes releases []*Release err error ) - for ; times > 0; times-- { - if releases, err = d.Downloader.GetReleases(); err == nil { - return releases, nil - } - if IsErrNotSupported(err) { - return nil, err - } - select { - case <-d.ctx.Done(): - return nil, d.ctx.Err() - case <-time.After(time.Second * time.Duration(d.RetryDelay)): - } - } - return nil, err + + err = d.retry(func() error { + releases, err = d.Downloader.GetReleases() + return err + }) + + return releases, err } // GetLabels returns a repository's labels with retry func (d *RetryDownloader) GetLabels() ([]*Label, error) { var ( - times = d.RetryTimes labels []*Label err error ) - for ; times > 0; times-- { - if labels, err = d.Downloader.GetLabels(); err == nil { - return labels, nil - } - if IsErrNotSupported(err) { - return nil, err - } - select { - case <-d.ctx.Done(): - return nil, d.ctx.Err() - case <-time.After(time.Second * time.Duration(d.RetryDelay)): - } - } - return nil, err + + err = d.retry(func() error { + labels, err = d.Downloader.GetLabels() + return err + }) + + return labels, err } // GetIssues returns a repository's issues with retry func (d *RetryDownloader) GetIssues(page, perPage int) ([]*Issue, bool, error) { var ( - times = d.RetryTimes issues []*Issue isEnd bool err error ) - for ; times > 0; times-- { - if issues, isEnd, err = d.Downloader.GetIssues(page, perPage); err == nil { - return issues, isEnd, nil - } - if IsErrNotSupported(err) { - return nil, false, err - } - select { - case <-d.ctx.Done(): - return nil, false, d.ctx.Err() - case <-time.After(time.Second * time.Duration(d.RetryDelay)): - } - } - return nil, false, err + + err = d.retry(func() error { + issues, isEnd, err = d.Downloader.GetIssues(page, perPage) + return err + }) + + return issues, isEnd, err } // GetComments returns a repository's comments with retry func (d *RetryDownloader) GetComments(issueNumber int64) ([]*Comment, error) { var ( - times = d.RetryTimes comments []*Comment err error ) - for ; times > 0; times-- { - if comments, err = d.Downloader.GetComments(issueNumber); err == nil { - return comments, nil - } - if IsErrNotSupported(err) { - return nil, err - } - select { - case <-d.ctx.Done(): - return nil, d.ctx.Err() - case <-time.After(time.Second * time.Duration(d.RetryDelay)): - } - } - return nil, err + + err = d.retry(func() error { + comments, err = d.Downloader.GetComments(issueNumber) + return err + }) + + return comments, err } // GetPullRequests returns a repository's pull requests with retry func (d *RetryDownloader) GetPullRequests(page, perPage int) ([]*PullRequest, bool, error) { var ( - times = d.RetryTimes prs []*PullRequest err error isEnd bool ) - for ; times > 0; times-- { - if prs, isEnd, err = d.Downloader.GetPullRequests(page, perPage); err == nil { - return prs, isEnd, nil - } - if IsErrNotSupported(err) { - return nil, false, err - } - select { - case <-d.ctx.Done(): - return nil, false, d.ctx.Err() - case <-time.After(time.Second * time.Duration(d.RetryDelay)): - } - } - return nil, false, err + + err = d.retry(func() error { + prs, isEnd, err = d.Downloader.GetPullRequests(page, perPage) + return err + }) + + return prs, isEnd, err } // GetReviews returns pull requests reviews func (d *RetryDownloader) GetReviews(pullRequestNumber int64) ([]*Review, error) { var ( - times = d.RetryTimes reviews []*Review err error ) - for ; times > 0; times-- { - if reviews, err = d.Downloader.GetReviews(pullRequestNumber); err == nil { - return reviews, nil - } - if IsErrNotSupported(err) { - return nil, err - } - select { - case <-d.ctx.Done(): - return nil, d.ctx.Err() - case <-time.After(time.Second * time.Duration(d.RetryDelay)): - } - } - return nil, err + + err = d.retry(func() error { + reviews, err = d.Downloader.GetReviews(pullRequestNumber) + return err + }) + + return reviews, err } diff --git a/modules/migrations/dump.go b/modules/migrations/dump.go index 4a18c47ae5bec..6c4cf174d4fc6 100644 --- a/modules/migrations/dump.go +++ b/modules/migrations/dump.go @@ -555,7 +555,7 @@ func DumpRepository(ctx context.Context, baseDir, ownerName string, opts base.Mi return err } - if err := migrateRepository(downloader, uploader, opts); err != nil { + if err := migrateRepository(downloader, uploader, opts, nil); err != nil { if err1 := uploader.Rollback(); err1 != nil { log.Error("rollback failed: %v", err1) } @@ -620,7 +620,7 @@ func RestoreRepository(ctx context.Context, baseDir string, ownerName, repoName } updateOptionsUnits(&migrateOpts, units) - if err = migrateRepository(downloader, uploader, migrateOpts); err != nil { + if err = migrateRepository(downloader, uploader, migrateOpts, nil); err != nil { if err1 := uploader.Rollback(); err1 != nil { log.Error("rollback failed: %v", err1) } diff --git a/modules/migrations/gitea_downloader_test.go b/modules/migrations/gitea_downloader_test.go index c52c1225f401d..babf038280d7c 100644 --- a/modules/migrations/gitea_downloader_test.go +++ b/modules/migrations/gitea_downloader_test.go @@ -158,13 +158,13 @@ func TestGiteaDownloadRepo(t *testing.T) { issues, isEnd, err := downloader.GetIssues(1, 50) assert.NoError(t, err) - assert.EqualValues(t, 7, len(issues)) + assert.Len(t, issues, 7) assert.True(t, isEnd) assert.EqualValues(t, "open", issues[0].State) issues, isEnd, err = downloader.GetIssues(3, 2) assert.NoError(t, err) - assert.EqualValues(t, 2, len(issues)) + assert.Len(t, issues, 2) assert.False(t, isEnd) var ( diff --git a/modules/migrations/gitea_uploader.go b/modules/migrations/gitea_uploader.go index 217acb00e2156..2b18098b7f161 100644 --- a/modules/migrations/gitea_uploader.go +++ b/modules/migrations/gitea_uploader.go @@ -276,19 +276,22 @@ func (g *GiteaLocalUploader) CreateReleases(releases ...*base.Release) error { // asset.DownloadURL maybe a local file var rc io.ReadCloser var err error - if asset.DownloadURL == nil { + if asset.DownloadFunc != nil { rc, err = asset.DownloadFunc() if err != nil { return err } - } else { + } else if asset.DownloadURL != nil { rc, err = uri.Open(*asset.DownloadURL) if err != nil { return err } } - defer rc.Close() + if rc == nil { + return nil + } _, err = storage.Attachments.Save(attach.RelativePath(), rc, int64(*asset.Size)) + rc.Close() return err }() if err != nil { diff --git a/modules/migrations/gitea_uploader_test.go b/modules/migrations/gitea_uploader_test.go index 3c7def467582e..5f36d545846dd 100644 --- a/modules/migrations/gitea_uploader_test.go +++ b/modules/migrations/gitea_uploader_test.go @@ -47,7 +47,7 @@ func TestGiteaUploadRepo(t *testing.T) { PullRequests: true, Private: true, Mirror: false, - }) + }, nil) assert.NoError(t, err) repo := models.AssertExistsAndLoadBean(t, &models.Repository{OwnerID: user.ID, Name: repoName}).(*models.Repository) @@ -59,18 +59,18 @@ func TestGiteaUploadRepo(t *testing.T) { State: structs.StateOpen, }) assert.NoError(t, err) - assert.EqualValues(t, 1, len(milestones)) + assert.Len(t, milestones, 1) milestones, err = models.GetMilestones(models.GetMilestonesOption{ RepoID: repo.ID, State: structs.StateClosed, }) assert.NoError(t, err) - assert.EqualValues(t, 0, len(milestones)) + assert.Empty(t, milestones) labels, err := models.GetLabelsByRepoID(repo.ID, "", models.ListOptions{}) assert.NoError(t, err) - assert.EqualValues(t, 11, len(labels)) + assert.Len(t, labels, 11) releases, err := models.GetReleasesByRepoID(repo.ID, models.FindReleasesOptions{ ListOptions: models.ListOptions{ @@ -80,7 +80,7 @@ func TestGiteaUploadRepo(t *testing.T) { IncludeTags: true, }) assert.NoError(t, err) - assert.EqualValues(t, 8, len(releases)) + assert.Len(t, releases, 8) releases, err = models.GetReleasesByRepoID(repo.ID, models.FindReleasesOptions{ ListOptions: models.ListOptions{ @@ -90,7 +90,7 @@ func TestGiteaUploadRepo(t *testing.T) { IncludeTags: false, }) assert.NoError(t, err) - assert.EqualValues(t, 1, len(releases)) + assert.Len(t, releases, 1) issues, err := models.Issues(&models.IssuesOptions{ RepoIDs: []int64{repo.ID}, @@ -98,16 +98,16 @@ func TestGiteaUploadRepo(t *testing.T) { SortType: "oldest", }) assert.NoError(t, err) - assert.EqualValues(t, 14, len(issues)) + assert.Len(t, issues, 14) assert.NoError(t, issues[0].LoadDiscussComments()) - assert.EqualValues(t, 0, len(issues[0].Comments)) + assert.Empty(t, issues[0].Comments) pulls, _, err := models.PullRequests(repo.ID, &models.PullRequestsOptions{ SortType: "oldest", }) assert.NoError(t, err) - assert.EqualValues(t, 34, len(pulls)) + assert.Len(t, pulls, 34) assert.NoError(t, pulls[0].LoadIssue()) assert.NoError(t, pulls[0].Issue.LoadDiscussComments()) - assert.EqualValues(t, 2, len(pulls[0].Issue.Comments)) + assert.Len(t, pulls[0].Issue.Comments, 2) } diff --git a/modules/migrations/github_test.go b/modules/migrations/github_test.go index efa8b6ba9bbb3..5bd980a9d8aa5 100644 --- a/modules/migrations/github_test.go +++ b/modules/migrations/github_test.go @@ -147,7 +147,7 @@ func TestGitHubDownloadRepo(t *testing.T) { // downloader.GetIssues() issues, isEnd, err := downloader.GetIssues(1, 2) assert.NoError(t, err) - assert.EqualValues(t, 2, len(issues)) + assert.Len(t, issues, 2) assert.False(t, isEnd) var ( @@ -242,7 +242,7 @@ func TestGitHubDownloadRepo(t *testing.T) { // downloader.GetComments() comments, err := downloader.GetComments(2) assert.NoError(t, err) - assert.EqualValues(t, 2, len(comments)) + assert.Len(t, comments, 2) assert.EqualValues(t, []*base.Comment{ { IssueIndex: 2, @@ -273,7 +273,7 @@ func TestGitHubDownloadRepo(t *testing.T) { // downloader.GetPullRequests() prs, _, err := downloader.GetPullRequests(1, 2) assert.NoError(t, err) - assert.EqualValues(t, 2, len(prs)) + assert.Len(t, prs, 2) closed1 = time.Date(2019, 11, 12, 21, 39, 27, 0, time.UTC) var merged1 = time.Date(2019, 11, 12, 21, 39, 27, 0, time.UTC) diff --git a/modules/migrations/gitlab_test.go b/modules/migrations/gitlab_test.go index f64d72147cf5c..32ed6d807a23b 100644 --- a/modules/migrations/gitlab_test.go +++ b/modules/migrations/gitlab_test.go @@ -115,7 +115,7 @@ func TestGitlabDownloadRepo(t *testing.T) { issues, isEnd, err := downloader.GetIssues(1, 2) assert.NoError(t, err) - assert.EqualValues(t, 2, len(issues)) + assert.Len(t, issues, 2) assert.False(t, isEnd) var ( @@ -206,7 +206,7 @@ func TestGitlabDownloadRepo(t *testing.T) { comments, err := downloader.GetComments(2) assert.NoError(t, err) - assert.EqualValues(t, 4, len(comments)) + assert.Len(t, comments, 4) assert.EqualValues(t, []*base.Comment{ { IssueIndex: 2, diff --git a/modules/migrations/gogs_test.go b/modules/migrations/gogs_test.go index c240ae6432acc..d173952b068e6 100644 --- a/modules/migrations/gogs_test.go +++ b/modules/migrations/gogs_test.go @@ -80,7 +80,7 @@ func TestGogsDownloadRepo(t *testing.T) { // downloader.GetIssues() issues, isEnd, err := downloader.GetIssues(1, 8) assert.NoError(t, err) - assert.EqualValues(t, 1, len(issues)) + assert.Len(t, issues, 1) assert.False(t, isEnd) assert.EqualValues(t, []*base.Issue{ @@ -105,7 +105,7 @@ func TestGogsDownloadRepo(t *testing.T) { // downloader.GetComments() comments, err := downloader.GetComments(1) assert.NoError(t, err) - assert.EqualValues(t, 1, len(comments)) + assert.Len(t, comments, 1) assert.EqualValues(t, []*base.Comment{ { PosterName: "lunny", diff --git a/modules/migrations/migrate.go b/modules/migrations/migrate.go index 2f8889e67b531..3cdf68ab627da 100644 --- a/modules/migrations/migrate.go +++ b/modules/migrations/migrate.go @@ -99,7 +99,7 @@ func IsMigrateURLAllowed(remoteURL string, doer *models.User) error { } // MigrateRepository migrate repository according MigrateOptions -func MigrateRepository(ctx context.Context, doer *models.User, ownerName string, opts base.MigrateOptions) (*models.Repository, error) { +func MigrateRepository(ctx context.Context, doer *models.User, ownerName string, opts base.MigrateOptions, messenger base.Messenger) (*models.Repository, error) { err := IsMigrateURLAllowed(opts.CloneAddr, doer) if err != nil { return nil, err @@ -118,7 +118,7 @@ func MigrateRepository(ctx context.Context, doer *models.User, ownerName string, var uploader = NewGiteaLocalUploader(ctx, doer, ownerName, opts.RepoName) uploader.gitServiceType = opts.GitServiceType - if err := migrateRepository(downloader, uploader, opts); err != nil { + if err := migrateRepository(downloader, uploader, opts, messenger); err != nil { if err1 := uploader.Rollback(); err1 != nil { log.Error("rollback failed: %v", err1) } @@ -167,7 +167,11 @@ func newDownloader(ctx context.Context, ownerName string, opts base.MigrateOptio // migrateRepository will download information and then upload it to Uploader, this is a simple // process for small repository. For a big repository, save all the data to disk // before upload is better -func migrateRepository(downloader base.Downloader, uploader base.Uploader, opts base.MigrateOptions) error { +func migrateRepository(downloader base.Downloader, uploader base.Uploader, opts base.MigrateOptions, messenger base.Messenger) error { + if messenger == nil { + messenger = base.NilMessenger + } + repo, err := downloader.GetRepoInfo() if err != nil { if !base.IsErrNotSupported(err) { @@ -184,13 +188,15 @@ func migrateRepository(downloader base.Downloader, uploader base.Uploader, opts return err } - log.Trace("migrating git data") + log.Trace("migrating git data from %s", repo.CloneURL) + messenger("repo.migrate.migrating_git") if err = uploader.CreateRepo(repo, opts); err != nil { return err } defer uploader.Close() log.Trace("migrating topics") + messenger("repo.migrate.migrating_topics") topics, err := downloader.GetTopics() if err != nil { if !base.IsErrNotSupported(err) { @@ -206,6 +212,7 @@ func migrateRepository(downloader base.Downloader, uploader base.Uploader, opts if opts.Milestones { log.Trace("migrating milestones") + messenger("repo.migrate.migrating_milestones") milestones, err := downloader.GetMilestones() if err != nil { if !base.IsErrNotSupported(err) { @@ -229,6 +236,7 @@ func migrateRepository(downloader base.Downloader, uploader base.Uploader, opts if opts.Labels { log.Trace("migrating labels") + messenger("repo.migrate.migrating_labels") labels, err := downloader.GetLabels() if err != nil { if !base.IsErrNotSupported(err) { @@ -252,6 +260,7 @@ func migrateRepository(downloader base.Downloader, uploader base.Uploader, opts if opts.Releases { log.Trace("migrating releases") + messenger("repo.migrate.migrating_releases") releases, err := downloader.GetReleases() if err != nil { if !base.IsErrNotSupported(err) { @@ -285,6 +294,7 @@ func migrateRepository(downloader base.Downloader, uploader base.Uploader, opts if opts.Issues { log.Trace("migrating issues and comments") + messenger("repo.migrate.migrating_issues") var issueBatchSize = uploader.MaxBatchInsertSize("issue") for i := 1; ; i++ { @@ -339,6 +349,7 @@ func migrateRepository(downloader base.Downloader, uploader base.Uploader, opts if opts.PullRequests { log.Trace("migrating pull requests and comments") + messenger("repo.migrate.migrating_pulls") var prBatchSize = uploader.MaxBatchInsertSize("pullrequest") for i := 1; ; i++ { prs, isEnd, err := downloader.GetPullRequests(i, prBatchSize) diff --git a/modules/migrations/restore.go b/modules/migrations/restore.go index 4e63df14292ad..5b44811d52927 100644 --- a/modules/migrations/restore.go +++ b/modules/migrations/restore.go @@ -83,7 +83,7 @@ func (r *RepositoryRestorer) GetRepoInfo() (*base.Repository, error) { IsPrivate: isPrivate, Description: opts["description"], OriginalURL: opts["original_url"], - CloneURL: opts["clone_addr"], + CloneURL: filepath.Join(r.baseDir, "git"), DefaultBranch: opts["default_branch"], }, nil } @@ -155,7 +155,9 @@ func (r *RepositoryRestorer) GetReleases() ([]*base.Release, error) { } for _, rel := range releases { for _, asset := range rel.Assets { - *asset.DownloadURL = "file://" + filepath.Join(r.baseDir, *asset.DownloadURL) + if asset.DownloadURL != nil { + *asset.DownloadURL = "file://" + filepath.Join(r.baseDir, *asset.DownloadURL) + } } } return releases, nil diff --git a/modules/password/password_test.go b/modules/password/password_test.go index 4325086b50b80..63f98aa9c3545 100644 --- a/modules/password/password_test.go +++ b/modules/password/password_test.go @@ -54,7 +54,7 @@ func TestComplexity_Generate(t *testing.T) { for i := 0; i < maxCount; i++ { pwd, err := Generate(pwdLen) assert.NoError(t, err) - assert.Equal(t, pwdLen, len(pwd)) + assert.Len(t, pwd, pwdLen) assert.True(t, IsComplexEnough(pwd), "Failed complexities with modes %+v for generated: %s", modes, pwd) } } diff --git a/modules/queue/queue_channel_test.go b/modules/queue/queue_channel_test.go index e7abe5b50b764..f1ddd7ec92104 100644 --- a/modules/queue/queue_channel_test.go +++ b/modules/queue/queue_channel_test.go @@ -36,7 +36,7 @@ func TestChannelQueue(t *testing.T) { }, &testData{}) assert.NoError(t, err) - assert.Equal(t, queue.(*ChannelQueue).WorkerPool.boostWorkers, 5) + assert.Equal(t, 5, queue.(*ChannelQueue).WorkerPool.boostWorkers) go queue.Run(nilFn, nilFn) diff --git a/modules/queue/unique_queue_disk_channel.go b/modules/queue/unique_queue_disk_channel.go index 65a3941519954..af42c0913d4da 100644 --- a/modules/queue/unique_queue_disk_channel.go +++ b/modules/queue/unique_queue_disk_channel.go @@ -188,7 +188,7 @@ func (q *PersistableChannelUniqueQueue) Run(atShutdown, atTerminate func(func()) go q.internal.Run(func(_ func()) {}, func(_ func()) {}) go func() { _ = q.internal.Flush(0) - log.Debug("LevelUniqueQueue: %s flushed so shutting down", q.internal.(*LevelQueue).Name()) + log.Debug("LevelUniqueQueue: %s flushed so shutting down", q.internal.(*LevelUniqueQueue).Name()) q.internal.(*LevelUniqueQueue).Shutdown() GetManager().Remove(q.internal.(*LevelUniqueQueue).qid) }() diff --git a/modules/repository/cache.go b/modules/repository/cache.go index 5d6dea8fcb60b..e574f1adb7f7c 100644 --- a/modules/repository/cache.go +++ b/modules/repository/cache.go @@ -5,6 +5,7 @@ package repository import ( + "context" "strings" "code.gitea.io/gitea/models" @@ -23,7 +24,7 @@ func getRefName(fullRefName string) string { } // CacheRef cachhe last commit information of the branch or the tag -func CacheRef(repo *models.Repository, gitRepo *git.Repository, fullRefName string) error { +func CacheRef(ctx context.Context, repo *models.Repository, gitRepo *git.Repository, fullRefName string) error { if !setting.CacheService.LastCommit.Enabled { return nil } @@ -43,5 +44,5 @@ func CacheRef(repo *models.Repository, gitRepo *git.Repository, fullRefName stri commitCache := git.NewLastCommitCache(repo.FullName(), gitRepo, setting.LastCommitCacheTTLSeconds, cache.GetCache()) - return commitCache.CacheCommit(commit) + return commitCache.CacheCommit(ctx, commit) } diff --git a/modules/repository/commits_test.go b/modules/repository/commits_test.go index ba69a7f97ca7e..a5b28ce933ed0 100644 --- a/modules/repository/commits_test.go +++ b/modules/repository/commits_test.go @@ -51,7 +51,7 @@ func TestPushCommits_ToAPIPayloadCommits(t *testing.T) { repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) payloadCommits, err := pushCommits.ToAPIPayloadCommits(repo.RepoPath(), "/user2/repo16") assert.NoError(t, err) - assert.EqualValues(t, 3, len(payloadCommits)) + assert.Len(t, payloadCommits, 3) assert.Equal(t, "69554a6", payloadCommits[0].ID) assert.Equal(t, "not signed commit", payloadCommits[0].Message) diff --git a/modules/repository/create_test.go b/modules/repository/create_test.go index d3e8bf5af1ebd..65ed7806a9b25 100644 --- a/modules/repository/create_test.go +++ b/modules/repository/create_test.go @@ -21,7 +21,7 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) { team := models.AssertExistsAndLoadBean(t, &models.Team{ID: teamID}).(*models.Team) assert.NoError(t, team.GetRepositories(&models.SearchTeamOptions{}), "%s: GetRepositories", team.Name) assert.Len(t, team.Repos, team.NumRepos, "%s: len repo", team.Name) - assert.Equal(t, len(repoIds), len(team.Repos), "%s: repo count", team.Name) + assert.Len(t, team.Repos, len(repoIds), "%s: repo count", team.Name) for i, rid := range repoIds { if rid > 0 { assert.True(t, team.HasRepository(rid), "%s: HasRepository(%d) %d", rid, i) diff --git a/modules/repository/repo.go b/modules/repository/repo.go index 50eb185daa9ec..08531c04ed3e1 100644 --- a/modules/repository/repo.go +++ b/modules/repository/repo.go @@ -7,6 +7,7 @@ package repository import ( "context" "fmt" + "io" "net/url" "path" "strings" @@ -323,64 +324,90 @@ func StoreMissingLfsObjectsInRepository(ctx context.Context, repo *models.Reposi errChan := make(chan error, 1) go lfs.SearchPointerBlobs(ctx, gitRepo, pointerChan, errChan) - err := func() error { - for pointerBlob := range pointerChan { - meta, err := models.NewLFSMetaObject(&models.LFSMetaObject{Pointer: pointerBlob.Pointer, RepositoryID: repo.ID}) - if err != nil { - return fmt.Errorf("StoreMissingLfsObjectsInRepository models.NewLFSMetaObject: %w", err) - } - if meta.Existing { - continue + downloadObjects := func(pointers []lfs.Pointer) error { + err := client.Download(ctx, pointers, func(p lfs.Pointer, content io.ReadCloser, objectError error) error { + if objectError != nil { + return objectError } - log.Trace("StoreMissingLfsObjectsInRepository: LFS OID[%s] not present in repository %s", pointerBlob.Oid, repo.FullName()) + defer content.Close() - err = func() error { - exist, err := contentStore.Exists(pointerBlob.Pointer) - if err != nil { - return fmt.Errorf("StoreMissingLfsObjectsInRepository contentStore.Exists: %w", err) - } - if !exist { - if setting.LFS.MaxFileSize > 0 && pointerBlob.Size > setting.LFS.MaxFileSize { - log.Info("LFS OID[%s] download denied because of LFS_MAX_FILE_SIZE=%d < size %d", pointerBlob.Oid, setting.LFS.MaxFileSize, pointerBlob.Size) - return nil - } - - stream, err := client.Download(ctx, pointerBlob.Oid, pointerBlob.Size) - if err != nil { - return fmt.Errorf("StoreMissingLfsObjectsInRepository: LFS OID[%s] failed to download: %w", pointerBlob.Oid, err) - } - defer stream.Close() - - if err := contentStore.Put(pointerBlob.Pointer, stream); err != nil { - return fmt.Errorf("StoreMissingLfsObjectsInRepository LFS OID[%s] contentStore.Put: %w", pointerBlob.Oid, err) - } - } else { - log.Trace("StoreMissingLfsObjectsInRepository: LFS OID[%s] already present in content store", pointerBlob.Oid) - } - return nil - }() + _, err := models.NewLFSMetaObject(&models.LFSMetaObject{Pointer: p, RepositoryID: repo.ID}) if err != nil { - if _, err2 := repo.RemoveLFSMetaObjectByOid(meta.Oid); err2 != nil { - log.Error("StoreMissingLfsObjectsInRepository RemoveLFSMetaObjectByOid[Oid: %s]: %w", meta.Oid, err2) - } + log.Error("Error creating LFS meta object %v: %v", p, err) + return err + } - select { - case <-ctx.Done(): - return nil - default: + if err := contentStore.Put(p, content); err != nil { + log.Error("Error storing content for LFS meta object %v: %v", p, err) + if _, err2 := repo.RemoveLFSMetaObjectByOid(p.Oid); err2 != nil { + log.Error("Error removing LFS meta object %v: %v", p, err2) } return err } + return nil + }) + if err != nil { + select { + case <-ctx.Done(): + return nil + default: + } } - return nil - }() - if err != nil { return err } + var batch []lfs.Pointer + for pointerBlob := range pointerChan { + meta, err := repo.GetLFSMetaObjectByOid(pointerBlob.Oid) + if err != nil && err != models.ErrLFSObjectNotExist { + log.Error("Error querying LFS meta object %v: %v", pointerBlob.Pointer, err) + return err + } + if meta != nil { + log.Trace("Skipping unknown LFS meta object %v", pointerBlob.Pointer) + continue + } + + log.Trace("LFS object %v not present in repository %s", pointerBlob.Pointer, repo.FullName()) + + exist, err := contentStore.Exists(pointerBlob.Pointer) + if err != nil { + log.Error("Error checking if LFS object %v exists: %v", pointerBlob.Pointer, err) + return err + } + + if exist { + log.Trace("LFS object %v already present; creating meta object", pointerBlob.Pointer) + _, err := models.NewLFSMetaObject(&models.LFSMetaObject{Pointer: pointerBlob.Pointer, RepositoryID: repo.ID}) + if err != nil { + log.Error("Error creating LFS meta object %v: %v", pointerBlob.Pointer, err) + return err + } + } else { + if setting.LFS.MaxFileSize > 0 && pointerBlob.Size > setting.LFS.MaxFileSize { + log.Info("LFS object %v download denied because of LFS_MAX_FILE_SIZE=%d < size %d", pointerBlob.Pointer, setting.LFS.MaxFileSize, pointerBlob.Size) + continue + } + + batch = append(batch, pointerBlob.Pointer) + if len(batch) >= client.BatchSize() { + if err := downloadObjects(batch); err != nil { + return err + } + batch = nil + } + } + } + if len(batch) > 0 { + if err := downloadObjects(batch); err != nil { + return err + } + } + err, has := <-errChan if has { + log.Error("Error enumerating LFS objects for repository: %v", err) return err } diff --git a/modules/setting/indexer.go b/modules/setting/indexer.go index 8f1d801a78ee9..f170a8d3afce9 100644 --- a/modules/setting/indexer.go +++ b/modules/setting/indexer.go @@ -28,10 +28,10 @@ var ( IssuePath string IssueConnStr string IssueIndexerName string - IssueQueueType string - IssueQueueDir string - IssueQueueConnStr string - IssueQueueBatchNumber int + IssueQueueType string // DEPRECATED - replaced by queue.issue_indexer + IssueQueueDir string // DEPRECATED - replaced by queue.issue_indexer + IssueQueueConnStr string // DEPRECATED - replaced by queue.issue_indexer + IssueQueueBatchNumber int // DEPRECATED - replaced by queue.issue_indexer StartupTimeout time.Duration RepoIndexerEnabled bool @@ -39,20 +39,17 @@ var ( RepoPath string RepoConnStr string RepoIndexerName string - UpdateQueueLength int + UpdateQueueLength int // DEPRECATED - replaced by queue.issue_indexer MaxIndexerFileSize int64 IncludePatterns []glob.Glob ExcludePatterns []glob.Glob ExcludeVendored bool }{ - IssueType: "bleve", - IssuePath: "indexers/issues.bleve", - IssueConnStr: "", - IssueIndexerName: "gitea_issues", - IssueQueueType: LevelQueueType, - IssueQueueDir: "queues/common", - IssueQueueConnStr: "", - IssueQueueBatchNumber: 20, + IssueType: "bleve", + IssuePath: "indexers/issues.bleve", + IssueConnStr: "", + IssueIndexerName: "gitea_issues", + IssueQueueType: LevelQueueType, RepoIndexerEnabled: false, RepoType: "bleve", @@ -74,10 +71,12 @@ func newIndexerService() { Indexer.IssueConnStr = sec.Key("ISSUE_INDEXER_CONN_STR").MustString(Indexer.IssueConnStr) Indexer.IssueIndexerName = sec.Key("ISSUE_INDEXER_NAME").MustString(Indexer.IssueIndexerName) - Indexer.IssueQueueType = sec.Key("ISSUE_INDEXER_QUEUE_TYPE").MustString(LevelQueueType) - Indexer.IssueQueueDir = filepath.ToSlash(sec.Key("ISSUE_INDEXER_QUEUE_DIR").MustString(filepath.ToSlash(filepath.Join(AppDataPath, "queues/common")))) + // The following settings are deprecated and can be overridden by settings in [queue] or [queue.issue_indexer] + Indexer.IssueQueueType = sec.Key("ISSUE_INDEXER_QUEUE_TYPE").MustString("") + Indexer.IssueQueueDir = filepath.ToSlash(sec.Key("ISSUE_INDEXER_QUEUE_DIR").MustString("")) Indexer.IssueQueueConnStr = sec.Key("ISSUE_INDEXER_QUEUE_CONN_STR").MustString("") - Indexer.IssueQueueBatchNumber = sec.Key("ISSUE_INDEXER_QUEUE_BATCH_NUMBER").MustInt(20) + Indexer.IssueQueueBatchNumber = sec.Key("ISSUE_INDEXER_QUEUE_BATCH_NUMBER").MustInt(0) + Indexer.UpdateQueueLength = sec.Key("UPDATE_BUFFER_LEN").MustInt(0) Indexer.RepoIndexerEnabled = sec.Key("REPO_INDEXER_ENABLED").MustBool(false) Indexer.RepoType = sec.Key("REPO_INDEXER_TYPE").MustString("bleve") @@ -91,7 +90,6 @@ func newIndexerService() { Indexer.IncludePatterns = IndexerGlobFromString(sec.Key("REPO_INDEXER_INCLUDE").MustString("")) Indexer.ExcludePatterns = IndexerGlobFromString(sec.Key("REPO_INDEXER_EXCLUDE").MustString("")) Indexer.ExcludeVendored = sec.Key("REPO_INDEXER_EXCLUDE_VENDORED").MustBool(true) - Indexer.UpdateQueueLength = sec.Key("UPDATE_BUFFER_LEN").MustInt(20) Indexer.MaxIndexerFileSize = sec.Key("MAX_FILE_SIZE").MustInt64(1024 * 1024) Indexer.StartupTimeout = sec.Key("STARTUP_TIMEOUT").MustDuration(30 * time.Second) } diff --git a/modules/setting/lfs.go b/modules/setting/lfs.go index 38c656fc29858..8b9224b86a6cb 100644 --- a/modules/setting/lfs.go +++ b/modules/setting/lfs.go @@ -54,7 +54,7 @@ func newLFSService() { n, err := base64.RawURLEncoding.Decode(LFS.JWTSecretBytes, []byte(LFS.JWTSecretBase64)) if err != nil || n != 32 { - LFS.JWTSecretBase64, err = generate.NewJwtSecret() + LFS.JWTSecretBase64, err = generate.NewJwtSecretBase64() if err != nil { log.Fatal("Error generating JWT Secret for custom config: %v", err) return diff --git a/modules/setting/markup.go b/modules/setting/markup.go index f0849a863a56f..43df4ce442cd0 100644 --- a/modules/setting/markup.go +++ b/modules/setting/markup.go @@ -38,6 +38,8 @@ type MarkupSanitizerRule struct { } func newMarkup() { + ExternalMarkupRenderers = make([]MarkupRenderer, 0, 10) + ExternalSanitizerRules = make([]MarkupSanitizerRule, 0, 10) for _, sec := range Cfg.Section("markup").ChildSections() { name := strings.TrimPrefix(sec.Name(), "markup.") if name == "" { diff --git a/modules/setting/queue.go b/modules/setting/queue.go index 41e95fbe5a5e7..4ff02b61ebcbd 100644 --- a/modules/setting/queue.go +++ b/modules/setting/queue.go @@ -65,7 +65,7 @@ func GetQueueSettings(name string) QueueSettings { q.SetName = q.QueueName + Queue.SetName } if !filepath.IsAbs(q.DataDir) { - q.DataDir = filepath.Join(AppDataPath, q.DataDir) + q.DataDir = filepath.ToSlash(filepath.Join(AppDataPath, q.DataDir)) } _, _ = sec.NewKey("DATADIR", q.DataDir) @@ -98,6 +98,7 @@ func NewQueueService() { Queue.QueueLength = sec.Key("LENGTH").MustInt(20) Queue.BatchLength = sec.Key("BATCH_LENGTH").MustInt(20) Queue.ConnectionString = sec.Key("CONN_STR").MustString("") + defaultType := sec.Key("TYPE").String() Queue.Type = sec.Key("TYPE").MustString("persistable-channel") Queue.Network, Queue.Addresses, Queue.Password, Queue.DBIndex, _ = ParseQueueConnStr(Queue.ConnectionString) Queue.WrapIfNecessary = sec.Key("WRAP_IF_NECESSARY").MustBool(true) @@ -117,7 +118,7 @@ func NewQueueService() { for _, key := range section.Keys() { sectionMap[key.Name()] = true } - if _, ok := sectionMap["TYPE"]; !ok { + if _, ok := sectionMap["TYPE"]; !ok && defaultType == "" { switch Indexer.IssueQueueType { case LevelQueueType: _, _ = section.NewKey("TYPE", "level") @@ -125,21 +126,23 @@ func NewQueueService() { _, _ = section.NewKey("TYPE", "persistable-channel") case RedisQueueType: _, _ = section.NewKey("TYPE", "redis") + case "": + _, _ = section.NewKey("TYPE", "level") default: log.Fatal("Unsupported indexer queue type: %v", Indexer.IssueQueueType) } } - if _, ok := sectionMap["LENGTH"]; !ok { + if _, ok := sectionMap["LENGTH"]; !ok && Indexer.UpdateQueueLength != 0 { _, _ = section.NewKey("LENGTH", fmt.Sprintf("%d", Indexer.UpdateQueueLength)) } - if _, ok := sectionMap["BATCH_LENGTH"]; !ok { + if _, ok := sectionMap["BATCH_LENGTH"]; !ok && Indexer.IssueQueueBatchNumber != 0 { _, _ = section.NewKey("BATCH_LENGTH", fmt.Sprintf("%d", Indexer.IssueQueueBatchNumber)) } - if _, ok := sectionMap["DATADIR"]; !ok { + if _, ok := sectionMap["DATADIR"]; !ok && Indexer.IssueQueueDir != "" { _, _ = section.NewKey("DATADIR", Indexer.IssueQueueDir) } - if _, ok := sectionMap["CONN_STR"]; !ok { + if _, ok := sectionMap["CONN_STR"]; !ok && Indexer.IssueQueueConnStr != "" { _, _ = section.NewKey("CONN_STR", Indexer.IssueQueueConnStr) } diff --git a/modules/setting/repository.go b/modules/setting/repository.go index a6fc73651a305..a7666895e1f36 100644 --- a/modules/setting/repository.go +++ b/modules/setting/repository.go @@ -78,6 +78,7 @@ var ( DefaultMergeMessageAllAuthors bool DefaultMergeMessageMaxApprovers int DefaultMergeMessageOfficialApproversOnly bool + PopulateSquashCommentWithCommitMessages bool } `ini:"repository.pull-request"` // Issue Setting @@ -199,6 +200,7 @@ var ( DefaultMergeMessageAllAuthors bool DefaultMergeMessageMaxApprovers int DefaultMergeMessageOfficialApproversOnly bool + PopulateSquashCommentWithCommitMessages bool }{ WorkInProgressPrefixes: []string{"WIP:", "[WIP]"}, // Same as GitHub. See @@ -210,6 +212,7 @@ var ( DefaultMergeMessageAllAuthors: false, DefaultMergeMessageMaxApprovers: 10, DefaultMergeMessageOfficialApproversOnly: true, + PopulateSquashCommentWithCommitMessages: false, }, // Issue settings diff --git a/modules/setting/setting.go b/modules/setting/setting.go index c16520572d357..f648179155594 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -20,6 +20,7 @@ import ( "runtime" "strconv" "strings" + "text/template" "time" "code.gitea.io/gitea/modules/generate" @@ -117,48 +118,57 @@ var ( GracefulRestartable bool GracefulHammerTime time.Duration StartupTimeout time.Duration + PerWriteTimeout = 30 * time.Second + PerWritePerKbTimeout = 10 * time.Second StaticURLPrefix string AbsoluteAssetURL string SSH = struct { - Disabled bool `ini:"DISABLE_SSH"` - StartBuiltinServer bool `ini:"START_SSH_SERVER"` - BuiltinServerUser string `ini:"BUILTIN_SSH_SERVER_USER"` - Domain string `ini:"SSH_DOMAIN"` - Port int `ini:"SSH_PORT"` - ListenHost string `ini:"SSH_LISTEN_HOST"` - ListenPort int `ini:"SSH_LISTEN_PORT"` - RootPath string `ini:"SSH_ROOT_PATH"` - ServerCiphers []string `ini:"SSH_SERVER_CIPHERS"` - ServerKeyExchanges []string `ini:"SSH_SERVER_KEY_EXCHANGES"` - ServerMACs []string `ini:"SSH_SERVER_MACS"` - ServerHostKeys []string `ini:"SSH_SERVER_HOST_KEYS"` - KeyTestPath string `ini:"SSH_KEY_TEST_PATH"` - KeygenPath string `ini:"SSH_KEYGEN_PATH"` - AuthorizedKeysBackup bool `ini:"SSH_AUTHORIZED_KEYS_BACKUP"` - AuthorizedPrincipalsBackup bool `ini:"SSH_AUTHORIZED_PRINCIPALS_BACKUP"` - MinimumKeySizeCheck bool `ini:"-"` - MinimumKeySizes map[string]int `ini:"-"` - CreateAuthorizedKeysFile bool `ini:"SSH_CREATE_AUTHORIZED_KEYS_FILE"` - CreateAuthorizedPrincipalsFile bool `ini:"SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE"` - ExposeAnonymous bool `ini:"SSH_EXPOSE_ANONYMOUS"` - AuthorizedPrincipalsAllow []string `ini:"SSH_AUTHORIZED_PRINCIPALS_ALLOW"` - AuthorizedPrincipalsEnabled bool `ini:"-"` - TrustedUserCAKeys []string `ini:"SSH_TRUSTED_USER_CA_KEYS"` - TrustedUserCAKeysFile string `ini:"SSH_TRUSTED_USER_CA_KEYS_FILENAME"` - TrustedUserCAKeysParsed []gossh.PublicKey `ini:"-"` + Disabled bool `ini:"DISABLE_SSH"` + StartBuiltinServer bool `ini:"START_SSH_SERVER"` + BuiltinServerUser string `ini:"BUILTIN_SSH_SERVER_USER"` + Domain string `ini:"SSH_DOMAIN"` + Port int `ini:"SSH_PORT"` + ListenHost string `ini:"SSH_LISTEN_HOST"` + ListenPort int `ini:"SSH_LISTEN_PORT"` + RootPath string `ini:"SSH_ROOT_PATH"` + ServerCiphers []string `ini:"SSH_SERVER_CIPHERS"` + ServerKeyExchanges []string `ini:"SSH_SERVER_KEY_EXCHANGES"` + ServerMACs []string `ini:"SSH_SERVER_MACS"` + ServerHostKeys []string `ini:"SSH_SERVER_HOST_KEYS"` + KeyTestPath string `ini:"SSH_KEY_TEST_PATH"` + KeygenPath string `ini:"SSH_KEYGEN_PATH"` + AuthorizedKeysBackup bool `ini:"SSH_AUTHORIZED_KEYS_BACKUP"` + AuthorizedPrincipalsBackup bool `ini:"SSH_AUTHORIZED_PRINCIPALS_BACKUP"` + AuthorizedKeysCommandTemplate string `ini:"SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE"` + AuthorizedKeysCommandTemplateTemplate *template.Template `ini:"-"` + MinimumKeySizeCheck bool `ini:"-"` + MinimumKeySizes map[string]int `ini:"-"` + CreateAuthorizedKeysFile bool `ini:"SSH_CREATE_AUTHORIZED_KEYS_FILE"` + CreateAuthorizedPrincipalsFile bool `ini:"SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE"` + ExposeAnonymous bool `ini:"SSH_EXPOSE_ANONYMOUS"` + AuthorizedPrincipalsAllow []string `ini:"SSH_AUTHORIZED_PRINCIPALS_ALLOW"` + AuthorizedPrincipalsEnabled bool `ini:"-"` + TrustedUserCAKeys []string `ini:"SSH_TRUSTED_USER_CA_KEYS"` + TrustedUserCAKeysFile string `ini:"SSH_TRUSTED_USER_CA_KEYS_FILENAME"` + TrustedUserCAKeysParsed []gossh.PublicKey `ini:"-"` + PerWriteTimeout time.Duration `ini:"SSH_PER_WRITE_TIMEOUT"` + PerWritePerKbTimeout time.Duration `ini:"SSH_PER_WRITE_PER_KB_TIMEOUT"` }{ - Disabled: false, - StartBuiltinServer: false, - Domain: "", - Port: 22, - ServerCiphers: []string{"aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com", "arcfour256", "arcfour128"}, - ServerKeyExchanges: []string{"diffie-hellman-group1-sha1", "diffie-hellman-group14-sha1", "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521", "curve25519-sha256@libssh.org"}, - ServerMACs: []string{"hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1", "hmac-sha1-96"}, - KeygenPath: "ssh-keygen", - MinimumKeySizeCheck: true, - MinimumKeySizes: map[string]int{"ed25519": 256, "ed25519-sk": 256, "ecdsa": 256, "ecdsa-sk": 256, "rsa": 2048}, - ServerHostKeys: []string{"ssh/gitea.rsa", "ssh/gogs.rsa"}, + Disabled: false, + StartBuiltinServer: false, + Domain: "", + Port: 22, + ServerCiphers: []string{"aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com", "arcfour256", "arcfour128"}, + ServerKeyExchanges: []string{"diffie-hellman-group1-sha1", "diffie-hellman-group14-sha1", "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521", "curve25519-sha256@libssh.org"}, + ServerMACs: []string{"hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1", "hmac-sha1-96"}, + KeygenPath: "ssh-keygen", + MinimumKeySizeCheck: true, + MinimumKeySizes: map[string]int{"ed25519": 256, "ed25519-sk": 256, "ecdsa": 256, "ecdsa-sk": 256, "rsa": 2048}, + ServerHostKeys: []string{"ssh/gitea.rsa", "ssh/gogs.rsa"}, + AuthorizedKeysCommandTemplate: "{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}", + PerWriteTimeout: PerWriteTimeout, + PerWritePerKbTimeout: PerWritePerKbTimeout, } // Security settings @@ -361,14 +371,17 @@ var ( AccessTokenExpirationTime int64 RefreshTokenExpirationTime int64 InvalidateRefreshTokens bool - JWTSecretBytes []byte `ini:"-"` + JWTSigningAlgorithm string `ini:"JWT_SIGNING_ALGORITHM"` JWTSecretBase64 string `ini:"JWT_SECRET"` + JWTSigningPrivateKeyFile string `ini:"JWT_SIGNING_PRIVATE_KEY_FILE"` MaxTokenLength int }{ Enable: true, AccessTokenExpirationTime: 3600, RefreshTokenExpirationTime: 730, InvalidateRefreshTokens: false, + JWTSigningAlgorithm: "RS256", + JWTSigningPrivateKeyFile: "jwt/private.pem", MaxTokenLength: math.MaxInt16, } @@ -612,6 +625,8 @@ func NewContext() { GracefulRestartable = sec.Key("ALLOW_GRACEFUL_RESTARTS").MustBool(true) GracefulHammerTime = sec.Key("GRACEFUL_HAMMER_TIME").MustDuration(60 * time.Second) StartupTimeout = sec.Key("STARTUP_TIMEOUT").MustDuration(0 * time.Second) + PerWriteTimeout = sec.Key("PER_WRITE_TIMEOUT").MustDuration(PerWriteTimeout) + PerWritePerKbTimeout = sec.Key("PER_WRITE_PER_KB_TIMEOUT").MustDuration(PerWritePerKbTimeout) defaultAppURL := string(Protocol) + "://" + Domain if (Protocol == HTTP && HTTPPort != "80") || (Protocol == HTTPS && HTTPPort != "443") { @@ -777,27 +792,20 @@ func NewContext() { } SSH.ExposeAnonymous = sec.Key("SSH_EXPOSE_ANONYMOUS").MustBool(false) + SSH.AuthorizedKeysCommandTemplate = sec.Key("SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE").MustString(SSH.AuthorizedKeysCommandTemplate) + + SSH.AuthorizedKeysCommandTemplateTemplate = template.Must(template.New("").Parse(SSH.AuthorizedKeysCommandTemplate)) + + SSH.PerWriteTimeout = sec.Key("SSH_PER_WRITE_TIMEOUT").MustDuration(PerWriteTimeout) + SSH.PerWritePerKbTimeout = sec.Key("SSH_PER_WRITE_PER_KB_TIMEOUT").MustDuration(PerWritePerKbTimeout) if err = Cfg.Section("oauth2").MapTo(&OAuth2); err != nil { log.Fatal("Failed to OAuth2 settings: %v", err) return } - if OAuth2.Enable { - OAuth2.JWTSecretBytes = make([]byte, 32) - n, err := base64.RawURLEncoding.Decode(OAuth2.JWTSecretBytes, []byte(OAuth2.JWTSecretBase64)) - - if err != nil || n != 32 { - OAuth2.JWTSecretBase64, err = generate.NewJwtSecret() - if err != nil { - log.Fatal("error generating JWT secret: %v", err) - return - } - - CreateOrAppendToCustomConf(func(cfg *ini.File) { - cfg.Section("oauth2").Key("JWT_SECRET").SetValue(OAuth2.JWTSecretBase64) - }) - } + if !filepath.IsAbs(OAuth2.JWTSigningPrivateKeyFile) { + OAuth2.JWTSigningPrivateKeyFile = filepath.Join(CustomPath, OAuth2.JWTSigningPrivateKeyFile) } sec = Cfg.Section("admin") @@ -1172,3 +1180,9 @@ func NewServices() { newProject() newMimeTypeMap() } + +// NewServicesForInstall initializes the services for install +func NewServicesForInstall() { + newService() + newMailService() +} diff --git a/modules/ssh/ssh_graceful.go b/modules/ssh/ssh_graceful.go index c213aa7b88a99..08a7c857529a9 100644 --- a/modules/ssh/ssh_graceful.go +++ b/modules/ssh/ssh_graceful.go @@ -7,12 +7,15 @@ package ssh import ( "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" "github.com/gliderlabs/ssh" ) func listen(server *ssh.Server) { gracefulServer := graceful.NewServer("tcp", server.Addr, "SSH") + gracefulServer.PerWriteTimeout = setting.SSH.PerWriteTimeout + gracefulServer.PerWritePerKbTimeout = setting.SSH.PerWritePerKbTimeout err := gracefulServer.ListenAndServe(server.Serve) if err != nil { diff --git a/modules/structs/org.go b/modules/structs/org.go index 483f5044a8982..38c6c6d6d849b 100644 --- a/modules/structs/org.go +++ b/modules/structs/org.go @@ -31,6 +31,8 @@ type CreateOrgOption struct { RepoAdminChangeTeamAccess bool `json:"repo_admin_change_team_access"` } +// TODO: make EditOrgOption fields optional after https://gitea.com/go-chi/binding/pulls/5 got merged + // EditOrgOption options for editing an organization type EditOrgOption struct { FullName string `json:"full_name"` @@ -40,5 +42,5 @@ type EditOrgOption struct { // possible values are `public`, `limited` or `private` // enum: public,limited,private Visibility string `json:"visibility" binding:"In(,public,limited,private)"` - RepoAdminChangeTeamAccess bool `json:"repo_admin_change_team_access"` + RepoAdminChangeTeamAccess *bool `json:"repo_admin_change_team_access"` } diff --git a/modules/structs/repo_tag.go b/modules/structs/repo_tag.go index b62395cac4983..80ee1ccf17743 100644 --- a/modules/structs/repo_tag.go +++ b/modules/structs/repo_tag.go @@ -7,6 +7,7 @@ package structs // Tag represents a repository tag type Tag struct { Name string `json:"name"` + Message string `json:"message"` ID string `json:"id"` Commit *CommitMeta `json:"commit"` ZipballURL string `json:"zipball_url"` @@ -30,3 +31,11 @@ type AnnotatedTagObject struct { URL string `json:"url"` SHA string `json:"sha"` } + +// CreateTagOption options when creating a tag +type CreateTagOption struct { + // required: true + TagName string `json:"tag_name" binding:"Required"` + Message string `json:"message"` + Target string `json:"target"` +} diff --git a/modules/structs/user.go b/modules/structs/user.go index 2dbc5305382ab..de2e68c2a2e30 100644 --- a/modules/structs/user.go +++ b/modules/structs/user.go @@ -43,6 +43,11 @@ type User struct { Website string `json:"website"` // the user's description Description string `json:"description"` + + // user counts + Followers int `json:"followers_count"` + Following int `json:"following_count"` + StarredRepos int `json:"starred_repos_count"` } // MarshalJSON implements the json.Marshaler interface for User, adding field(s) for backward compatibility diff --git a/modules/task/migrate.go b/modules/task/migrate.go index 57424abac38ca..1d190faf872af 100644 --- a/modules/task/migrate.go +++ b/modules/task/migrate.go @@ -20,6 +20,7 @@ import ( "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" + jsoniter "github.com/json-iterator/go" ) func handleCreateError(owner *models.User, err error) error { @@ -56,7 +57,7 @@ func runMigrateTask(t *models.Task) (err error) { t.EndTime = timeutil.TimeStampNow() t.Status = structs.TaskStatusFailed - t.Errors = err.Error() + t.Message = err.Error() t.RepoID = 0 if err := t.UpdateCols("status", "errors", "repo_id", "end_time"); err != nil { log.Error("Task UpdateCols failed: %v", err) @@ -106,7 +107,16 @@ func runMigrateTask(t *models.Task) (err error) { return } - repo, err = migrations.MigrateRepository(ctx, t.Doer, t.Owner.Name, *opts) + repo, err = migrations.MigrateRepository(ctx, t.Doer, t.Owner.Name, *opts, func(format string, args ...interface{}) { + message := models.TranslatableMessage{ + Format: format, + Args: args, + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + bs, _ := json.Marshal(message) + t.Message = string(bs) + _ = t.UpdateCols("message") + }) if err == nil { log.Trace("Repository migrated [%d]: %s/%s", repo.ID, t.Owner.Name, repo.Name) return @@ -118,7 +128,7 @@ func runMigrateTask(t *models.Task) (err error) { } // remoteAddr may contain credentials, so we sanitize it - err = util.URLSanitizedError(err, opts.CloneAddr) + err = util.NewStringURLSanitizedError(err, opts.CloneAddr, true) if strings.Contains(err.Error(), "Authentication failed") || strings.Contains(err.Error(), "could not read Username") { return fmt.Errorf("Authentication failed: %v", err.Error()) diff --git a/modules/task/task.go b/modules/task/task.go index 0685aa23d743a..1c0a87e1f61a4 100644 --- a/modules/task/task.go +++ b/modules/task/task.go @@ -74,7 +74,7 @@ func CreateMigrateTask(doer, u *models.User, opts base.MigrateOptions) (*models. if err != nil { return nil, err } - opts.CloneAddr = util.SanitizeURLCredentials(opts.CloneAddr, true) + opts.CloneAddr = util.NewStringURLSanitizer(opts.CloneAddr, true).Replace(opts.CloneAddr) opts.AuthPasswordEncrypted, err = secret.EncryptSecret(setting.SecretKey, opts.AuthPassword) if err != nil { return nil, err diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 9922cfb225f95..83359a6ef234b 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -27,6 +27,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/emoji" + "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/repository" @@ -35,7 +36,6 @@ import ( "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/gitdiff" - mirror_service "code.gitea.io/gitea/services/mirror" "github.com/editorconfig/editorconfig-core-go/v2" jsoniter "github.com/json-iterator/go" @@ -294,11 +294,8 @@ func NewFuncMap() []template.FuncMap { } return float32(n) * 100 / float32(sum) }, - "CommentMustAsDiff": gitdiff.CommentMustAsDiff, - "MirrorAddress": mirror_service.Address, - "MirrorFullAddress": mirror_service.AddressNoCredentials, - "MirrorUserName": mirror_service.Username, - "MirrorPassword": mirror_service.Password, + "CommentMustAsDiff": gitdiff.CommentMustAsDiff, + "MirrorRemoteAddress": mirrorRemoteAddress, "CommitType": func(commit interface{}) string { switch commit.(type) { case models.SignCommitWithStatuses: @@ -963,3 +960,28 @@ func buildSubjectBodyTemplate(stpl *texttmpl.Template, btpl *template.Template, log.Warn("Failed to parse template [%s/body]: %v", name, err) } } + +type remoteAddress struct { + Address string + Username string + Password string +} + +func mirrorRemoteAddress(m models.RemoteMirrorer) remoteAddress { + a := remoteAddress{} + + u, err := git.GetRemoteAddress(m.GetRepository().RepoPath(), m.GetRemoteName()) + if err != nil { + log.Error("GetRemoteAddress %v", err) + return a + } + + if u.User != nil { + a.Username = u.User.Username() + a.Password, _ = u.User.Password() + } + u.User = nil + a.Address = u.String() + + return a +} diff --git a/modules/typesniffer/typesniffer.go b/modules/typesniffer/typesniffer.go new file mode 100644 index 0000000000000..7c89f66699c12 --- /dev/null +++ b/modules/typesniffer/typesniffer.go @@ -0,0 +1,96 @@ +// 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 typesniffer + +import ( + "fmt" + "io" + "net/http" + "regexp" + "strings" +) + +// Use at most this many bytes to determine Content Type. +const sniffLen = 1024 + +// SvgMimeType MIME type of SVG images. +const SvgMimeType = "image/svg+xml" + +var svgTagRegex = regexp.MustCompile(`(?si)\A\s*(?:(||>))\s*)*\/]`) +var svgTagInXMLRegex = regexp.MustCompile(`(?si)\A<\?xml\b.*?\?>\s*(?:(||>))\s*)*\/]`) + +// SniffedType contains informations about a blobs type. +type SniffedType struct { + contentType string +} + +// IsText etects if content format is plain text. +func (ct SniffedType) IsText() bool { + return strings.Contains(ct.contentType, "text/") +} + +// IsImage detects if data is an image format +func (ct SniffedType) IsImage() bool { + return strings.Contains(ct.contentType, "image/") +} + +// IsSvgImage detects if data is an SVG image format +func (ct SniffedType) IsSvgImage() bool { + return strings.Contains(ct.contentType, SvgMimeType) +} + +// IsPDF detects if data is a PDF format +func (ct SniffedType) IsPDF() bool { + return strings.Contains(ct.contentType, "application/pdf") +} + +// IsVideo detects if data is an video format +func (ct SniffedType) IsVideo() bool { + return strings.Contains(ct.contentType, "video/") +} + +// IsAudio detects if data is an video format +func (ct SniffedType) IsAudio() bool { + return strings.Contains(ct.contentType, "audio/") +} + +// IsRepresentableAsText returns true if file content can be represented as +// plain text or is empty. +func (ct SniffedType) IsRepresentableAsText() bool { + return ct.IsText() || ct.IsSvgImage() +} + +// DetectContentType extends http.DetectContentType with more content types. Defaults to text/unknown if input is empty. +func DetectContentType(data []byte) SniffedType { + if len(data) == 0 { + return SniffedType{"text/unknown"} + } + + ct := http.DetectContentType(data) + + if len(data) > sniffLen { + data = data[:sniffLen] + } + + if (strings.Contains(ct, "text/plain") || strings.Contains(ct, "text/html")) && svgTagRegex.Match(data) || + strings.Contains(ct, "text/xml") && svgTagInXMLRegex.Match(data) { + // SVG is unsupported. https://github.com/golang/go/issues/15888 + ct = SvgMimeType + } + + return SniffedType{ct} +} + +// DetectContentTypeFromReader guesses the content type contained in the reader. +func DetectContentTypeFromReader(r io.Reader) (SniffedType, error) { + buf := make([]byte, sniffLen) + n, err := r.Read(buf) + if err != nil && err != io.EOF { + return SniffedType{}, fmt.Errorf("DetectContentTypeFromReader io error: %w", err) + } + buf = buf[:n] + + return DetectContentType(buf), nil +} diff --git a/modules/typesniffer/typesniffer_test.go b/modules/typesniffer/typesniffer_test.go new file mode 100644 index 0000000000000..a3b47c459863e --- /dev/null +++ b/modules/typesniffer/typesniffer_test.go @@ -0,0 +1,97 @@ +// 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 typesniffer + +import ( + "bytes" + "encoding/base64" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDetectContentTypeLongerThanSniffLen(t *testing.T) { + // Pre-condition: Shorter than sniffLen detects SVG. + assert.Equal(t, "image/svg+xml", DetectContentType([]byte(``)).contentType) + // Longer than sniffLen detects something else. + assert.NotEqual(t, "image/svg+xml", DetectContentType([]byte(``)).contentType) +} + +func TestIsTextFile(t *testing.T) { + assert.True(t, DetectContentType([]byte{}).IsText()) + assert.True(t, DetectContentType([]byte("lorem ipsum")).IsText()) +} + +func TestIsSvgImage(t *testing.T) { + assert.True(t, DetectContentType([]byte("")).IsSvgImage()) + assert.True(t, DetectContentType([]byte(" ")).IsSvgImage()) + assert.True(t, DetectContentType([]byte(``)).IsSvgImage()) + assert.True(t, DetectContentType([]byte("")).IsSvgImage()) + assert.True(t, DetectContentType([]byte(``)).IsSvgImage()) + assert.True(t, DetectContentType([]byte(` + `)).IsSvgImage()) + assert.True(t, DetectContentType([]byte(` + + `)).IsSvgImage()) + assert.True(t, DetectContentType([]byte(` + `)).IsSvgImage()) + assert.True(t, DetectContentType([]byte(` + `)).IsSvgImage()) + assert.True(t, DetectContentType([]byte(` + + `)).IsSvgImage()) + assert.True(t, DetectContentType([]byte(` + + + `)).IsSvgImage()) + assert.True(t, DetectContentType([]byte(` + + `)).IsSvgImage()) + assert.True(t, DetectContentType([]byte(` + + + `)).IsSvgImage()) + assert.False(t, DetectContentType([]byte{}).IsSvgImage()) + assert.False(t, DetectContentType([]byte("svg")).IsSvgImage()) + assert.False(t, DetectContentType([]byte("")).IsSvgImage()) + assert.False(t, DetectContentType([]byte("text")).IsSvgImage()) + assert.False(t, DetectContentType([]byte("")).IsSvgImage()) + assert.False(t, DetectContentType([]byte(``)).IsSvgImage()) + assert.False(t, DetectContentType([]byte(` + `)).IsSvgImage()) + assert.False(t, DetectContentType([]byte(` + + `)).IsSvgImage()) +} + +func TestIsPDF(t *testing.T) { + pdf, _ := base64.StdEncoding.DecodeString("JVBERi0xLjYKJcOkw7zDtsOfCjIgMCBvYmoKPDwvTGVuZ3RoIDMgMCBSL0ZpbHRlci9GbGF0ZURlY29kZT4+CnN0cmVhbQp4nF3NPwsCMQwF8D2f4s2CNYk1baF0EHRwOwg4iJt/NsFb/PpevUE4Mjwe") + assert.True(t, DetectContentType(pdf).IsPDF()) + assert.False(t, DetectContentType([]byte("plain text")).IsPDF()) +} + +func TestIsVideo(t *testing.T) { + mp4, _ := base64.StdEncoding.DecodeString("AAAAGGZ0eXBtcDQyAAAAAGlzb21tcDQyAAEI721vb3YAAABsbXZoZAAAAADaBlwX2gZcFwAAA+gA") + assert.True(t, DetectContentType(mp4).IsVideo()) + assert.False(t, DetectContentType([]byte("plain text")).IsVideo()) +} + +func TestIsAudio(t *testing.T) { + mp3, _ := base64.StdEncoding.DecodeString("SUQzBAAAAAABAFRYWFgAAAASAAADbWFqb3JfYnJhbmQAbXA0MgBUWFhYAAAAEQAAA21pbm9yX3Zl") + assert.True(t, DetectContentType(mp3).IsAudio()) + assert.False(t, DetectContentType([]byte("plain text")).IsAudio()) +} + +func TestDetectContentTypeFromReader(t *testing.T) { + mp3, _ := base64.StdEncoding.DecodeString("SUQzBAAAAAABAFRYWFgAAAASAAADbWFqb3JfYnJhbmQAbXA0MgBUWFhYAAAAEQAAA21pbm9yX3Zl") + st, err := DetectContentTypeFromReader(bytes.NewReader(mp3)) + assert.NoError(t, err) + assert.True(t, st.IsAudio()) +} diff --git a/modules/util/sanitize.go b/modules/util/sanitize.go index a4f5479dfb749..de59ffaa2e5d7 100644 --- a/modules/util/sanitize.go +++ b/modules/util/sanitize.go @@ -1,4 +1,4 @@ -// Copyright 2017 The Gitea Authors. All rights reserved. +// 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. @@ -9,40 +9,53 @@ import ( "strings" ) -// urlSafeError wraps an error whose message may contain a sensitive URL -type urlSafeError struct { - err error - unsanitizedURL string +const userPlaceholder = "sanitized-credential" +const unparsableURL = "(unparsable url)" + +type sanitizedError struct { + err error + replacer *strings.Replacer } -func (err urlSafeError) Error() string { - return SanitizeMessage(err.err.Error(), err.unsanitizedURL) +func (err sanitizedError) Error() string { + return err.replacer.Replace(err.err.Error()) } -// URLSanitizedError returns the sanitized version an error whose message may -// contain a sensitive URL -func URLSanitizedError(err error, unsanitizedURL string) error { - return urlSafeError{err: err, unsanitizedURL: unsanitizedURL} +// NewSanitizedError wraps an error and replaces all old, new string pairs in the message text. +func NewSanitizedError(err error, oldnew ...string) error { + return sanitizedError{err: err, replacer: strings.NewReplacer(oldnew...)} } -// SanitizeMessage sanitizes a message which may contains a sensitive URL -func SanitizeMessage(message, unsanitizedURL string) string { - sanitizedURL := SanitizeURLCredentials(unsanitizedURL, true) - return strings.ReplaceAll(message, unsanitizedURL, sanitizedURL) +// NewURLSanitizedError wraps an error and replaces the url credential or removes them. +func NewURLSanitizedError(err error, u *url.URL, usePlaceholder bool) error { + return sanitizedError{err: err, replacer: NewURLSanitizer(u, usePlaceholder)} } -// SanitizeURLCredentials sanitizes a url, either removing user credentials -// or replacing them with a placeholder. -func SanitizeURLCredentials(unsanitizedURL string, usePlaceholder bool) string { - u, err := url.Parse(unsanitizedURL) - if err != nil { - // don't log the error, since it might contain unsanitized URL. - return "(unparsable url)" - } +// NewStringURLSanitizedError wraps an error and replaces the url credential or removes them. +// If the url can't get parsed it gets replaced with a placeholder string. +func NewStringURLSanitizedError(err error, unsanitizedURL string, usePlaceholder bool) error { + return sanitizedError{err: err, replacer: NewStringURLSanitizer(unsanitizedURL, usePlaceholder)} +} + +// NewURLSanitizer creates a replacer for the url with the credential sanitized or removed. +func NewURLSanitizer(u *url.URL, usePlaceholder bool) *strings.Replacer { + old := u.String() + if u.User != nil && usePlaceholder { - u.User = url.User("") + u.User = url.User(userPlaceholder) } else { u.User = nil } - return u.String() + return strings.NewReplacer(old, u.String()) +} + +// NewStringURLSanitizer creates a replacer for the url with the credential sanitized or removed. +// If the url can't get parsed it gets replaced with a placeholder string +func NewStringURLSanitizer(unsanitizedURL string, usePlaceholder bool) *strings.Replacer { + u, err := url.Parse(unsanitizedURL) + if err != nil { + // don't log the error, since it might contain unsanitized URL. + return strings.NewReplacer(unsanitizedURL, unparsableURL) + } + return NewURLSanitizer(u, usePlaceholder) } diff --git a/modules/util/sanitize_test.go b/modules/util/sanitize_test.go index 4f07100675b02..578f75f5188f6 100644 --- a/modules/util/sanitize_test.go +++ b/modules/util/sanitize_test.go @@ -1,25 +1,164 @@ -// Copyright 2020 The Gitea Authors. All rights reserved. +// 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 util import ( + "errors" "testing" "github.com/stretchr/testify/assert" ) -func TestSanitizeURLCredentials(t *testing.T) { - var kases = map[string]string{ - "https://github.com/go-gitea/test_repo.git": "https://github.com/go-gitea/test_repo.git", - "https://mytoken@github.com/go-gitea/test_repo.git": "https://github.com/go-gitea/test_repo.git", - "http://github.com/go-gitea/test_repo.git": "http://github.com/go-gitea/test_repo.git", - "/test/repos/repo1": "/test/repos/repo1", - "git@github.com:go-gitea/test_repo.git": "(unparsable url)", +func TestNewSanitizedError(t *testing.T) { + err := errors.New("error while secret on test") + err2 := NewSanitizedError(err) + assert.Equal(t, err.Error(), err2.Error()) + + var cases = []struct { + input error + oldnew []string + expected string + }{ + // case 0 + { + errors.New("error while secret on test"), + []string{"secret", "replaced"}, + "error while replaced on test", + }, + // case 1 + { + errors.New("error while sec-ret on test"), + []string{"secret", "replaced"}, + "error while sec-ret on test", + }, } - for source, value := range kases { - assert.EqualValues(t, value, SanitizeURLCredentials(source, false)) + for n, c := range cases { + err := NewSanitizedError(c.input, c.oldnew...) + + assert.Equal(t, c.expected, err.Error(), "case %d: error should match", n) + } +} + +func TestNewStringURLSanitizer(t *testing.T) { + var cases = []struct { + input string + placeholder bool + expected string + }{ + // case 0 + { + "https://github.com/go-gitea/test_repo.git", + true, + "https://github.com/go-gitea/test_repo.git", + }, + // case 1 + { + "https://github.com/go-gitea/test_repo.git", + false, + "https://github.com/go-gitea/test_repo.git", + }, + // case 2 + { + "https://mytoken@github.com/go-gitea/test_repo.git", + true, + "https://" + userPlaceholder + "@github.com/go-gitea/test_repo.git", + }, + // case 3 + { + "https://mytoken@github.com/go-gitea/test_repo.git", + false, + "https://github.com/go-gitea/test_repo.git", + }, + // case 4 + { + "https://user:password@github.com/go-gitea/test_repo.git", + true, + "https://" + userPlaceholder + "@github.com/go-gitea/test_repo.git", + }, + // case 5 + { + "https://user:password@github.com/go-gitea/test_repo.git", + false, + "https://github.com/go-gitea/test_repo.git", + }, + // case 6 + { + "https://gi\nthub.com/go-gitea/test_repo.git", + false, + unparsableURL, + }, + } + + for n, c := range cases { + // uses NewURLSanitizer internally + result := NewStringURLSanitizer(c.input, c.placeholder).Replace(c.input) + + assert.Equal(t, c.expected, result, "case %d: error should match", n) + } +} + +func TestNewStringURLSanitizedError(t *testing.T) { + var cases = []struct { + input string + placeholder bool + expected string + }{ + // case 0 + { + "https://github.com/go-gitea/test_repo.git", + true, + "https://github.com/go-gitea/test_repo.git", + }, + // case 1 + { + "https://github.com/go-gitea/test_repo.git", + false, + "https://github.com/go-gitea/test_repo.git", + }, + // case 2 + { + "https://mytoken@github.com/go-gitea/test_repo.git", + true, + "https://" + userPlaceholder + "@github.com/go-gitea/test_repo.git", + }, + // case 3 + { + "https://mytoken@github.com/go-gitea/test_repo.git", + false, + "https://github.com/go-gitea/test_repo.git", + }, + // case 4 + { + "https://user:password@github.com/go-gitea/test_repo.git", + true, + "https://" + userPlaceholder + "@github.com/go-gitea/test_repo.git", + }, + // case 5 + { + "https://user:password@github.com/go-gitea/test_repo.git", + false, + "https://github.com/go-gitea/test_repo.git", + }, + // case 6 + { + "https://gi\nthub.com/go-gitea/test_repo.git", + false, + unparsableURL, + }, + } + + encloseText := func(input string) string { + return "test " + input + " test" + } + + for n, c := range cases { + err := errors.New(encloseText(c.input)) + + result := NewStringURLSanitizedError(err, c.input, c.placeholder) + + assert.Equal(t, encloseText(c.expected), result.Error(), "case %d: error should match", n) } } diff --git a/options/gitignore/Android b/options/gitignore/Android index 23de6e20a61f1..5d18272eb42e4 100644 --- a/options/gitignore/Android +++ b/options/gitignore/Android @@ -44,6 +44,7 @@ captures/ .idea/assetWizardSettings.xml .idea/dictionaries .idea/libraries +.idea/jarRepositories.xml # Android Studio 3 in .gitignore file. .idea/caches .idea/modules.xml diff --git a/options/gitignore/Autotools b/options/gitignore/Autotools index d9ecd8928acc3..617156f8192de 100644 --- a/options/gitignore/Autotools +++ b/options/gitignore/Autotools @@ -45,8 +45,8 @@ m4/ltsugar.m4 m4/ltversion.m4 m4/lt~obsolete.m4 -# Generated Makefile -# (meta build system like autotools, +# Generated Makefile +# (meta build system like autotools, # can automatically generate from config.status script # (which is called by configure script)) Makefile diff --git a/options/gitignore/Xojo b/options/gitignore/Xojo index 1b036dd4f2eb1..4915783bf0d98 100644 --- a/options/gitignore/Xojo +++ b/options/gitignore/Xojo @@ -8,4 +8,4 @@ Debug*/Debug*.exe Debug*/Debug*\ Libs *.rbuistate *.xojo_uistate -*.obsolete +*.obsolete* diff --git a/options/locale/locale_bg-BG.ini b/options/locale/locale_bg-BG.ini index 1fd266b5d7824..ebfc59d1668ca 100644 --- a/options/locale/locale_bg-BG.ini +++ b/options/locale/locale_bg-BG.ini @@ -82,6 +82,7 @@ loading=Зареждане… error404=Страницата, която се опитвате да достъпите, не съществува или не сте оторизирани да я достъпите. + [error] [startpage] diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index 2871a0f865b5f..576cf0615e3b9 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -93,6 +93,7 @@ step2=Krok 2: error404=Stránka, kterou se snažíte zobrazit, buď neexistuje, nebo nemáte oprávnění ji zobrazit. + [error] occurred=Nastala chyba report_message=Pokud jste si jisti, že se jedná o chybu Gitea, prosím vyhledejte problém na GitHub a v případě potřeby otevřete nový problém. @@ -206,6 +207,7 @@ default_enable_timetracking_popup=Povolí sledování času pro nové repozitá no_reply_address=Skrytá e-mailová doména no_reply_address_helper=Název domény pro uživatele se skrytou e-mailovou adresou. Příklad: Pokud je název skryté e-mailové domény nastaven na „noreply.example.org“, uživatelské jméno „joe“ bude zaznamenáno v Gitu jako „joe@noreply.example.org“. password_algorithm=Hash algoritmus hesla +password_algorithm_helper=Nastavte algoritmus hashování hesla. Algoritmy mají odlišné požadavky a sílu. `argon2` používá mnoho paměti a může být nevhodný pro malé systémy. [home] uname_holder=Uživatelské jméno nebo e-mailová adresa @@ -318,8 +320,13 @@ reset_password=Obnovit váš účet register_success=Registrace byla úspěšná register_notify=Vítejte v Gitea +release.new.subject=%s v %s vydáno +repo.transfer.subject_to=%s by chtěl převést „%s“ pro %s +repo.transfer.subject_to_you=%s by Vám chtěl převést „%s“ +repo.transfer.to_you=vám +repo.collaborator.added.subject=%s vás přidal do %s [modal] yes=Ano @@ -414,6 +421,7 @@ repositories=Repozitáře activity=Veřejná aktivita followers=Sledující starred=Oblíbené repozitáře +watched=Sledované repozitáře projects=Projekty following=Sledovaní follow=Sledovat @@ -667,6 +675,7 @@ email_notifications.disable=Zakázat e-mailová oznámení email_notifications.submit=Nastavit předvolby e-mailu [repo] +new_repo_helper=Repozitář obsahuje všechny projektové soubory, včetně historie revizí. Už ho máte jinde? Migrovat repozitář. owner=Vlastník owner_helper=Některé organizace se nemusejí v seznamu zobrazit kvůli maximálnímu dosaženému počtu repozitářů. repo_name=Název repozitáře @@ -692,26 +701,39 @@ repo_desc=Popis repo_desc_helper=Zadejte krátký popis (volitelné) repo_lang=Jazyk repo_gitignore_helper=Vyberte šablony .gitignore. +repo_gitignore_helper_desc=Vyberte soubory, které nechcete sledovat ze seznamu šablon pro běžné jazyky. Typické artefakty generované nástroji pro sestavení každého jazyka jsou ve výchozím stavu součástí .gitignore. issue_labels=Štítky úkolů issue_labels_helper=Vyberte sadu štítků úkolů. license=Licence license_helper=Vyberte licenční soubor. +license_helper_desc=Licence řídí, co ostatní mohou a nemohou dělat s vaším kódem. Nejste si jisti, která je pro váš projekt správná? Podívejte se na Zvolte licenci readme=README readme_helper=Vyberte šablonu souboru README. +readme_helper_desc=Toto je místo, kde můžete napsat úplný popis vašeho projektu. auto_init=Inicializovat repozitář (Přidá .gitignore, License a README) +trust_model_helper=Vyberte model důvěry pro ověření podpisu. Možnosti jsou: trust_model_helper_collaborator=Spolupracovník: Důvěřovat podpisům spolupracovníků trust_model_helper_committer=Přispěvatel: Důvěřovat podpisům, které se shodují s přispěvateli +trust_model_helper_collaborator_committer=Spolupracovník+Tvůrce revize: Důvěřovat podpisům od spolupracovníků, které odpovídají tvůrci revize +trust_model_helper_default=Výchozí: Použít výchozí model důvěry pro tuto instalaci create_repo=Vytvořit repozitář default_branch=Výchozí větev +default_branch_helper=Výchozí větev je základní větev pro požadavky na natažení a revize kódu. mirror_prune=Vyčistit mirror_prune_desc=Odstranit zastaralé reference na vzdálené sledování mirror_interval=Interval zrcadlení (platné časové jednotky jsou „h“, „m“ a „s“). 0 zakáže automatickou synchronizaci. mirror_interval_invalid=Interval zrcadlení není platný. mirror_address=Klonovat z URL -mirror_address_desc=Zadejte nějaké přístupové údaje do sekce Ověření klonování. mirror_address_url_invalid=Poskytnutá URL je neplatná. Všechny komponenty musíte správně nahradit escape sekvencí. mirror_address_protocol_invalid=Zadaná URL je neplatná. Mohou být zrcadleny pouze umístění http(s):// nebo git://. +mirror_lfs=Úložiště velkých souborů (LFS) +mirror_lfs_desc=Aktivovat zrcadlení dat LFS. +mirror_lfs_endpoint=Koncový bod LFS +mirror_lfs_endpoint_desc=Synchronizace se pokusí použít URL pro klonování k určení LFS serveru. Můžete také zadat vlastní koncový bod, pokud jsou data LFS repozitáře uložena někde jinde. mirror_last_synced=Poslední synchronizace +mirror_password_placeholder=(Nezměněno) +mirror_password_blank_placeholder=(Nenastaveno) +mirror_password_help=Změňte uživatelské jméno pro vymazání uloženého hesla. watchers=Sledující stargazers=Sledující forks=Rozštěpení @@ -764,11 +786,15 @@ form.reach_limit_of_creation_n=Již jste dosáhli svůj limit %d repozitářů. form.name_reserved=Jméno repozitáře „%s“ je rezervované. form.name_pattern_not_allowed=Vzor „%s“ není povolený v názvu repozitáře. -need_auth=Ověření klonování migrate_options=Možnosti migrace migrate_service=Migrační služba migrate_options_mirror_helper=Tento repozitář bude zrcadlem migrate_options_mirror_disabled=Administrátor vašeho webu zakázal nová zrcadla. +migrate_options_lfs=Migrovat LFS soubory +migrate_options_lfs_endpoint.label=Koncový bod LFS +migrate_options_lfs_endpoint.description=Migrace se pokusí použít váš vzdálený Git pro určení LFS serveru. Můžete také zadat vlastní koncový bod, pokud jsou data LFS repozitáře uložena někde jinde. +migrate_options_lfs_endpoint.description.local=Podporována je také cesta k lokálnímu serveru. +migrate_options_lfs_endpoint.placeholder=Ponechte prázdné pro odvození z URL adresy pro klonování migrate_items=Položky pro migrování migrate_items_wiki=Wiki migrate_items_milestones=Milníky @@ -782,7 +808,10 @@ migrate.clone_address=Migrovat / klonovat z URL migrate.clone_address_desc=HTTP(S) nebo URL pro klonování existujícího repozitáře migrate.clone_local_path=nebo místní cesta serveru migrate.permission_denied=Není dovoleno importovat místní repozitáře. +migrate.permission_denied_blocked=Nemáte oprávnění provést import z blokovaných serverů. +migrate.permission_denied_private_ip=Nemáte oprávnění provést import ze soukromých IP adres. migrate.invalid_local_path=Místní cesta je neplatná, buď neexistuje nebo není adresářem. +migrate.invalid_lfs_endpoint=Koncový bod LFS není platný. migrate.failed=Přenesení selhalo: %v migrate.migrate_items_options=Pro migraci dalších položek je vyžadován přístupový token migrated_from=Migrováno z %[2]s @@ -827,6 +856,7 @@ branch=Větev tree=Strom clear_ref=`Vymazat aktuální referenci" filter_branch_and_tag=Filtr pro větev nebo značku +find_tag=Najít značku branches=Větve tags=Značky issues=Úkoly @@ -1096,6 +1126,8 @@ issues.context.edit=Upravit issues.context.delete=Smazat issues.no_content=Není zde žádný obsah. issues.close_issue=Zavřít +issues.pull_merged_at=`sloučil(a) revizi %[2]s do %[3]s %[4]s` +issues.manually_pull_merged_at=`sloučil(a) revizi %[2]s do %[3]s ručně %[4]s` issues.close_comment_issue=Okomentovat a zavřít issues.reopen_issue=Znovuotevřít issues.reopen_comment_issue=Okomentovat a znovuotevřít @@ -1117,6 +1149,8 @@ issues.re_request_review=Znovu požádat o posouzení issues.is_stale=Od tohoto posouzení došlo ke změnám v tomto požadavku na natažení issues.remove_request_review=Odstranit žádost o posouzení issues.remove_request_review_block=Nelze odstranit žádost o posouzení +issues.dismiss_review=Zamítnout posouzení +issues.dismiss_review_warning=Jste si jisti, že chcete zamítnout toto posouzení? issues.sign_in_require_desc=Přihlaste se pro zapojení do konverzace. issues.edit=Upravit issues.cancel=Zrušit @@ -1171,6 +1205,7 @@ issues.stop_tracking_history=`ukončil(a) práci %s` issues.cancel_tracking=Zahodit issues.cancel_tracking_history=`zrušil(a) sledování času %s` issues.add_time=Přidat čas ručně +issues.del_time=Odstranit tento časový záznam issues.add_time_short=Přidat čas issues.add_time_cancel=Zrušit issues.add_time_history=`přidal(a) strávený čas %s` @@ -1186,6 +1221,7 @@ issues.error_modifying_due_date=Změna termínu dokončení selhala. issues.error_removing_due_date=Odstranění termínu dokončení selhalo. issues.push_commit_1=přidal(a) %d revizi %s issues.push_commits_n=přidal(a) %d revize %s +issues.force_push_codes=`vynucené nahrání %[1]s od %[2]s do %[4]s %[6]s` issues.due_date_form=rrrr-mm-dd issues.due_date_form_add=Přidat termín dokončení issues.due_date_form_edit=Upravit @@ -1228,6 +1264,8 @@ issues.review.self.approval=Nemůžete schválit svůj požadavek na natažení. issues.review.self.rejection=Nemůžete požadovat změny ve svém vlastním požadavku na natažení. issues.review.approve=schválil tyto změny %s issues.review.comment=posoudil %s +issues.review.dismissed=zamítl(a) posouzení od %s %s +issues.review.dismissed_label=Zamítnuto issues.review.left_comment=zanechal komentář issues.review.content.empty=Je potřeba zanechat poznámku s uvedením požadované změny (požadovaných změn). issues.review.reject=požadované změny %s @@ -1249,6 +1287,8 @@ issues.review.resolved_by=označil tuto konverzaci jako vyřešenou issues.assignee.error=Ne všichni zpracovatelé byli přidáni z důvodu neočekávané chyby. issues.reference_issue.body=Tělo zprávy +compare.compare_base=základ +compare.compare_head=porovnat pulls.desc=Povolit požadavky na natažení a posuzování kódu. pulls.new=Nový požadavek na natažení @@ -1259,6 +1299,7 @@ pulls.compare_compare=natáhnout z pulls.filter_branch=Filtrovat větev pulls.no_results=Nebyly nalezeny žádné výsledky. pulls.nothing_to_compare=Tyto větve jsou stejné. Není potřeba vytvářet požadavek na natažení. +pulls.nothing_to_compare_and_allow_empty_pr=Tyto větve jsou stejné. Tento požadavek na natažení bude prázdný. pulls.has_pull_request=`Požadavek na natažení mezi těmito dvěma větvemi již existuje: %[2]s#%[3]d` pulls.create=Vytvořit požadavek na natažení pulls.title_desc=chce sloučit %[1]d revizí z větve %[2]s do %[3]s @@ -1272,9 +1313,14 @@ pulls.cant_reopen_deleted_branch=Tento požadavek na natažení nemůže být zn pulls.merged=Sloučený pulls.merged_as=Požadavek na natažení byl sloučen jako %[2]s. pulls.manually_merged=Sloučeno ručně +pulls.manually_merged_as=Požadavek na natažení byl ručně sloučen jako %[2]s. pulls.is_closed=Požadavek na natažení byl uzavřen. pulls.has_merged=Požadavek na natažení byl sloučen. pulls.title_wip_desc=`Začněte název s %s a zamezíte tak nechtěnému sloučení požadavku na natažení.` +pulls.cannot_merge_work_in_progress=Tento požadavek na natažení je označen jako probíhající práce. +pulls.still_in_progress=Stále probíhá? +pulls.add_prefix=Přidat prefix %s +pulls.remove_prefix=Odstranit prefix %s pulls.data_broken=Tento požadavek na natažení je rozbitý kvůli chybějícím informacím o rozštěpení. pulls.files_conflicted=Tento požadavek na natažení obsahuje změny, které kolidují s cílovou větví. pulls.is_checking=Právě probíhá kontrola konfliktů při sloučení. Zkuste to za chvíli. @@ -1299,6 +1345,7 @@ pulls.reject_count_1=%d žádost o změnu pulls.reject_count_n=%d žádosti o změnu pulls.waiting_count_1=%d čekající posouzení pulls.waiting_count_n=%d čekající posouzení +pulls.wrong_commit_id=ID revize musí být ID revize v cílové větvi pulls.no_merge_desc=Tento požadavek na natažení nemůže být sloučen, protože všechny možnosti repozitáře na sloučení jsou zakázány. pulls.no_merge_helper=Povolte možnosti sloučení v nastavení repozitáře nebo proveďte sloučení požadavku na natažení ručně. @@ -1310,6 +1357,7 @@ pulls.rebase_merge_pull_request=Rebase a sloučit pulls.rebase_merge_commit_pull_request=Rebase a sloučit (--no-ff) pulls.squash_merge_pull_request=Squash a sloučit pulls.merge_manually=Sloučeno ručně +pulls.merge_commit_id=ID slučovací revize pulls.require_signed_wont_sign=Větev vyžaduje podepsané revize, ale toto sloučení nebude podepsáno pulls.invalid_merge_option=Nemůžete použít tuto možnost sloučení pro tento požadavek na natažení. pulls.merge_conflict=Sloučení selhalo: Došlo ke konfliktu při sloučení. Tip: Zkuste jinou strategii @@ -1443,6 +1491,7 @@ activity.closed_issues_count_1=Uzavřený úkol activity.closed_issues_count_n=Uzavřené úkoly activity.title.issues_1=%d úkol activity.title.issues_n=%d úkolů +activity.title.issues_closed_from=%s uzavřel z %s activity.title.issues_created_by=%s vytvořil %s activity.closed_issue_label=Uzavřený activity.new_issues_count_1=Nový úkol @@ -1505,6 +1554,7 @@ settings.email_notifications.disable=Zakázat e-mailová oznámení settings.email_notifications.submit=Nastavit předvolby e-mailu settings.site=Webová stránka settings.update_settings=Aktualizovat nastavení +settings.branches.update_default_branch=Aktualizovat výchozí větev settings.advanced_settings=Pokročilá nastavení settings.wiki_desc=Povolit Wiki repozitáře settings.use_internal_wiki=Používat vestavěnou Wiki @@ -1532,6 +1582,8 @@ settings.pulls.allow_merge_commits=Povolit slučování revizí settings.pulls.allow_rebase_merge=Povolit rebase pro slučovací revize settings.pulls.allow_rebase_merge_commit=Povolit rebase s vyžádanou slučovací revizí (--no-ff) settings.pulls.allow_squash_commits=Povolit squash pro slučovací revize +settings.pulls.allow_manual_merge=Povolit označování požadavků na natažení jako ručně sloučené +settings.pulls.enable_autodetect_manual_merge=Povolit autodetekci ručních sloučení (Poznámka: V některých zvláštních případech může dojít k nesprávnému rozhodnutí) settings.projects_desc=Povolit projekty v repozitáři settings.admin_settings=Nastavení správce settings.admin_enable_health_check=Povolit kontrolu stavu repozitáře (git fsck) @@ -1766,6 +1818,7 @@ settings.block_on_official_review_requests_desc=Slučování nebude možné, pok settings.block_outdated_branch=Blokovat sloučení, pokud je požadavek na natažení zastaralý settings.block_outdated_branch_desc=Slučování nebude možné, pokud je hlavní větev za základní větví. settings.default_branch_desc=Vybrat výchozí větev repozitáře pro požadavky na natažení a revize kódu: +settings.default_merge_style_desc=Výchozí styl sloučení pro požadavky na natažení: settings.choose_branch=Vyberte větev… settings.no_protected_branch=Nejsou tu žádné chráněné větve. settings.edit_protected_branch=Upravit @@ -1834,13 +1887,15 @@ diff.whitespace_ignore_at_eol=Ignorovat změny v bílých znacích na konci ří diff.stats_desc= %d změnil soubory, kde provedl %d přidání a %d odebrání diff.stats_desc_file=%d změn: %d přidání a %d smazání diff.bin=binární +diff.bin_not_shown=Binární soubor nebyl zobrazen. diff.view_file=Zobrazit soubor diff.file_before=Před diff.file_after=Za diff.file_image_width=Šířka diff.file_image_height=Výška diff.file_byte_size=Velikost -diff.file_suppressed=Diff nebyl zobrazen, protože je příliš veliký +diff.file_suppressed=Rozdílový obsah nebyl zobrazen, protože je příliš veliký +diff.file_suppressed_line_too_long=Rozdílový obsah nebyl zobrazen, protože některé řádky jsou příliš dlouhá diff.too_many_files=Některé soubory nejsou zobrazny, neboť je v této revizi změněno mnoho souborů diff.comment.placeholder=Zanechat komentář diff.comment.markdown_info=Je podporována úprava vzhledu pomocí markdown. @@ -1857,6 +1912,7 @@ diff.review.reject=Požadovat změny diff.committed_by=odevzdal diff.protected=Chráněno diff.image.side_by_side=Vedle sebe +diff.image.swipe=Posunout diff.image.overlay=Překrytí releases.desc=Sledování verzí projektu a souborů ke stažení. @@ -1867,6 +1923,7 @@ release.new_release=Nové vydání release.draft=Koncept release.prerelease=Předběžná verze release.stable=Stabilní +release.compare=Porovnat release.edit=upravit release.ahead.commits=%d revizí release.ahead.target=do %s od tohoto vydání @@ -1924,6 +1981,10 @@ branch.restore=Obnovit větev „%s“ branch.download=Stáhnout větev „%s“ branch.included_desc=Tato větev je součástí výchozí větve branch.included=Zahrnuje +branch.create_new_branch=Vytvořit větev z větve: +branch.confirm_create_branch=Vytvořit větev +branch.new_branch=Vytvořit novou větev +branch.new_branch_from=Vytvořit novou větev z „%s“ tag.create_tag=Vytvořit značku %s tag.create_success=Značka „%s“ byla vytvořena. @@ -1933,6 +1994,9 @@ topic.done=Hotovo topic.count_prompt=Nelze vybrat více než 25 témat topic.format_prompt=Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků. +error.csv.too_large=Tento soubor nelze vykreslit, protože je příliš velký. +error.csv.unexpected=Tento soubor nelze vykreslit, protože obsahuje neočekávaný znak na řádku %d ve sloupci %d. +error.csv.invalid_field_count=Soubor nelze vykreslit, protože má nesprávný počet polí na řádku %d. [org] org_name_holder=Název organizace @@ -2081,6 +2145,7 @@ dashboard.cron.error=Chyba v naplánované úloze: %s: %[3]s dashboard.cron.finished=Naplánovaná úloha: %[1]s skončila dashboard.delete_inactive_accounts=Smazat všechny neaktivované účty dashboard.delete_inactive_accounts.started=Spuštěna úloha mazání všech neaktivovaných účtů. +dashboard.delete_repo_archives=Odstranit všechny archivy repozitáře (ZIP, TAR.GZ, atd.) dashboard.delete_repo_archives.started=Spuštěna úloha smazání všech archivovaných repozitářů. dashboard.delete_missing_repos=Smazat všechny repozitáře, které nemají Git soubory dashboard.delete_missing_repos.started=Spuštěna úloha mazání všech repozitářů, které nemají Git soubory. @@ -2129,6 +2194,8 @@ dashboard.total_gc_time=Celková pauza GC dashboard.total_gc_pause=Celková pauza GC dashboard.last_gc_pause=Poslední pauza GC dashboard.gc_times=Časy GC +dashboard.delete_old_actions=Odstranit všechny staré akce z databáze +dashboard.delete_old_actions.started=Začalo odstraňování všech starých akcí z databáze. users.user_manage_panel=Správa uživatelských účtů users.new_account=Vytvořit uživatelský účet @@ -2254,6 +2321,7 @@ auths.allowed_domains_helper=Nechte prázdné k povolení všech domén. Oddělt auths.enable_tls=Povolit šifrování TLS auths.skip_tls_verify=Přeskočit ověření TLS auths.pam_service_name=Název služby PAM +auths.pam_email_domain=PAM e-mailová doména (volitelné) auths.oauth2_provider=Poskytovatel OAuth2 auths.oauth2_icon_url=URL ikony auths.oauth2_clientID=Klientské ID (klíč) @@ -2353,6 +2421,7 @@ config.db_path=Cesta config.service_config=Nastavení služby config.register_email_confirm=Pro registraci vyžadovat potvrzení e-mailu config.disable_register=Vypnout možnost uživatelské registrace +config.allow_only_internal_registration=Povolit registraci pouze prostřednictvím Gitea config.allow_only_external_registration=Povolit registraci pouze prostřednictvím externích služeb config.enable_openid_signup=Povolit automatickou registraci pomocí OpenID config.enable_openid_signin=Povolit přihlášení pomocí OpenID @@ -2546,6 +2615,7 @@ mirror_sync_delete=synchronizoval(a) a smazal(a) referenci %[2]s v approve_pull_request=`schválil(a) %s#%[2]s` reject_pull_request=`navrhl(a) změny pro %s#%[2]s` publish_release=`vydána značka "%[4]s" v %[3]s` +review_dismissed=`zamítl posouzení z %[4]s pro %[3]s#%[2]s` review_dismissed_reason=Důvod: create_branch=vytvořena větev %[3]s v %[4]s diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index c2182f0bdfa8c..0de4f5b5323e1 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -93,6 +93,7 @@ step2=Schritt 2: error404=Die Seite, die du gerade versuchst aufzurufen, existiert entweder nicht oder du bist nicht berechtigt, diese anzusehen. + [error] occurred=Ein Fehler ist aufgetreten report_message=Wenn du dir sicher bist, dass dies ein Gitea-Fehler ist, suche bitte auf GitHub nach diesem Fehler und erstelle gegebenenfalls einen neuen Bugreport. @@ -723,7 +724,6 @@ mirror_prune_desc=Entferne veraltete remote-tracking Referenzen mirror_interval=Spiegel-Intervall (gültige Zeiteinheiten sind 'h', 'm', 's'). 0 schaltet die automatische Synchronisierung aus. mirror_interval_invalid=Das Spiegel-Intervall ist ungültig. mirror_address=Klonen via URL -mirror_address_desc=Gib alle erforderlichen Anmeldedaten im Abschnitt "Autorisierung klonen" ein. mirror_address_url_invalid=Die angegebene URL ist ungültig. Achte darauf, alle URL-Komponenten korrekt zu maskieren. mirror_address_protocol_invalid=Die angegebene URL ist ungültig. Nur Pfade beginnend mit http(s):// oder git:// können gespiegelt werden. mirror_lfs=Großdatei-Speicher (LFS) @@ -783,7 +783,6 @@ form.reach_limit_of_creation_n=Du hast bereits dein Limit von %d Repositories er form.name_reserved=Der Repository-Name „%s“ ist reserviert. form.name_pattern_not_allowed='%s' ist nicht erlaubt für Repository-Namen. -need_auth=Authentifizierung zum Klonen benötigt migrate_options=Migrationsoptionen migrate_service=Migrationsdienst migrate_options_mirror_helper=Dieses Repository wird ein Mirror sein @@ -1315,6 +1314,7 @@ pulls.manually_merged_as=Dieser Pull Request wurde manuell als Beginne den Titel mit %s um zu verhindern, dass der Pull Request versehentlich gemergt wird.` +pulls.cannot_merge_work_in_progress=Dieser Pull Request ist als Work in Progress markiert. pulls.data_broken=Dieser Pull-Requests ist kaputt, da Fork-Informationen gelöscht wurden. pulls.files_conflicted=Dieser Pull-Request hat Änderungen, die im Widerspruch zum Ziel-Branch stehen. pulls.is_checking=Die Konfliktprüfung läuft noch. Bitte aktualisiere die Seite in wenigen Augenblicken. diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 4df9965bcb07a..2fa70679d8281 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -91,8 +91,11 @@ loading = Loading… step1 = Step 1: step2 = Step 2: +error = Error error404 = The page you are trying to reach either does not exist or you are not authorized to view it. +never = Never + [error] occurred = An error has occurred report_message = If you are sure this is a Gitea bug, please search for issue on GitHub and open new issue if necessary. @@ -724,7 +727,7 @@ mirror_prune_desc = Remove obsolete remote-tracking references mirror_interval = Mirror Interval (valid time units are 'h', 'm', 's'). 0 to disable automatic sync. mirror_interval_invalid = The mirror interval is not valid. mirror_address = Clone From URL -mirror_address_desc = Put any required credentials in the Clone Authorization section. +mirror_address_desc = Put any required credentials in the Authorization section. mirror_address_url_invalid = The provided url is invalid. You must escape all components of the url correctly. mirror_address_protocol_invalid = The provided url is invalid. Only http(s):// or git:// locations can be mirrored from. mirror_lfs = Large File Storage (LFS) @@ -787,7 +790,7 @@ form.reach_limit_of_creation_n = You have already reached your limit of %d repos form.name_reserved = The repository name '%s' is reserved. form.name_pattern_not_allowed = The pattern '%s' is not allowed in a repository name. -need_auth = Clone Authorization +need_auth = Authorization migrate_options = Migration Options migrate_service = Migration Service migrate_options_mirror_helper = This repository will be a mirror @@ -821,11 +824,19 @@ migrated_from_fake = Migrated From %[1]s migrate.migrate = Migrate From %s migrate.migrating = Migrating from %s ... migrate.migrating_failed = Migrating from %s failed. +migrate.migrating_failed.error = Error: %s migrate.github.description = Migrating data from Github.com or Github Enterprise. migrate.git.description = Migrating or Mirroring git data from Git services migrate.gitlab.description = Migrating data from GitLab.com or Self-Hosted gitlab server. migrate.gitea.description = Migrating data from Gitea.com or Self-Hosted Gitea server. migrate.gogs.description = Migrating data from notabug.org or other Self-Hosted Gogs server. +migrate.migrating_git = Migrating Git Data +migrate.migrating_topics = Migrating Topics +migrate.migrating_milestones = Migrating Milestones +migrate.migrating_labels = Migrating Labels +migrate.migrating_releases = Migrating Releases +migrate.migrating_issues = Migrating Issues +migrate.migrating_pulls = Migrating Pull Requests mirror_from = mirror of forked_from = forked from @@ -1548,6 +1559,15 @@ settings.hooks = Webhooks settings.githooks = Git Hooks settings.basic_settings = Basic Settings settings.mirror_settings = Mirror Settings +settings.mirror_settings.docs = Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically. How do I mirror repositories? +settings.mirror_settings.mirrored_repository = Mirrored repository +settings.mirror_settings.direction = Direction +settings.mirror_settings.direction.pull = Pull +settings.mirror_settings.direction.push = Push +settings.mirror_settings.last_update = Last update +settings.mirror_settings.push_mirror.none = No push mirrors configured +settings.mirror_settings.push_mirror.remote_url = Git Remote Repository URL +settings.mirror_settings.push_mirror.add = Add Push Mirror settings.sync_mirror = Synchronize Now settings.mirror_sync_in_progress = Mirror synchronization is in progress. Check back in a minute. settings.email_notifications.enable = Enable Email Notifications @@ -1613,6 +1633,7 @@ settings.transfer_form_title = Enter the repository name as confirmation: settings.transfer_in_progress = There is currently an ongoing transfer. Please cancel it if you will like to transfer this repository to another user. settings.transfer_notices_1 = - You will lose access to the repository if you transfer it to an individual user. settings.transfer_notices_2 = - You will keep access to the repository if you transfer it to an organization that you (co-)own. +settings.transfer_notices_3 = - If the repository is private and is transferred to an individual user, this action makes sure that the user does have at least read permission (and changes permissions if necessary). settings.transfer_owner = New Owner settings.transfer_perform = Perform Transfer settings.transfer_started = This repository has been marked for transfer and awaits confirmation from "%s" diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index dd1d90036e533..2ece1992e619b 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -91,8 +91,11 @@ loading=Cargando… step1=Paso 1: step2=Paso 2: +error=Error error404=La página a la que está intentando acceder o no existe o no está autorizado para verla. +never=Nunca + [error] occurred=Se ha producido un error report_message=Si estás seguro de que este es un error de Gitea, por favor busca un problema en GitHub y abre un nuevo problema si es necesario. @@ -723,7 +726,6 @@ mirror_prune_desc=Eliminar referencias de seguimiento de remotes obsoletas mirror_interval=Intervalo de réplica (Las unidades de tiempo válidas son 'h', 'm', 's'). Pone 0 para deshabilitar la sincronización automática. mirror_interval_invalid=El intervalo de réplica no es válido. mirror_address=Clonar desde URL -mirror_address_desc=Agregue las credenciales que sean necesarias en la sección de Autorización de Clonado. mirror_address_url_invalid=La url proporcionada no es válida. Debe escapar correctamente de todos los componentes de la url. mirror_address_protocol_invalid=La url proporcionada no es válida. Sólo las ubicaciones http(s):// o git:// pueden ser replicadas desde. mirror_lfs=Almacenamiento de archivos grande (LFS) @@ -731,6 +733,7 @@ mirror_lfs_desc=Activar la reproducción de datos LFS. mirror_lfs_endpoint=Punto final de LFS mirror_lfs_endpoint_desc=Sync intentará usar la url del clon para determinar el servidor LFS. También puede especificar un punto final personalizado si los datos LFS del repositorio se almacenan en otro lugar. mirror_last_synced=Sincronizado por última vez +mirror_password_placeholder=(Sin cambios) watchers=Seguidores stargazers=Fans forks=Forks @@ -783,7 +786,7 @@ form.reach_limit_of_creation_n=Ya han alcanzado su límite de repositorios de %d form.name_reserved=El nombre de repositorio '%s' está reservado. form.name_pattern_not_allowed=El patrón '%s' no está permitido en un nombre de repositorio. -need_auth=Autorización de clonación +need_auth=Autorización migrate_options=Opciones de migración migrate_service=Servicio de Migración migrate_options_mirror_helper=Este repositorio será uno replicado @@ -1315,6 +1318,7 @@ pulls.manually_merged_as=El Pull Request se ha fusionado manualmente como Comience el título con %s para prevenir que el pull request se fusione accidentalmente.` +pulls.still_in_progress=¿Aún en curso? pulls.data_broken=Este pull request está rota debido a que falta información del fork. pulls.files_conflicted=Este pull request tiene cambios en conflicto con la rama de destino. pulls.is_checking=La comprobación de conflicto de fusión está en progreso. Inténtalo de nuevo en unos momentos. @@ -1540,6 +1544,11 @@ settings.hooks=Webhooks settings.githooks=Git Hooks settings.basic_settings=Configuración Básica settings.mirror_settings=Configuración de réplica +settings.mirror_settings.docs=Configure su proyecto para insertar y/o extraer automáticamente los cambios hacia/desde otro repositorio. Las ramas, etiquetas y commits se sincronizarán automáticamente. ¿Cómo replico los repositorios? +settings.mirror_settings.direction=Dirección +settings.mirror_settings.direction.pull=Pull +settings.mirror_settings.last_update=Última actualización +settings.mirror_settings.push_mirror.remote_url=URL del repositorio remoto de Git settings.sync_mirror=Sincronizar ahora settings.mirror_sync_in_progress=La sincronización del repositorio replicado está en curso. Vuelva a intentarlo más tarde. settings.email_notifications.enable=Habilitar las notificaciones por correo electrónico @@ -1605,6 +1614,7 @@ settings.transfer_form_title=Escriba el nombre del repositorio como confirmació settings.transfer_in_progress=Actualmente hay una transferencia en curso. Por favor, cancela si quieres transferir este repositorio a otro usuario. settings.transfer_notices_1=- Perderá el acceso al repositorio si lo transfiere a un usuario individual. settings.transfer_notices_2=- Mantendrá el acceso al repositorio si lo transfiere a una organización que usted (co-)posee. +settings.transfer_notices_3=- Si el repositorio es privado y se transfiere a un usuario individual, esta acción se asegura de que el usuario tenga al menos permisos de lectura (y cambie los permisos si es necesario). settings.transfer_owner=Nuevo Propietario settings.transfer_perform=Realizar transferencia settings.transfer_started=Este repositorio ha sido marcado para transferencia y espera confirmación de "%s" @@ -1975,6 +1985,10 @@ branch.restore=Restaurar rama '%s' branch.download=Descargar rama '%s' branch.included_desc=Esta rama forma parte de la predeterminada branch.included=Incluida +branch.create_new_branch=Crear rama desde la rama: +branch.confirm_create_branch=Crear rama +branch.new_branch=Crear nueva rama +branch.new_branch_from=Crear nueva rama desde '%s' tag.create_tag=Crear etiqueta %s tag.create_success=La etiqueta '%s' ha sido creada. diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini index aa6f5e9dbbb41..ce9e429e31294 100644 --- a/options/locale/locale_fa-IR.ini +++ b/options/locale/locale_fa-IR.ini @@ -87,6 +87,7 @@ loading=بارگذاری… error404=صفحه موردنظر شما یا وجود ندارد یا شما دسترسی کافی برای مشاهده آن را ندارید. + [error] occurred=خطایی رخ داده است report_message=اگر شما مطمئن هستیند این مشکل مربوط به یک باگ در Gitea است، لطفا در GitHub مشکل را جستجو کنید و در صورت نیاز، یک موضوع جدید باز کنید. @@ -665,7 +666,6 @@ mirror_prune_desc=حذف منابع پیگیری‌راه‌دور منسوخ mirror_interval=بازه زمانی قرینه سازی (mirror) با 'h', 'm', 's'. برای غیر فعال کردن همگام سازی خودکار 0 بگذارید. mirror_interval_invalid=بازه زمانی سازی قرینه نیست. mirror_address=همسان‌سازی از نشانی -mirror_address_desc=هر گواهینامه لازم را در بخش Clone Authority (مجوز همسان‌سازی) قرار دهید. mirror_address_url_invalid=Url ارائه شده نامعتبر است. شما باید از تمام اجزای Url صحیح گزیر بزنید. mirror_address_protocol_invalid=نشانی ارائه شده غیرمعتبر است. فقط استفاده از http(s):// یا git:// می‌تواند قرینه شوند. mirror_last_synced=آخرین همگام سازی @@ -704,7 +704,6 @@ archive.pull.nocomment=این مخزن بایگانی شده. شما نمی تو form.name_reserved=یک مخزن با نام '%s' از قبل وجود دارد. form.name_pattern_not_allowed=الگوی %s در نام مخزن مجاز نیست. -need_auth=مجوز همسان‌سازی migrate_items=مولفه های مهاجرت migrate_items_wiki=دانشنامه migrate_items_milestones=نقاط عطف diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini index 43f2f845ccf00..7d3db4c28cbe8 100644 --- a/options/locale/locale_fi-FI.ini +++ b/options/locale/locale_fi-FI.ini @@ -81,6 +81,7 @@ loading=Ladataan… error404=Sivu, jota yrität nähdä, joko ei löydy tai et ole oikeutettu katsomaan sitä. + [error] occurred=Tapahtui virhe @@ -523,7 +524,6 @@ template.issue_labels=Ongelmien tunnisteet -need_auth=Kloonauksen valtuutus migrate_items=Siirrettävät asiat migrate_items_wiki=Wiki migrate_items_milestones=Merkkipaalut diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index e8e56585969fa..b96e0d56eb5b3 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -93,6 +93,7 @@ step2=Étape 2: error404=La page que vous essayez d'atteindre n'existe pas ou vous n'êtes pas autorisé à la voir. + [error] occurred=Une erreur est survenue report_message=Si vous êtes sûr qu'il s'agit d'un bug de Gitea, cherchez s’il existe un ticket sur GitHub et ouvrez-en un nouveau si nécessaire. @@ -721,7 +722,6 @@ mirror_prune_desc=Supprimer les références externes obsolètes mirror_interval=Intervalle de synchronisation ('h', 'm', et 's' sont des unités valides), 0 pour désactiver. mirror_interval_invalid=L'intervalle de synchronisation est invalide. mirror_address=Cloner depuis une URL -mirror_address_desc=Mettez les identifiants requis dans la section Autorisation de Clonage. mirror_address_url_invalid=L'url fournie est invalide. Vous devez échapper tous les composants de l'url correctement. mirror_address_protocol_invalid=L'url fournie est invalide. Seuls les protocoles http(s):// ou git:// peuvent être la source du miroir. mirror_lfs=Stockage de fichiers volumineux (LFS) @@ -781,7 +781,6 @@ form.reach_limit_of_creation_n=Vous avez déjà atteint la limite de %d dépôts form.name_reserved=Le dépôt "%s" a un nom réservé. form.name_pattern_not_allowed="%s" n'est pas autorisé dans un nom de dépôt. -need_auth=Autorisations de clonage migrate_options=Options de migration migrate_service=Service de migration migrate_options_mirror_helper=Ce dépôt sera un miroir @@ -1264,8 +1263,8 @@ issues.review.dismissed_label=Rejeté issues.review.left_comment=laisser un commentaire issues.review.content.empty=Vous devez laisser un commentaire indiquant le(s) changement(s) demandé(s). issues.review.reject=a requis les changements %s -issues.review.wait=a demandé une révision pour %s -issues.review.add_review_request=demande de révision de %s %s +issues.review.wait=a été sollicité pour une révision %s +issues.review.add_review_request=a demandé une révision de %s %s issues.review.remove_review_request=a supprimé la demande de révision pour %s %s issues.review.remove_review_request_self=a refusé la revue %s issues.review.pending=En attente diff --git a/options/locale/locale_hu-HU.ini b/options/locale/locale_hu-HU.ini index 6e6a81e16a3e6..0669c679ee4c7 100644 --- a/options/locale/locale_hu-HU.ini +++ b/options/locale/locale_hu-HU.ini @@ -82,6 +82,7 @@ loading=Betöltés… error404=Az elérni kívánt oldal vagy nem létezik, vagy nincs jogosultsága a megtekintéséhez. + [error] occurred=Probléma lépett fel report_message=Ha biztos benne, hogy ez egy Gitea hiba, keressen a problémára a GitHub-on és hozzon létre új hibajelentést, ha szükséges. @@ -631,7 +632,6 @@ archive.pull.nocomment=Ez a tároló archíválva van. Nem szólhat hozzá ehhez form.name_reserved=A tárolónév ('%s') a rendszernek van fenntartva. form.name_pattern_not_allowed='%s' minta nem engedélyezett tárolónévben. -need_auth=Hitelesítés másoláshoz migrate_items_wiki=Wiki migrate_items_milestones=Mérföldkövek migrate_items_labels=Címkék diff --git a/options/locale/locale_id-ID.ini b/options/locale/locale_id-ID.ini index 1cb3e1f0f0c60..d96dea88fb218 100644 --- a/options/locale/locale_id-ID.ini +++ b/options/locale/locale_id-ID.ini @@ -81,6 +81,7 @@ loading=Memuat… + [error] [startpage] @@ -639,7 +640,6 @@ archive.pull.nocomment=Repositori ini diarsipkan. Anda tidak dapat mengomentari form.name_reserved=Nama repositori '%s' dicadangkan. form.name_pattern_not_allowed=Pola '%s' tidak diperbolehkan dalam nama repositori. -need_auth=Otorisasi Kloning migrate_items=Ihwal Migrasi migrate_items_wiki=Wiki migrate_repo=Migrasi Repositori diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini index 869f1a1e5b15d..f9d0a869cf7cd 100644 --- a/options/locale/locale_it-IT.ini +++ b/options/locale/locale_it-IT.ini @@ -93,6 +93,7 @@ step2=Passo 2: error404=La pagina che stai cercando di raggiungere non esiste oppure non sei autorizzato a visualizzarla. + [error] occurred=Si è verificato un errore report_message=Se sei sicuro che sia un bug di Gitea, cerca il problema su GitHub e apri una nuova segnalazione se necessario. @@ -702,7 +703,6 @@ mirror_prune_desc=Rimuovi i riferimenti di puntamento-remoto obsoleti mirror_interval=Intervallo del Mirror (unità di tempo valide 'h', 'm', 's'). 0 per disabilitare la sincronizzazione automatica. mirror_interval_invalid=L'intervallo di aggiornamento dei mirror non è valido. mirror_address=Clona da URL -mirror_address_desc=Inserisci le credenziali richieste nella scheda Clone sezione Autorizzazione. mirror_address_url_invalid=L'url fornito non è valido. Devi effettuare l'escape completo tutti i componenti dell'Url. mirror_address_protocol_invalid=L'url fornito non è valido. Solo dai link http(s):// o git:// possono essere replicate. mirror_last_synced=Ultima sincronizzazione @@ -755,7 +755,6 @@ archive.pull.nocomment=Questo repository è archiviato. Non puoi commentare le r form.name_reserved=Il nome repository '%s' è riservato. form.name_pattern_not_allowed=Il modello '%s' non è consentito come nome di un repository. -need_auth=Autorizzazione clone migrate_options=Opzioni di migrazione migrate_service=Servizio migrazione migrate_options_mirror_helper=Questo repository sarà un mirror diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index adcbef83bf382..c93bf48802a35 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -93,6 +93,7 @@ step2=ステップ 2: error404=アクセスしようとしたページは存在しないか、閲覧が許可されていません。 + [error] occurred=エラーが発生しました report_message=Giteaのバグが疑われる場合は、GitHubでIssueを検索して、見つからなければ新しいIssueを作成してください。 @@ -723,7 +724,6 @@ mirror_prune_desc=不要になった古いリモートトラッキング参照 mirror_interval=ミラー間隔 (有効な時間の単位は'h'、'm'、's')。 自動的な同期を無効にする場合は0。 mirror_interval_invalid=ミラー間隔が不正です。 mirror_address=クローンするURL -mirror_address_desc=必要な資格情報は「クローン時の認証」セクションに設定してください。 mirror_address_url_invalid=入力したURLは無効です。 URLの構成要素はすべて正しくエスケープする必要があります。 mirror_address_protocol_invalid=入力したURLは無効です。 ミラーできるのは、http(s):// または git:// の場所からだけです。 mirror_lfs=Large File Storage (LFS) @@ -731,6 +731,9 @@ mirror_lfs_desc=LFS データのミラーリングを有効にする。 mirror_lfs_endpoint=LFS エンドポイント mirror_lfs_endpoint_desc=同期するときは、クローンURLをもとにLFSサーバーを決定しようとします。 リポジトリのLFSデータがほかの場所に保存されている場合は、独自のエンドポイントを指定することができます。 mirror_last_synced=前回の同期 +mirror_password_placeholder=(変更なし) +mirror_password_blank_placeholder=(未設定) +mirror_password_help=ユーザー名を変更すると保存されているパスワードは消去されます。 watchers=ウォッチャー stargazers=スターゲイザー forks=フォーク @@ -783,7 +786,6 @@ form.reach_limit_of_creation_n=すでにあなたが作成できるリポジト form.name_reserved=リポジトリ名 '%s' は予約されています。 form.name_pattern_not_allowed='%s' の形式はリポジトリ名に使用できません。 -need_auth=クローン時の認証 migrate_options=移行オプション migrate_service=移行するサービス migrate_options_mirror_helper=このリポジトリをミラーにする diff --git a/options/locale/locale_ko-KR.ini b/options/locale/locale_ko-KR.ini index b8d5f21dc36c3..acf7efb4786c4 100644 --- a/options/locale/locale_ko-KR.ini +++ b/options/locale/locale_ko-KR.ini @@ -81,6 +81,7 @@ loading=불러오는 중... + [error] [startpage] @@ -593,7 +594,6 @@ template.topics=토론 주제 form.name_reserved=저장소 이름 '%s'은 예약 되어 있습니다. form.name_pattern_not_allowed='%s' 패턴은 저장소명으로 허용되지 않습니다. -need_auth=클론시 인증 migrate_items_wiki=위키 migrate_items_issues=이슈 migrate_repo=저장소 마이그레이션 diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index c427b37727837..f4db35671ac90 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -93,6 +93,7 @@ step2=Solis 2: error404=Lapa, ko vēlaties atvērt, neeksistē vai arī Jums nav tiesības to aplūkot. + [error] occurred=Radusies kļūda report_message=Ja esat drošs, ka šī ir Gitea kļūda, pirms ziņošanas Gitea problēmās pārliecinieties, ka lietojat jaunāko Gitea versiju un par šādu kļūdu jau nav ziņots. @@ -717,7 +718,6 @@ mirror_prune_desc=Izdzēst visas ārējās atsauces, kas ārējā repozitorijā mirror_interval=Spoguļošanas biežums (atļautās laika vienības 'h', 'm' un 's'). Ievadiet 0, lai atslēgtu automātisko spoguļošanu. mirror_interval_invalid=Nekorekts spoguļošanas intervāls. mirror_address=Spoguļa adrese -mirror_address_desc=Pieslēgšanās rekvizītus norādiet autorizācijas sadaļā. mirror_address_url_invalid=Norādītais URL nav korekts. Norādiet visas URL daļas korekti. mirror_address_protocol_invalid=Norādītais URL nav korekts. Var spoguļot tikai no http(s):// vai git:// adresēm. mirror_last_synced=Pēdējo reizi sinhronizēts @@ -773,7 +773,6 @@ form.reach_limit_of_creation_n=Sasniegts Jums noteiktais %d repozitoriju ierobe form.name_reserved=Repozitorija nosaukums '%s' ir jau rezervēts. form.name_pattern_not_allowed=Repozitorija nosaukums '%s' nav atļauts. -need_auth=Nepieciešama autorizācija migrate_options=Migrācijas opcijas migrate_service=Migrācijas serviss migrate_options_mirror_helper=Šis repozitorijs būs spogulis diff --git a/options/locale/locale_ml-IN.ini b/options/locale/locale_ml-IN.ini index 04c6b22b7f8e8..c3705182f839f 100644 --- a/options/locale/locale_ml-IN.ini +++ b/options/locale/locale_ml-IN.ini @@ -75,6 +75,7 @@ loading=ലഭ്യമാക്കുന്നു… + [error] [startpage] @@ -611,7 +612,6 @@ archive.pull.nocomment=ഈ കലവറ ചരിത്രപരമായി ന form.name_reserved='%s' എന്ന കലവറയുടെ പേരു് മറ്റാവശ്യങ്ങള്‍ക്കായി നീക്കിവച്ചിരിക്കുന്നു. form.name_pattern_not_allowed=കലവറനാമത്തിൽ '%s' എന്ന ശ്രേണി അനുവദനീയമല്ല. -need_auth=ക്ലോൺ അംഗീകാരിയ്ക്കുക migrate_items=മൈഗ്രേഷൻ ഇനങ്ങൾ migrate_items_wiki=വിക്കി migrate_items_milestones=നാഴികക്കല്ലുകള്‍ diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index 21e0e0968e407..611fe52a335b0 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -89,6 +89,7 @@ loading=Laden… error404=De pagina die u probeert te bereiken bestaat niet of u bent niet gemachtigd om het te bekijken. + [error] occurred=Er is een fout opgetreden report_message=Als je zeker weet dat dit een Gitea bug is, zoek dan naar een issue op GitHub en open zo nodig een nieuw issue. @@ -687,7 +688,6 @@ mirror_prune_desc=Verwijder verouderde remote-tracking-referenties mirror_interval=Kopie-interval (geldige tijdseenheden zijn 'h', 'm' en 's'). 0 om automatische synchronisatie uit te schakelen. mirror_interval_invalid=Kloon-interval is niet geldig. mirror_address=Klonen van URL -mirror_address_desc=Voeg alle vereiste inloggegevens toe in de kloon autorisatiesectie. mirror_address_url_invalid=De opgegeven url is ongeldig. U dient alle componenten van de url correct te escapen. mirror_address_protocol_invalid=De opgegeven url is ongeldig. Alleen http(s):// of git:// locaties kunnen worden gemirrord. mirror_last_synced=Laatst gesynchroniseerd @@ -735,7 +735,6 @@ archive.pull.nocomment=Deze repo is gearchiveerd. U kunt niet reageren op pull r form.name_reserved=Repositorienaam '%s' is gereserveerd. form.name_pattern_not_allowed=Het patroon '%s' is niet toegestaan in de naam van een repository. -need_auth=Authenticatie benodigd om te klonen migrate_options=Migratie opties migrate_service=Migratie Service migrate_options_mirror_helper=Deze repository zal een kopie zijn diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini index 60849cd7cbbec..718b79da0f67c 100644 --- a/options/locale/locale_pl-PL.ini +++ b/options/locale/locale_pl-PL.ini @@ -87,6 +87,7 @@ loading=Ładowanie… error404=Strona, do której próbujesz dotrzeć nie istnieje lub nie jesteś autoryzowany aby go zobaczyć. + [error] occurred=Wystąpił błąd report_message=Jeśli jesteś pewien, że jest to błąd w Gitea, poszukaj problemu na GitHub i w razie potrzeby otwórz nowe zgłoszenie. @@ -663,7 +664,6 @@ mirror_prune_desc=Usuń przestarzałe odwołania do zdalnych śledzeń mirror_interval=Przedział czasowy dla tworzenia kopii lustrzanej (prawidłowe jednostki czasu to 'h' (godziny), 'm', 's'). 0, aby wyłączyć automatyczną synchronizację. mirror_interval_invalid=Interwał lustrzanej kopii jest niepoprawny. mirror_address=Sklonuj z adresu URL -mirror_address_desc=Wpisz wymagane dane uwierzytelnienia w sekcji Autoryzacja klonowania. mirror_address_url_invalid=Podany adres URL jest niewłaściwy. Musisz poprawnie escape'ować wszystkie jego elementy. mirror_address_protocol_invalid=Podany adres URL jest niewłaściwy. Tylko z http(s):// lub git:// można utworzyć kopie lustrzane. mirror_last_synced=Ostatnio zsynchronizowano @@ -702,7 +702,6 @@ archive.pull.nocomment=To repozytorium jest zarchiwizowane. Nie możesz komentow form.name_reserved=Nazwa repozytorium „%s” jest zarezerwowana. form.name_pattern_not_allowed=Wzór "%s" nie jest dozwolony w nazwie repozytorium. -need_auth=Autoryzacja klonowania migrate_items=Składniki migracji migrate_items_wiki=Wiki migrate_items_milestones=Kamienie milowe diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index 0712107781b2d..fc53fabc0c581 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -93,6 +93,7 @@ step2=Passo 2: error404=A página que você está tentando acessar não existe ou você não está autorizado a visualizá-la. + [error] occurred=Ocorreu um erro report_message=Se você tem certeza de que se trata de um bug do Gitea, por favor, procure a issue no GitHub e abra novas issues se necessário. @@ -717,7 +718,6 @@ mirror_prune_desc=Remover referências obsoletas de controle remoto mirror_interval=Intervalo de espelhamento (as unidades de tempo válidas são 'h', 'm', 's'). 0 para desativar a sincronização automática. mirror_interval_invalid=O intervalo do espelhamento não é válido. mirror_address=Clonar de URL -mirror_address_desc=Coloque qualquer credencial necessária na seção de Autorização de Clone. mirror_address_url_invalid=A url fornecida é inválida. Você deve escapar todos os componentes da url corretamente. mirror_address_protocol_invalid=A url fornecida é inválida. Apenas http(s):// ou git:// podem ser espelhados. mirror_last_synced=Última sincronização @@ -771,7 +771,6 @@ form.reach_limit_of_creation_n=Você já atingiu o limite de %d repositórios. form.name_reserved=O nome de repositório '%s' está reservado e não pode ser usado. form.name_pattern_not_allowed=O padrão de '%s' não é permitido em um nome de repositório. -need_auth=Autorização de clone migrate_options=Opções de Migração migrate_service=Serviço de Migração migrate_options_mirror_helper=Este repositório será um espelho diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index c3dcabf50f957..09dc4a0a9de52 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -93,6 +93,7 @@ step2=Passo 2: error404=A página que pretende aceder não existe ou não tem autorização para a ver. + [error] occurred=Ocorreu um erro report_message=Se tiver certeza de que se trata de um erro do Gitea, por favor, procure a questão no GitHub e abra uma nova questão, se necessário. @@ -720,13 +721,16 @@ mirror_prune_desc=Remover referências obsoletas de seguimento remoto mirror_interval=Intervalo de espelhamento (as unidade de tempo válidas são 'h', 'm' e 's'). O valor zero desabilita a sincronização automática. mirror_interval_invalid=O intervalo do espelhamento não é válido. mirror_address=Clonar a partir do URL -mirror_address_desc=Coloque, na secção de Autorização de Clonagem, as credenciais que, eventualmente, sejam necessárias. +mirror_address_desc=Coloque, na secção de Autorização, as credenciais que, eventualmente, sejam necessárias. mirror_address_url_invalid=O URL fornecido é inválido. Tem que codificar adequadamente todos os componentes do URL. mirror_address_protocol_invalid=O URL fornecido é inválido. Só se pode espelhar a partir de endereços http(s):// ou git://. mirror_lfs=Armazenamento de Ficheiros Grandes (LFS) mirror_lfs_desc=Habilitar o espelhamento de dados LFS. mirror_lfs_endpoint_desc=A sincronização irá tentar usar o URL de clonagem para determinar o servidor LFS. Também pode especificar um destino personalizado se os dados do repositório LFS forem armazenados noutro lugar. mirror_last_synced=Última sincronização +mirror_password_placeholder=(inalterada) +mirror_password_blank_placeholder=(não definida) +mirror_password_help=Altere o nome de utilizador para eliminar uma senha armazenada. watchers=Vigilantes stargazers=Fãs forks=Derivações @@ -779,7 +783,7 @@ form.reach_limit_of_creation_n=Já atingiu o seu limite de %d repositórios. form.name_reserved=O nome de repositório '%s' está reservado. form.name_pattern_not_allowed=O padrão '%s' não é permitido no nome de um repositório. -need_auth=Autorização de clonagem +need_auth=Autorização migrate_options=Opções de migração migrate_service=Serviço de migração migrate_options_mirror_helper=Este repositório irá ser um espelho @@ -809,13 +813,13 @@ migrate.migrate_items_options=É necessário um código de acesso para migrar it migrated_from=Migrado de %[2]s migrated_from_fake=Migrado de %[1]s migrate.migrate=Migrar de %s -migrate.migrating=Migrando de %s... +migrate.migrating=Migrando a partir de %s ... migrate.migrating_failed=A migração de %s falhou. -migrate.github.description=Migrando dados do Github.com ou do Github Enterprise. -migrate.git.description=Migrando ou espelhando dados git a partir de serviços Git -migrate.gitlab.description=Migrando dados do GitLab.com ou de um servidor GitLab auto-hospedado. -migrate.gitea.description=Migrando dados do Gitea.com ou de um servidor Gitea auto-hospedado. -migrate.gogs.description=Migrando dados de notabug.com ou de outro servidor Gogs auto-hospedado. +migrate.github.description=Migrar dados do Github.com ou do Github Enterprise. +migrate.git.description=Migrar ou espelhar dados git a partir de serviços Git +migrate.gitlab.description=Migrar dados do GitLab.com ou de um servidor GitLab auto-hospedado. +migrate.gitea.description=Migrar dados do Gitea.com ou de um servidor Gitea auto-hospedado. +migrate.gogs.description=Migrar dados de notabug.com ou de outro servidor Gogs auto-hospedado. mirror_from=espelho de forked_from=derivado de @@ -1530,6 +1534,13 @@ settings.hooks=Automatismos web settings.githooks=Automatismos do Git settings.basic_settings=Configurações básicas settings.mirror_settings=Configurações do espelhamento +settings.mirror_settings.docs=Configure o seu repositório para puxar e/ou enviar automaticamente as modificações de/para outro repositório. Ramos, etiquetas e cometimentos serão sincronizados automaticamente. Como é que eu faço um espelho de outro repositório? +settings.mirror_settings.mirrored_repository=Repositório espelhado +settings.mirror_settings.direction=Sentido +settings.mirror_settings.last_update=Última modificação +settings.mirror_settings.push_mirror.none=Não foram configurados quaisquer espelhos de envio +settings.mirror_settings.push_mirror.remote_url=URL do repositório remoto Git +settings.mirror_settings.push_mirror.add=Adicionar espelho de envio settings.sync_mirror=Sincronizar agora settings.mirror_sync_in_progress=A sincronização do espelho está em andamento. Volte a verificar daqui a um minuto. settings.email_notifications.enable=Habilitar notificações por email @@ -1538,6 +1549,7 @@ settings.email_notifications.disable=Desabilitar notificações por email settings.email_notifications.submit=Definir preferência do email settings.site=Sítio web settings.update_settings=Modificar configurações +settings.branches.update_default_branch=Modificar o ramo padrão settings.advanced_settings=Configurações avançadas settings.wiki_desc=Habilitar wiki do repositório settings.use_internal_wiki=Usar o wiki nativo @@ -1962,6 +1974,10 @@ branch.restore=Restaurar ramo '%s' branch.download=Descarregar o ramo '%s' branch.included_desc=Este ramo faz parte do ramo padrão branch.included=Incluído +branch.create_new_branch=Criar ramo a partir do ramo: +branch.confirm_create_branch=Criar ramo +branch.new_branch=Criar um novo ramo +branch.new_branch_from=Criar um novo ramo a partir do ramo '%s' tag.create_tag=Criar etiqueta %s tag.create_success=A etiqueta '%s' foi criada. diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index 6634b06810971..a7e1dadbccd6d 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -93,6 +93,7 @@ step2=Шаг 2: error404=Страница, которую вы пытаетесь открыть, либо не существует, либо вы не авторизованы для ее просмотра. + [error] occurred=Произошла ошибка report_message=Если вы уверены, что это ошибка Gitea, пожалуйста, проверьте наличие существующей проблемы на GitHub и откройте новую при необходимости. @@ -717,7 +718,6 @@ mirror_prune_desc=Удаление устаревших отслеживаемы mirror_interval=Интервал зеркалирования (допустимые единицы измерения 'h', 'm', 's'). Значение 0 отключает синхронизацию. mirror_interval_invalid=Недопустимый интервал зеркалирования. mirror_address=Клонировать по URL -mirror_address_desc=Поместите все необходимые учётные данные в раздел Авторизация клона. mirror_address_url_invalid=Указанный url неверный. Вы должны правильно экранировать все компоненты url. mirror_address_protocol_invalid=Указанный url неверный. Только http(s):// или git:// местоположения могут быть зеркалированы. mirror_last_synced=Последняя синхронизация @@ -773,7 +773,6 @@ form.reach_limit_of_creation_n=Вы уже достигли ваш предел form.name_reserved=Название репозитория '%s' зарезервировано. form.name_pattern_not_allowed=Шаблон имени репозитория '%s' не допускается. -need_auth=Требуется авторизация migrate_options=Параметры миграции migrate_service=Сервис миграции migrate_options_mirror_helper=Этот репозиторий будет зеркалом @@ -1996,7 +1995,7 @@ settings.visibility.public=Публичный settings.visibility.limited=Ограничено (Видно только для авторизованных пользователей) settings.visibility.limited_shortname=Ограничить settings.visibility.private=Частный (Видимый только для участников организации) -settings.visibility.private_shortname=Приватизировать +settings.visibility.private_shortname=Приватный settings.update_settings=Обновить настройки settings.update_setting_success=Настройки организации обновлены. diff --git a/options/locale/locale_sr-SP.ini b/options/locale/locale_sr-SP.ini index 8b11a0b3bfb95..06df78a4f5c9f 100644 --- a/options/locale/locale_sr-SP.ini +++ b/options/locale/locale_sr-SP.ini @@ -37,6 +37,7 @@ cancel=Откажи + [error] [startpage] diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini index 6d05498f5dad8..c340c55a4ca1e 100644 --- a/options/locale/locale_sv-SE.ini +++ b/options/locale/locale_sv-SE.ini @@ -89,6 +89,7 @@ loading=Laddar… error404=Sidan du försöker nå finns inte eller så har du inte behörighet att se den. + [error] occurred=Ett fel har inträffat report_message=Om du är säker på att detta är en Gitea bugg, vänligen sök efter ärende på GitHub och öppna nytt ärende om det behövs. @@ -673,7 +674,6 @@ mirror_prune_desc=Ta bort förlegade fjärrföljande referenser mirror_interval=Intervall för spegling (giltiga enheter är 'h', 'm', 's'). 0 stänger av automatisk synkronisering. mirror_interval_invalid=Speglingsintervallen är inte giltig. mirror_address=Klona Från URL -mirror_address_desc=Fyll i alla nödvändiga uppgifter i avsnittet Klona Auktorisering. mirror_address_url_invalid=Den angivna webbadressen är ogiltig. Du måste "escapa" alla delar av webbadressen korrekt. mirror_address_protocol_invalid=Den angivna webbadressen är ogiltig. Endast http(s):// eller git:// platser går att spegla från. mirror_last_synced=Senaste Synkronisering @@ -716,7 +716,6 @@ archive.pull.nocomment=Den här utvecklingskatalogen är arkiverad. Du kan inte form.name_reserved=Utvecklingskatalogsnamnet '%s' är reserverat. form.name_pattern_not_allowed=Mönstret '%s' är otillåtet i ett utvecklingskatalogsnamn. -need_auth=Klona Auktorisering migrate_options=Migrationsalternativ migrate_service=Migreringstjänst migrate_options_mirror_helper=Denna utvecklingskatalog kommer att vara en spegel diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index cf5274dc2d61c..90bce5f7b0e3b 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -91,8 +91,11 @@ loading=Yükleniyor… step1=1. Adım: step2=2. Adım: +error=Hata error404=Ulaşmaya çalıştığınız sayfa mevcut değil veya görüntüleme yetkiniz yok. +never=Asla + [error] occurred=Bir hata oluştu report_message=Bunun bir Gitea hatası olduğundan eminseniz, lütfen GitHub 'da sorunu arayın ve gerekirse yeni bir sorun açın. @@ -723,7 +726,7 @@ mirror_prune_desc=Kullanılmayan uzak depoları izleyen referansları kaldır mirror_interval=Yansı Aralığı (geçerli zaman birimleri 'h', 'm', 's'). 0 otomatik senkronizasyonu devre dışı bırakmak için. mirror_interval_invalid=Yansı süre aralığı geçerli değil. mirror_address=URL'den Klonla -mirror_address_desc=Gerekli kimlikleri Yetkilendirmeyi Klonla bölümüne girin. +mirror_address_desc=Yetkilendirme bölümüne gerekli tüm kimlik bilgilerini girin. mirror_address_url_invalid=Sağlanan Url geçersiz. Url'nin tüm bileşenlerinden doğru olarak kaçmalısınız. mirror_address_protocol_invalid=Sağlanan url geçersiz. Yalnızca http(s):// veya git:// konumları yansıtılabilir. mirror_lfs=Büyük Dosya Depolama (LFS) @@ -731,6 +734,9 @@ mirror_lfs_desc=LFS verisinin yansılamasını etkinleştir. mirror_lfs_endpoint=LFS Uç Noktası mirror_lfs_endpoint_desc=Senkronizasyon, LFS sunucusunu belirlemek için klonlama url'sini kullanmaya çalışacak. Eğer LFS veri deposu başka yerdeyse özel bir uç nokta da belirtebilirsiniz. mirror_last_synced=Son Senkronize Edilen +mirror_password_placeholder=(Değiştirilmedi) +mirror_password_blank_placeholder=(Ayarı kaldır) +mirror_password_help=Saklanan bir parolayı silmek için kullanıcı adını değiştirin. watchers=İzleyenler stargazers=Yıldızlayanlar forks=Çatallamalar @@ -783,7 +789,7 @@ form.reach_limit_of_creation_n=Zaten %d depo limitinize ulaştınız. form.name_reserved=Depo ismi '%s' rezerve edildi. form.name_pattern_not_allowed='%s' deseni, depo adı için geçerli değildir. -need_auth=Yetkilendirmeyi Klonla +need_auth=Yetkilendirme migrate_options=Göç Seçenekleri migrate_service=Göç Hizmeti migrate_options_mirror_helper=Bu depo bir yansı olacaktır @@ -1315,6 +1321,10 @@ pulls.manually_merged_as=Değişiklik isteği başlığı %s ile başlatın` +pulls.cannot_merge_work_in_progress=Bu değişiklik isteği, devam eden bir çalışma olarak işaretlendi. +pulls.still_in_progress=Hala devam ediyor mu? +pulls.add_prefix=%s ön ekini ekle +pulls.remove_prefix=%s ön ekini kaldır pulls.data_broken=Bu değişiklik isteği, çatallama bilgilerinin eksik olması nedeniyle bozuldu. pulls.files_conflicted=Bu değişiklik isteğinde, hedef dalla çakışan değişiklikler var. pulls.is_checking=Birleştirme çakışması denetimi devam ediyor. Birkaç dakika sonra tekrar deneyin. @@ -1540,6 +1550,15 @@ settings.hooks=Web İstemcileri settings.githooks=Git İstekleri settings.basic_settings=Temel Ayarlar settings.mirror_settings=Yansıma Ayarları +settings.mirror_settings.docs=Projenizi, değişiklikleri başka bir depoya/depodan otomatik olarak gönderecek ve/veya çekecek şekilde ayarlayın. Dallar, etiketler ve işlemeler otomatik olarak senkronize edilecektir. Depoları nasıl yansıtrım? +settings.mirror_settings.mirrored_repository=Yansıtılmış depo +settings.mirror_settings.direction=Yön +settings.mirror_settings.direction.pull=Çek +settings.mirror_settings.direction.push=Gönder +settings.mirror_settings.last_update=Son güncelleme +settings.mirror_settings.push_mirror.none=Yapılandırılmış yansı gönderimi yok +settings.mirror_settings.push_mirror.remote_url=Git Uzak Depo URL'si +settings.mirror_settings.push_mirror.add=Yansı Gönderimi Ekle settings.sync_mirror=Şimdi Eşitle settings.mirror_sync_in_progress=Yansı senkronizasyonu devam ediyor. Bir dakika sonra tekrar kontrol edin. settings.email_notifications.enable=E-posta Bildirimlerini Etkinleştir @@ -1605,6 +1624,7 @@ settings.transfer_form_title=Onaylamak için depo adını girin: settings.transfer_in_progress=Şu anda devam etmekte olan bir aktarım mevcut. Eğer bu depoyu başka bir kullanıcıya aktarmak istiyorsanız mevcut aktarımı iptal edin. settings.transfer_notices_1=- Bireysel bir kullanıcıya aktarırsanız depoya erişiminizi kaybedersiniz. settings.transfer_notices_2=- Sahip (-yardımcı) olduğunuz bir organizasyona devrederseniz, depoya erişmeye devam edersiniz. +settings.transfer_notices_3=- Depo özelse ve bireysel bir kullanıcıya aktarılmışsa, bu eylem kullanıcının en azından okuma iznine sahip olmasını sağlar (ve gerekirse izinleri değiştirir). settings.transfer_owner=Yeni Sahip settings.transfer_perform=Aktarımı Gerçekleştir settings.transfer_started=Bu depo aktarılmak üzere işaretlendi ve "%s" tarafından onay bekliyor diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index dabf82cfd5f1a..3bd41ca372a7d 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -87,6 +87,7 @@ loading=Завантаження… error404=Сторінка, до якої ви намагаєтеся звернутися або до , не існує або Ви не маєте права на її перегляд. + [error] occurred=Сталася помилка report_message=Якщо ви впевнені, що це помилка Gitea, будь ласка, спробуйте відшукати відповідну проблему на GitHub та за відсутності створіть нову. @@ -663,7 +664,6 @@ mirror_prune_desc=Видалення застарілих посилань як mirror_interval=Інтервал дзеркалювання (допустимі значення 'h', 'm', 's'). 0 - щоб вимкнути автоматичну синхронізацію. mirror_interval_invalid=Інтервал дзеркалювання є неприпустимим. mirror_address=Клонування з URL-адреси -mirror_address_desc=Покласти будь-які необхідні облікові дані у розділі клонування авторизації. mirror_address_url_invalid=Надана URL-адреса є неприпустимою. Ви повинні екранувати всі компоненти URL-адреси правильно. mirror_address_protocol_invalid=Надана URL-адреса є неприпустимою. Тільки http(s):// або git:// можливо використовувати при дзеркальні. mirror_last_synced=Остання синхронізація @@ -702,7 +702,6 @@ archive.pull.nocomment=Це архівний репозитарій. Ви не form.name_reserved=Назву репозиторію '%s' зарезервовано. form.name_pattern_not_allowed=Шаблон '%s' не дозволено в назві репозиторія. -need_auth=Клонувати з авторизацією migrate_items=Деталі міграції migrate_items_wiki=Вікі migrate_items_milestones=Етапи diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index a3274f465cf68..1eba2d89a4095 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -91,8 +91,11 @@ loading=正在加载... step1=第一步: step2=第二步: +error=错误 error404=您正尝试访问的页面 不存在您尚未被授权 查看该页面。 +never=从不 + [error] occurred=发生错误 report_message=如果您确定这是一个 Gitea bug,请在 GitHub 上搜索问题,并在必要时打开新问题。 @@ -723,7 +726,7 @@ mirror_prune_desc=删除过时的远程跟踪引用 mirror_interval=镜像间隔 (有效时间单位为 "h"、"m"、"s")。0将禁用自动同步。 mirror_interval_invalid=镜像间隔无效。 mirror_address=从URL克隆 -mirror_address_desc=在 Clone 认证部分里输入必要的信息。 +mirror_address_desc=在授权框中输入必要的凭据。 mirror_address_url_invalid=URL无效。请检查您所输入的URL是否正确。 mirror_address_protocol_invalid=提供的 url 无效。只能从 http(s):// 或 git:// 位置进行镜像。 mirror_lfs=大文件存储 (LFS) @@ -731,6 +734,9 @@ mirror_lfs_desc=镜像 LFS 数据。 mirror_lfs_endpoint=LFS 网址 mirror_lfs_endpoint_desc=同步将尝试使用克隆网址来 确定 LFS 服务器。如果仓库 LFS 数据存储在其他位置,你还可以指定自定义网址。 mirror_last_synced=上次同步 +mirror_password_placeholder=(未更改) +mirror_password_blank_placeholder=(未设置) +mirror_password_help=更改用户名以删除已储存的密码。 watchers=关注者 stargazers=称赞者 forks=派生仓库 @@ -783,7 +789,7 @@ form.reach_limit_of_creation_n=你已经达到了 %d 个仓库的上限。 form.name_reserved=仓库名称 '%s' 是被保留的。 form.name_pattern_not_allowed=仓库名称中不允许使用模式 "%s"。 -need_auth=需要授权验证 +need_auth=授权 migrate_options=迁移选项 migrate_service=迁移服务 migrate_options_mirror_helper=该仓库将是一个 镜像 @@ -817,11 +823,19 @@ migrated_from_fake=从 %[1]s 迁移成功 migrate.migrate=从 %s 迁移 migrate.migrating=正在从 %s 迁移... migrate.migrating_failed=从 %s 迁移失败。 +migrate.migrating_failed.error=错误:%s migrate.github.description=从 Github.com 或者 Github Enterprise 迁移数据 migrate.git.description=从 Git 迁移数据 migrate.gitlab.description=从 GitLab.com 或者 自部署 GitLab 迁移数据 migrate.gitea.description=从 Gitea.com 或 自托管 Gitea 服务器迁移数据。 migrate.gogs.description=正从 notabug.org 或其他自托管 Gogs 服务器迁移数据。 +migrate.migrating_git=迁移Git数据 +migrate.migrating_topics=迁移主题 +migrate.migrating_milestones=迁移里程碑 +migrate.migrating_labels=迁移标签 +migrate.migrating_releases=迁移发布 +migrate.migrating_issues=迁移工单 +migrate.migrating_pulls=迁移合并请求 mirror_from=镜像自地址 forked_from=派生自 @@ -1052,8 +1066,8 @@ issues.label_templates.use=使用标签集 issues.label_templates.fail_to_load_file=加载标签模板文件 '%s' 时发生错误:%v issues.add_label=于 %[2]s 添加了标签 %[1]s issues.add_labels=于 %s 添加 %s 标签 -issues.remove_label=已删除 %s 标签 %s -issues.remove_labels=已删除 %s 标签 %s +issues.remove_label=于 %[2]s 删除了标签 %[1]s +issues.remove_labels=于 %[2]s 删除了标签 %[1]s issues.add_remove_labels=于 %[3]s 添加了标签 %[1]s ,删除了标签 %[2]s issues.add_milestone_at=`于 %[2]s 添加了里程碑 %[1]s` issues.add_project_at=`将此添加到 %s 项目 %s` @@ -1315,6 +1329,10 @@ pulls.manually_merged_as=合并请求已被手动合并为 标题以 %s 开头以免合并请求意外合并。` +pulls.cannot_merge_work_in_progress=此合并请求被标记为正在进行的工作。 +pulls.still_in_progress=仍在进行中? +pulls.add_prefix=添加 %s 前缀 +pulls.remove_prefix=删除 %s 前缀 pulls.data_broken=此合并请求因为派生仓库信息缺失而中断。 pulls.files_conflicted=此合并请求有变更与目标分支冲突。 pulls.is_checking=正在进行合并冲突检测,请稍后再试。 @@ -1540,6 +1558,15 @@ settings.hooks=Web 钩子 settings.githooks=管理 Git 钩子 settings.basic_settings=基本设置 settings.mirror_settings=镜像设置 +settings.mirror_settings.docs=将你的项目设置成自动从其它存储库推送或拉取变更。分支、标签以及提交将会自动同步。如何镜像存储库? +settings.mirror_settings.mirrored_repository=镜像库 +settings.mirror_settings.direction=方向 +settings.mirror_settings.direction.pull=拉取 +settings.mirror_settings.direction.push=推送 +settings.mirror_settings.last_update=最后更新 +settings.mirror_settings.push_mirror.none=未配置推送镜像 +settings.mirror_settings.push_mirror.remote_url=Git 远程存储库链接 +settings.mirror_settings.push_mirror.add=添加推送镜像 settings.sync_mirror=同步 settings.mirror_sync_in_progress=镜像同步正在进行中,请稍后后再试。 settings.email_notifications.enable=启用邮件通知 @@ -1548,6 +1575,7 @@ settings.email_notifications.disable=停用邮件通知 settings.email_notifications.submit=邮件通知设置 settings.site=网站 settings.update_settings=更新仓库设置 +settings.branches.update_default_branch=更新默认分支 settings.advanced_settings=高级设置 settings.wiki_desc=启用仓库百科 settings.use_internal_wiki=使用内置百科 @@ -1604,6 +1632,7 @@ settings.transfer_form_title=输入仓库名称以做确认: settings.transfer_in_progress=当前正在进行转让。 如果你想将此代码库转让给另一个用户,请取消它。 settings.transfer_notices_1=-如果将其传输给单个用户, 您将失去对存储库的访问权限。 settings.transfer_notices_2=-如果将其转移到您 (共同) 拥有的组织,您可以继续访问该仓库。 +settings.transfer_notices_3=- 如果存储库是私有的并且被转移给某个用户,那么此操作可以确保该用户至少具有读权限(以及必要时的更改权限)。 settings.transfer_owner=新拥有者 settings.transfer_perform=执行转让 settings.transfer_started=该代码库已被标记为转让并等待来自 %s 的确认 @@ -1974,6 +2003,10 @@ branch.restore=恢复分支 '%s' branch.download=下载分支 '%s' branch.included_desc=此分支是默认分支的一部分 branch.included=已包含 +branch.create_new_branch=从下列分支创建分支: +branch.confirm_create_branch=创建分支 +branch.new_branch=创建新分支 +branch.new_branch_from=从 %s 创建新分支 tag.create_tag=创建标签 %s tag.create_success=标签 '%s' 已创建。 diff --git a/options/locale/locale_zh-HK.ini b/options/locale/locale_zh-HK.ini index 2b258d10ab647..88d77db30be20 100644 --- a/options/locale/locale_zh-HK.ini +++ b/options/locale/locale_zh-HK.ini @@ -44,6 +44,7 @@ cancel=取消 + [error] [startpage] diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini index 44adc0087df99..9cd7c237b0769 100644 --- a/options/locale/locale_zh-TW.ini +++ b/options/locale/locale_zh-TW.ini @@ -91,8 +91,11 @@ loading=載入中… step1=第一步: step2=第二步: +error=錯誤 error404=您正嘗試訪問的頁面 不存在您尚未被授權 查看該頁面。 +never=從來沒有 + [error] occurred=發生錯誤 report_message=如果你確定這是一個 Gitea 的 bug,請去 GitHub 搜尋相關的問題,如果有需要你也可以開一個新的問題 @@ -673,7 +676,7 @@ email_notifications.disable=關閉郵件通知 email_notifications.submit=套用郵件偏好設定 [repo] -new_repo_helper=儲存庫包含所以專案檔案,包含修訂歷史。已經存放於別處了嗎?遷移儲存庫。 +new_repo_helper=儲存庫包含所有專案檔案,包含修訂歷史。已經存放於別處了嗎?遷移儲存庫。 owner=擁有者 owner_helper=組織可能因為儲存庫數量上限而未列入此選單。 repo_name=儲存庫名稱 @@ -722,7 +725,7 @@ mirror_prune_desc=刪除過時的遠端追蹤參考 mirror_interval=鏡像間隔(有效時間單位為 'h'、'm'、's')。設為 0 以停用自動同步。 mirror_interval_invalid=鏡像週期無效 mirror_address=從 URL Clone -mirror_address_desc=在 Clone 授權資訊中填入必要的資料。 +mirror_address_desc=在授權資訊中填入必要的資料。 mirror_address_url_invalid=提供的網址無效。請檢查您輸入的網址是否正確。 mirror_address_protocol_invalid=提供的網址無效。只能從 http(s):// 或是 git:// 位址鏡像儲存庫。 mirror_lfs=Large File Storage (LFS) @@ -785,7 +788,7 @@ form.reach_limit_of_creation_n=您已經達到了您儲存庫的數量上限 (%d form.name_reserved=儲存庫名稱 '%s' 是預留的。 form.name_pattern_not_allowed=儲存庫名稱無法使用 "%s"。 -need_auth=Clone 授權資訊 +need_auth=授權 migrate_options=遷移選項 migrate_service=遷移服務 migrate_options_mirror_helper=將此儲存庫設定為鏡像儲存庫 @@ -819,11 +822,19 @@ migrated_from_fake=已從 %[1]s 遷移 migrate.migrate=從 %s 遷移 migrate.migrating=正在從 %s 遷移... migrate.migrating_failed=從 %s 遷移失敗 +migrate.migrating_failed.error=錯誤:%s migrate.github.description=從 Github.com 或 Github Enterprise 遷移資料。 migrate.git.description=從 Git 服務遷移或鏡像資料。 migrate.gitlab.description=從 GitLab.com 或自託管的 Gitlab 伺服器遷移資料。 migrate.gitea.description=從 Gitea.com 或自託管的 Gitea 伺服器遷移資料。 migrate.gogs.description=從 notabug.org 或自託管的 Gogs 伺服器遷移資料。 +migrate.migrating_git=正在遷移 Git 資料 +migrate.migrating_topics=正在遷移主題 +migrate.migrating_milestones=正在遷移里程碑 +migrate.migrating_labels=正在遷移標籤 +migrate.migrating_releases=正在遷移版本發佈 +migrate.migrating_issues=正在遷移問題 +migrate.migrating_pulls=正在遷移合併請求 mirror_from=鏡像自 forked_from=fork 自 @@ -1546,7 +1557,16 @@ settings.hooks=Webhook settings.githooks=Git Hook settings.basic_settings=基本設定 settings.mirror_settings=鏡像設定 -settings.sync_mirror=現在同步 +settings.mirror_settings.docs=設定您的專案自動向其它儲存庫推送、拉取變更,分支、標籤和提交會自動同步。如何鏡像儲存庫? +settings.mirror_settings.mirrored_repository=已鏡像的儲存庫 +settings.mirror_settings.direction=方向 +settings.mirror_settings.direction.pull=拉取 +settings.mirror_settings.direction.push=推送 +settings.mirror_settings.last_update=最近更新時間 +settings.mirror_settings.push_mirror.none=未設定推送鏡像 +settings.mirror_settings.push_mirror.remote_url=Git 遠端儲存庫 URL +settings.mirror_settings.push_mirror.add=新增推送鏡像 +settings.sync_mirror=立即同步 settings.mirror_sync_in_progress=鏡像同步正在進行中。 請稍後再回來看看。 settings.email_notifications.enable=啟用郵件通知 settings.email_notifications.onmention=只在被提到時傳送郵件通知 @@ -1611,6 +1631,7 @@ settings.transfer_form_title=輸入儲存庫名稱以確認: settings.transfer_in_progress=目前正在進行轉移。如果您想要將此儲存庫轉移給其它使用者,請取消它。 settings.transfer_notices_1=- 如果將此儲存庫轉移給個別使用者,您將會失去此儲存庫的存取權。 settings.transfer_notices_2=- 如果將此儲存庫轉移到您(共同)擁有的組織,您將能繼續保有此儲存庫的存取權。 +settings.transfer_notices_3=- 如果此儲存庫為私有儲存庫且將轉移給個別使用者,此動作確保該使用者至少擁有讀取權限(必要時將會修改權限)。 settings.transfer_owner=新擁有者 settings.transfer_perform=進行轉移 settings.transfer_started=此儲存庫已被標記為待轉移且正在等待「%s」的確認 diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index f3efd67bb3e10..34cf80e0721c3 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -83,6 +83,7 @@ import ( "code.gitea.io/gitea/routers/api/v1/settings" _ "code.gitea.io/gitea/routers/api/v1/swagger" // for swagger generation "code.gitea.io/gitea/routers/api/v1/user" + "code.gitea.io/gitea/services/auth" "code.gitea.io/gitea/services/forms" "gitea.com/go-chi/binding" @@ -573,6 +574,9 @@ func Routes() *web.Route { } m.Use(context.APIContexter()) + // Get user from session if logged in. + m.Use(context.APIAuth(auth.NewGroup(auth.Methods()...))) + m.Use(context.ToggleAPI(&context.ToggleOptions{ SignInRequired: setting.Service.RequireSignInView, })) @@ -742,6 +746,8 @@ func Routes() *web.Route { Put(reqAdmin(), bind(api.AddCollaboratorOption{}), repo.AddCollaborator). Delete(reqAdmin(), repo.DeleteCollaborator) }, reqToken()) + m.Get("/assignees", reqToken(), reqAnyRepoReader(), repo.GetAssignees) + m.Get("/reviewers", reqToken(), reqAnyRepoReader(), repo.GetReviewers) m.Group("/teams", func() { m.Get("", reqAnyRepoReader(), repo.ListTeams) m.Combo("/{team}").Get(reqAnyRepoReader(), repo.IsTeam). @@ -769,6 +775,7 @@ func Routes() *web.Route { }, reqToken(), reqAdmin()) m.Group("/tags", func() { m.Get("", repo.ListTags) + m.Post("", reqRepoWriter(models.UnitTypeCode), bind(api.CreateTagOption{}), repo.CreateTag) m.Delete("/{tag}", repo.DeleteTag) }, reqRepoReader(models.UnitTypeCode), context.ReferencesGitRepo(true)) m.Group("/keys", func() { diff --git a/routers/api/v1/notify/notifications.go b/routers/api/v1/notify/notifications.go index 71dd7d949267f..a5e095a3b5c29 100644 --- a/routers/api/v1/notify/notifications.go +++ b/routers/api/v1/notify/notifications.go @@ -6,10 +6,12 @@ package notify import ( "net/http" + "strings" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/routers/api/v1/utils" ) // NewAvailable check if unread notifications exist @@ -22,3 +24,44 @@ func NewAvailable(ctx *context.APIContext) { // "$ref": "#/responses/NotificationCount" ctx.JSON(http.StatusOK, api.NotificationCount{New: models.CountUnread(ctx.User)}) } + +func getFindNotificationOptions(ctx *context.APIContext) *models.FindNotificationOptions { + before, since, err := utils.GetQueryBeforeSince(ctx) + if err != nil { + ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err) + return nil + } + opts := &models.FindNotificationOptions{ + ListOptions: utils.GetListOptions(ctx), + UserID: ctx.User.ID, + UpdatedBeforeUnix: before, + UpdatedAfterUnix: since, + } + if !ctx.QueryBool("all") { + statuses := ctx.QueryStrings("status-types") + opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread", "pinned"}) + } + + subjectTypes := ctx.QueryStrings("subject-type") + if len(subjectTypes) != 0 { + opts.Source = subjectToSource(subjectTypes) + } + + return opts +} + +func subjectToSource(value []string) (result []models.NotificationSource) { + for _, v := range value { + switch strings.ToLower(v) { + case "issue": + result = append(result, models.NotificationSourceIssue) + case "pull": + result = append(result, models.NotificationSourcePullRequest) + case "commit": + result = append(result, models.NotificationSourceCommit) + case "repository": + result = append(result, models.NotificationSourceRepository) + } + } + return +} diff --git a/routers/api/v1/notify/repo.go b/routers/api/v1/notify/repo.go index 0a75fcd30a1df..af55d1d49c05b 100644 --- a/routers/api/v1/notify/repo.go +++ b/routers/api/v1/notify/repo.go @@ -13,7 +13,6 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/routers/api/v1/utils" ) func statusStringToNotificationStatus(status string) models.NotificationStatus { @@ -66,8 +65,7 @@ func ListRepoNotifications(ctx *context.APIContext) { // - name: all // in: query // description: If true, show notifications marked as read. Default value is false - // type: string - // required: false + // type: boolean // - name: status-types // in: query // description: "Show notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread & pinned" @@ -75,19 +73,24 @@ func ListRepoNotifications(ctx *context.APIContext) { // collectionFormat: multi // items: // type: string - // required: false + // - name: subject-type + // in: query + // description: "filter notifications by subject type" + // type: array + // collectionFormat: multi + // items: + // type: string + // enum: [issue,pull,commit,repository] // - name: since // in: query // description: Only show notifications updated after the given time. This is a timestamp in RFC 3339 format // type: string // format: date-time - // required: false // - name: before // in: query // description: Only show notifications updated before the given time. This is a timestamp in RFC 3339 format // type: string // format: date-time - // required: false // - name: page // in: query // description: page number of results to return (1-based) @@ -99,24 +102,12 @@ func ListRepoNotifications(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/NotificationThreadList" - - before, since, err := utils.GetQueryBeforeSince(ctx) - if err != nil { - ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err) + opts := getFindNotificationOptions(ctx) + if ctx.Written() { return } - opts := models.FindNotificationOptions{ - ListOptions: utils.GetListOptions(ctx), - UserID: ctx.User.ID, - RepoID: ctx.Repo.Repository.ID, - UpdatedBeforeUnix: before, - UpdatedAfterUnix: since, - } + opts.RepoID = ctx.Repo.Repository.ID - if !ctx.QueryBool("all") { - statuses := ctx.QueryStrings("status-types") - opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread", "pinned"}) - } nl, err := models.GetNotifications(opts) if err != nil { ctx.InternalServerError(err) @@ -192,7 +183,7 @@ func ReadRepoNotifications(ctx *context.APIContext) { } } - opts := models.FindNotificationOptions{ + opts := &models.FindNotificationOptions{ UserID: ctx.User.ID, RepoID: ctx.Repo.Repository.ID, UpdatedBeforeUnix: lastRead, diff --git a/routers/api/v1/notify/user.go b/routers/api/v1/notify/user.go index e739c6a38dd84..475a541bdc603 100644 --- a/routers/api/v1/notify/user.go +++ b/routers/api/v1/notify/user.go @@ -12,7 +12,6 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" - "code.gitea.io/gitea/routers/api/v1/utils" ) // ListNotifications list users's notification threads @@ -28,8 +27,7 @@ func ListNotifications(ctx *context.APIContext) { // - name: all // in: query // description: If true, show notifications marked as read. Default value is false - // type: string - // required: false + // type: boolean // - name: status-types // in: query // description: "Show notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread & pinned." @@ -37,19 +35,24 @@ func ListNotifications(ctx *context.APIContext) { // collectionFormat: multi // items: // type: string - // required: false + // - name: subject-type + // in: query + // description: "filter notifications by subject type" + // type: array + // collectionFormat: multi + // items: + // type: string + // enum: [issue,pull,commit,repository] // - name: since // in: query // description: Only show notifications updated after the given time. This is a timestamp in RFC 3339 format // type: string // format: date-time - // required: false // - name: before // in: query // description: Only show notifications updated before the given time. This is a timestamp in RFC 3339 format // type: string // format: date-time - // required: false // - name: page // in: query // description: page number of results to return (1-based) @@ -61,22 +64,11 @@ func ListNotifications(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/NotificationThreadList" - - before, since, err := utils.GetQueryBeforeSince(ctx) - if err != nil { - ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err) + opts := getFindNotificationOptions(ctx) + if ctx.Written() { return } - opts := models.FindNotificationOptions{ - ListOptions: utils.GetListOptions(ctx), - UserID: ctx.User.ID, - UpdatedBeforeUnix: before, - UpdatedAfterUnix: since, - } - if !ctx.QueryBool("all") { - statuses := ctx.QueryStrings("status-types") - opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread", "pinned"}) - } + nl, err := models.GetNotifications(opts) if err != nil { ctx.InternalServerError(err) @@ -141,7 +133,7 @@ func ReadNotifications(ctx *context.APIContext) { lastRead = tmpLastRead.Unix() } } - opts := models.FindNotificationOptions{ + opts := &models.FindNotificationOptions{ UserID: ctx.User.ID, UpdatedBeforeUnix: lastRead, } diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go index e0f36aa1e657d..f4a634f4d56c0 100644 --- a/routers/api/v1/org/org.go +++ b/routers/api/v1/org/org.go @@ -264,7 +264,13 @@ func Edit(ctx *context.APIContext) { if form.Visibility != "" { org.Visibility = api.VisibilityModes[form.Visibility] } - if err := models.UpdateUserCols(org, "full_name", "description", "website", "location", "visibility"); err != nil { + if form.RepoAdminChangeTeamAccess != nil { + org.RepoAdminChangeTeamAccess = *form.RepoAdminChangeTeamAccess + } + if err := models.UpdateUserCols(org, + "full_name", "description", "website", "location", + "visibility", "repo_admin_change_team_access", + ); err != nil { ctx.Error(http.StatusInternalServerError, "EditOrganization", err) return } diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go index 451fdcf516f04..85c1681dfec1b 100644 --- a/routers/api/v1/repo/branch.go +++ b/routers/api/v1/repo/branch.go @@ -6,6 +6,7 @@ package repo import ( + "errors" "fmt" "net/http" @@ -13,7 +14,6 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/git" - "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" @@ -117,62 +117,20 @@ func DeleteBranch(ctx *context.APIContext) { branchName := ctx.Params("*") - if ctx.Repo.Repository.DefaultBranch == branchName { - ctx.Error(http.StatusForbidden, "DefaultBranch", fmt.Errorf("can not delete default branch")) - return - } - - isProtected, err := ctx.Repo.Repository.IsProtectedBranch(branchName, ctx.User) - if err != nil { - ctx.InternalServerError(err) - return - } - if isProtected { - ctx.Error(http.StatusForbidden, "IsProtectedBranch", fmt.Errorf("branch protected")) - return - } - - branch, err := repo_module.GetBranch(ctx.Repo.Repository, branchName) - if err != nil { - if git.IsErrBranchNotExist(err) { + if err := repo_service.DeleteBranch(ctx.User, ctx.Repo.Repository, ctx.Repo.GitRepo, branchName); err != nil { + switch { + case git.IsErrBranchNotExist(err): ctx.NotFound(err) - } else { - ctx.Error(http.StatusInternalServerError, "GetBranch", err) + case errors.Is(err, repo_service.ErrBranchIsDefault): + ctx.Error(http.StatusForbidden, "DefaultBranch", fmt.Errorf("can not delete default branch")) + case errors.Is(err, repo_service.ErrBranchIsProtected): + ctx.Error(http.StatusForbidden, "IsProtectedBranch", fmt.Errorf("branch protected")) + default: + ctx.Error(http.StatusInternalServerError, "DeleteBranch", err) } return } - c, err := branch.GetCommit() - if err != nil { - ctx.Error(http.StatusInternalServerError, "GetCommit", err) - return - } - - if err := ctx.Repo.GitRepo.DeleteBranch(branchName, git.DeleteBranchOptions{ - Force: true, - }); err != nil { - ctx.Error(http.StatusInternalServerError, "DeleteBranch", err) - return - } - - // Don't return error below this - if err := repo_service.PushUpdate( - &repo_module.PushUpdateOptions{ - RefFullName: git.BranchPrefix + branchName, - OldCommitID: c.ID.String(), - NewCommitID: git.EmptySHA, - PusherID: ctx.User.ID, - PusherName: ctx.User.Name, - RepoUserName: ctx.Repo.Owner.Name, - RepoName: ctx.Repo.Repository.Name, - }); err != nil { - log.Error("Update: %v", err) - } - - if err := ctx.Repo.Repository.AddDeletedBranch(branchName, c.ID.String(), ctx.User.ID); err != nil { - log.Warn("AddDeletedBranch: %v", err) - } - ctx.Status(http.StatusNoContent) } diff --git a/routers/api/v1/repo/collaborators.go b/routers/api/v1/repo/collaborators.go index d0936019faddf..078af1f6ff8e9 100644 --- a/routers/api/v1/repo/collaborators.go +++ b/routers/api/v1/repo/collaborators.go @@ -221,3 +221,63 @@ func DeleteCollaborator(ctx *context.APIContext) { } ctx.Status(http.StatusNoContent) } + +// GetReviewers return all users that can be requested to review in this repo +func GetReviewers(ctx *context.APIContext) { + // swagger:operation GET /repos/{owner}/{repo}/reviewers repository repoGetReviewers + // --- + // summary: Return all users that can be requested to review in this repo + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // responses: + // "200": + // "$ref": "#/responses/UserList" + + reviewers, err := ctx.Repo.Repository.GetReviewers(ctx.User.ID, 0) + if err != nil { + ctx.Error(http.StatusInternalServerError, "ListCollaborators", err) + return + } + ctx.JSON(http.StatusOK, convert.ToUsers(ctx.User, reviewers)) +} + +// GetAssignees return all users that have write access and can be assigned to issues +func GetAssignees(ctx *context.APIContext) { + // swagger:operation GET /repos/{owner}/{repo}/assignees repository repoGetAssignees + // --- + // summary: Return all users that have write access and can be assigned to issues + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // responses: + // "200": + // "$ref": "#/responses/UserList" + + assignees, err := ctx.Repo.Repository.GetAssignees() + if err != nil { + ctx.Error(http.StatusInternalServerError, "ListCollaborators", err) + return + } + ctx.JSON(http.StatusOK, convert.ToUsers(ctx.User, assignees)) +} diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go index 37e02874b4ffd..39a60df33f01b 100644 --- a/routers/api/v1/repo/file.go +++ b/routers/api/v1/repo/file.go @@ -17,7 +17,7 @@ import ( "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" + "code.gitea.io/gitea/routers/common" ) // GetRawFile get a file by path on a repository @@ -83,7 +83,7 @@ func GetRawFile(ctx *context.APIContext) { } return } - if err = repo.ServeBlob(ctx.Context, blob); err != nil { + if err = common.ServeBlob(ctx.Context, blob); err != nil { ctx.Error(http.StatusInternalServerError, "ServeBlob", err) } } @@ -126,7 +126,7 @@ func GetArchive(ctx *context.APIContext) { ctx.Repo.GitRepo = gitRepo defer gitRepo.Close() - repo.Download(ctx.Context) + common.Download(ctx.Context) } // GetEditorconfig get editor config of a repository diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index 6b46dc0fef531..5a7d10b36f134 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -42,6 +42,10 @@ func SearchIssues(ctx *context.APIContext) { // in: query // description: comma separated list of labels. Fetch only issues that have any of this labels. Non existent labels are discarded // type: string + // - name: milestones + // in: query + // description: comma separated list of milestone names. Fetch only issues that have any of this milestones. Non existent are discarded + // type: string // - name: q // in: query // description: search string @@ -164,6 +168,12 @@ func SearchIssues(ctx *context.APIContext) { includedLabelNames = strings.Split(labels, ",") } + milestones := strings.TrimSpace(ctx.Query("milestones")) + var includedMilestones []string + if len(milestones) > 0 { + includedMilestones = strings.Split(milestones, ",") + } + // this api is also used in UI, // so the default limit is set to fit UI needs limit := ctx.QueryInt("limit") @@ -175,7 +185,7 @@ func SearchIssues(ctx *context.APIContext) { // Only fetch the issues if we either don't have a keyword or the search returned issues // This would otherwise return all issues if no issues were found by the search. - if len(keyword) == 0 || len(issueIDs) > 0 || len(includedLabelNames) > 0 { + if len(keyword) == 0 || len(issueIDs) > 0 || len(includedLabelNames) > 0 || len(includedMilestones) > 0 { issuesOpt := &models.IssuesOptions{ ListOptions: models.ListOptions{ Page: ctx.QueryInt("page"), @@ -185,6 +195,7 @@ func SearchIssues(ctx *context.APIContext) { IsClosed: isClosed, IssueIDs: issueIDs, IncludedLabelNames: includedLabelNames, + IncludeMilestones: includedMilestones, SortType: "priorityrepo", PriorityRepoID: ctx.QueryInt64("priority_repo_id"), IsPull: isPull, @@ -266,6 +277,30 @@ func ListIssues(ctx *context.APIContext) { // in: query // description: comma separated list of milestone names or ids. It uses names and fall back to ids. Fetch only issues that have any of this milestones. Non existent milestones are discarded // type: string + // - name: since + // in: query + // description: Only show notifications updated after the given time. This is a timestamp in RFC 3339 format + // type: string + // format: date-time + // required: false + // - name: before + // in: query + // description: Only show notifications updated before the given time. This is a timestamp in RFC 3339 format + // type: string + // format: date-time + // required: false + // - name: created_by + // in: query + // description: filter (issues / pulls) created to + // type: string + // - name: assigned_by + // in: query + // description: filter (issues / pulls) assigned to + // type: string + // - name: mentioned_by + // in: query + // description: filter (issues / pulls) mentioning to + // type: string // - name: page // in: query // description: page number of results to return (1-based) @@ -277,6 +312,11 @@ func ListIssues(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/IssueList" + before, since, err := utils.GetQueryBeforeSince(ctx) + if err != nil { + ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err) + return + } var isClosed util.OptionalBool switch ctx.Query("state") { @@ -297,7 +337,6 @@ func ListIssues(ctx *context.APIContext) { } var issueIDs []int64 var labelIDs []int64 - var err error if len(keyword) > 0 { issueIDs, err = issue_indexer.SearchIssuesByKeyword([]int64{ctx.Repo.Repository.ID}, keyword) if err != nil { @@ -356,17 +395,36 @@ func ListIssues(ctx *context.APIContext) { isPull = util.OptionalBoolNone } + // FIXME: we should be more efficient here + createdByID := getUserIDForFilter(ctx, "created_by") + if ctx.Written() { + return + } + assignedByID := getUserIDForFilter(ctx, "assigned_by") + if ctx.Written() { + return + } + mentionedByID := getUserIDForFilter(ctx, "mentioned_by") + if ctx.Written() { + return + } + // Only fetch the issues if we either don't have a keyword or the search returned issues // This would otherwise return all issues if no issues were found by the search. if len(keyword) == 0 || len(issueIDs) > 0 || len(labelIDs) > 0 { issuesOpt := &models.IssuesOptions{ - ListOptions: listOptions, - RepoIDs: []int64{ctx.Repo.Repository.ID}, - IsClosed: isClosed, - IssueIDs: issueIDs, - LabelIDs: labelIDs, - MilestoneIDs: mileIDs, - IsPull: isPull, + ListOptions: listOptions, + RepoIDs: []int64{ctx.Repo.Repository.ID}, + IsClosed: isClosed, + IssueIDs: issueIDs, + LabelIDs: labelIDs, + MilestoneIDs: mileIDs, + IsPull: isPull, + UpdatedBeforeUnix: before, + UpdatedAfterUnix: since, + PosterID: createdByID, + AssigneeID: assignedByID, + MentionedID: mentionedByID, } if issues, err = models.Issues(issuesOpt); err != nil { @@ -389,6 +447,26 @@ func ListIssues(ctx *context.APIContext) { ctx.JSON(http.StatusOK, convert.ToAPIIssueList(issues)) } +func getUserIDForFilter(ctx *context.APIContext, queryName string) int64 { + userName := ctx.Query(queryName) + if len(userName) == 0 { + return 0 + } + + user, err := models.GetUserByName(userName) + if models.IsErrUserNotExist(err) { + ctx.NotFound(err) + return 0 + } + + if err != nil { + ctx.InternalServerError(err) + return 0 + } + + return user.ID +} + // GetIssue get an issue of a repository func GetIssue(ctx *context.APIContext) { // swagger:operation GET /repos/{owner}/{repo}/issues/{index} issue issueGetIssue diff --git a/routers/api/v1/repo/migrate.go b/routers/api/v1/repo/migrate.go index edae358338fc9..de33a3645b75f 100644 --- a/routers/api/v1/repo/migrate.go +++ b/routers/api/v1/repo/migrate.go @@ -199,7 +199,7 @@ func Migrate(ctx *context.APIContext) { } }() - if _, err = migrations.MigrateRepository(graceful.GetManager().HammerContext(), ctx.User, repoOwner.Name, opts); err != nil { + if _, err = migrations.MigrateRepository(graceful.GetManager().HammerContext(), ctx.User, repoOwner.Name, opts, nil); err != nil { handleMigrateError(ctx, repoOwner, remoteAddr, err) return } @@ -231,7 +231,7 @@ func handleMigrateError(ctx *context.APIContext, repoOwner *models.User, remoteA case base.IsErrNotSupported(err): ctx.Error(http.StatusUnprocessableEntity, "", err) default: - err = util.URLSanitizedError(err, remoteAddr) + err = util.NewStringURLSanitizedError(err, remoteAddr, true) if strings.Contains(err.Error(), "Authentication failed") || strings.Contains(err.Error(), "Bad credentials") || strings.Contains(err.Error(), "could not read Username") { diff --git a/routers/api/v1/repo/pull_review.go b/routers/api/v1/repo/pull_review.go index 63179aa9907db..35414e0a80c53 100644 --- a/routers/api/v1/repo/pull_review.go +++ b/routers/api/v1/repo/pull_review.go @@ -359,7 +359,7 @@ func CreatePullReview(ctx *context.APIContext) { } // create review and associate all pending review comments - review, _, err := pull_service.SubmitReview(ctx.User, ctx.Repo.GitRepo, pr.Issue, reviewType, opts.Body, opts.CommitID) + review, _, err := pull_service.SubmitReview(ctx.User, ctx.Repo.GitRepo, pr.Issue, reviewType, opts.Body, opts.CommitID, nil) if err != nil { ctx.Error(http.StatusInternalServerError, "SubmitReview", err) return @@ -447,7 +447,7 @@ func SubmitPullReview(ctx *context.APIContext) { } // create review and associate all pending review comments - review, _, err = pull_service.SubmitReview(ctx.User, ctx.Repo.GitRepo, pr.Issue, reviewType, opts.Body, headCommitID) + review, _, err = pull_service.SubmitReview(ctx.User, ctx.Repo.GitRepo, pr.Issue, reviewType, opts.Body, headCommitID, nil) if err != nil { ctx.Error(http.StatusInternalServerError, "SubmitReview", err) return diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go index 327a2d790b6be..1b52de55ffffc 100644 --- a/routers/api/v1/repo/release.go +++ b/routers/api/v1/repo/release.go @@ -5,6 +5,7 @@ package repo import ( + "fmt" "net/http" "code.gitea.io/gitea/models" @@ -83,6 +84,14 @@ func ListReleases(ctx *context.APIContext) { // description: name of the repo // type: string // required: true + // - name: draft + // in: query + // description: filter (exclude / include) drafts, if you dont have repo write access none will show + // type: boolean + // - name: pre-release + // in: query + // description: filter (exclude / include) pre-releases + // type: boolean // - name: per_page // in: query // description: page size of results, deprecated - use limit @@ -100,15 +109,19 @@ func ListReleases(ctx *context.APIContext) { // "200": // "$ref": "#/responses/ReleaseList" listOptions := utils.GetListOptions(ctx) - if ctx.QueryInt("per_page") != 0 { + if listOptions.PageSize == 0 && ctx.QueryInt("per_page") != 0 { listOptions.PageSize = ctx.QueryInt("per_page") } - releases, err := models.GetReleasesByRepoID(ctx.Repo.Repository.ID, models.FindReleasesOptions{ + opts := models.FindReleasesOptions{ ListOptions: listOptions, IncludeDrafts: ctx.Repo.AccessMode >= models.AccessModeWrite, IncludeTags: false, - }) + IsDraft: ctx.QueryOptionalBool("draft"), + IsPreRelease: ctx.QueryOptionalBool("pre-release"), + } + + releases, err := models.GetReleasesByRepoID(ctx.Repo.Repository.ID, opts) if err != nil { ctx.Error(http.StatusInternalServerError, "GetReleasesByRepoID", err) return @@ -121,6 +134,16 @@ func ListReleases(ctx *context.APIContext) { } rels[i] = convert.ToRelease(release) } + + filteredCount, err := models.CountReleasesByRepoID(ctx.Repo.Repository.ID, opts) + if err != nil { + ctx.InternalServerError(err) + return + } + + ctx.SetLinkHeader(int(filteredCount), listOptions.PageSize) + ctx.Header().Set("X-Total-Count", fmt.Sprint(filteredCount)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link") ctx.JSON(http.StatusOK, rels) } diff --git a/routers/api/v1/repo/tag.go b/routers/api/v1/repo/tag.go index ec9b541bd41d3..51ba43ea89ba6 100644 --- a/routers/api/v1/repo/tag.go +++ b/routers/api/v1/repo/tag.go @@ -6,12 +6,14 @@ package repo import ( "errors" + "fmt" "net/http" "code.gitea.io/gitea/models" "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" ) @@ -160,3 +162,62 @@ func DeleteTag(ctx *context.APIContext) { ctx.Status(http.StatusNoContent) } + +// CreateTag create a new git tag in a repository +func CreateTag(ctx *context.APIContext) { + // swagger:operation POST /repos/{owner}/{repo}/tags repository repoCreateTag + // --- + // summary: Create a new git tag in a repository + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // - name: body + // in: body + // schema: + // "$ref": "#/definitions/CreateTagOption" + // responses: + // "200": + // "$ref": "#/responses/AnnotatedTag" + // "404": + // "$ref": "#/responses/notFound" + // "409": + // "$ref": "#/responses/conflict" + form := web.GetForm(ctx).(*api.CreateTagOption) + + // If target is not provided use default branch + if len(form.Target) == 0 { + form.Target = ctx.Repo.Repository.DefaultBranch + } + + commit, err := ctx.Repo.GitRepo.GetCommit(form.Target) + if err != nil { + ctx.Error(http.StatusNotFound, "target not found", fmt.Errorf("target not found: %v", err)) + return + } + + if err := releaseservice.CreateNewTag(ctx.User, ctx.Repo.Repository, commit.ID.String(), form.TagName, form.Message); err != nil { + if models.IsErrTagAlreadyExists(err) { + ctx.Error(http.StatusConflict, "tag exist", err) + return + } + ctx.InternalServerError(err) + return + } + + tag, err := ctx.Repo.GitRepo.GetTag(form.TagName) + if err != nil { + ctx.InternalServerError(err) + return + } + ctx.JSON(http.StatusCreated, convert.ToTag(ctx.Repo.Repository, tag)) +} diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go index dad025710dbad..11158fb86d0e8 100644 --- a/routers/api/v1/swagger/options.go +++ b/routers/api/v1/swagger/options.go @@ -158,4 +158,7 @@ type swaggerParameterBodies struct { // in:body PullReviewRequestOptions api.PullReviewRequestOptions + + // in:body + CreateTagOption api.CreateTagOption } diff --git a/routers/api/v1/user/user.go b/routers/api/v1/user/user.go index 6e811bf0f8a4d..4adae532fdfcf 100644 --- a/routers/api/v1/user/user.go +++ b/routers/api/v1/user/user.go @@ -13,7 +13,6 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" - api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -73,18 +72,13 @@ func Search(ctx *context.APIContext) { return } - results := make([]*api.User, len(users)) - for i := range users { - results[i] = convert.ToUser(users[i], ctx.User) - } - ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", maxResults)) ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link") ctx.JSON(http.StatusOK, map[string]interface{}{ "ok": true, - "data": results, + "data": convert.ToUsers(ctx.User, users), }) } diff --git a/routers/api/v1/utils/utils.go b/routers/api/v1/utils/utils.go index ad1a136db463a..10ab3ebd0cfb9 100644 --- a/routers/api/v1/utils/utils.go +++ b/routers/api/v1/utils/utils.go @@ -55,7 +55,7 @@ func parseTime(value string) (int64, error) { // prepareQueryArg unescape and trim a query arg func prepareQueryArg(ctx *context.APIContext, name string) (value string, err error) { value, err = url.PathUnescape(ctx.Query(name)) - value = strings.Trim(value, " ") + value = strings.TrimSpace(value) return } diff --git a/routers/common/db.go b/routers/common/db.go new file mode 100644 index 0000000000000..069a46f64fe4d --- /dev/null +++ b/routers/common/db.go @@ -0,0 +1,39 @@ +// 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 common + +import ( + "context" + "fmt" + "time" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/models/migrations" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" +) + +// InitDBEngine In case of problems connecting to DB, retry connection. Eg, PGSQL in Docker Container on Synology +func InitDBEngine(ctx context.Context) (err error) { + log.Info("Beginning ORM engine initialization.") + for i := 0; i < setting.Database.DBConnectRetries; i++ { + select { + case <-ctx.Done(): + return fmt.Errorf("Aborted due to shutdown:\nin retry ORM engine initialization") + default: + } + log.Info("ORM engine initialization attempt #%d/%d...", i+1, setting.Database.DBConnectRetries) + if err = models.NewEngine(ctx, migrations.Migrate); err == nil { + break + } else if i == setting.Database.DBConnectRetries-1 { + return err + } + log.Error("ORM engine initialization attempt #%d/%d failed. Error: %v", i+1, setting.Database.DBConnectRetries, err) + log.Info("Backing off for %d seconds", int64(setting.Database.DBConnectBackoff/time.Second)) + time.Sleep(setting.Database.DBConnectBackoff) + } + models.HasEngine = true + return nil +} diff --git a/routers/common/logger.go b/routers/common/logger.go new file mode 100644 index 0000000000000..bc1149543c94e --- /dev/null +++ b/routers/common/logger.go @@ -0,0 +1,33 @@ +// 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 common + +import ( + "net/http" + "time" + + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/log" +) + +// LoggerHandler is a handler that will log the routing to the default gitea log +func LoggerHandler(level log.Level) 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() + + _ = log.GetLogger("router").Log(0, level, "Started %s %s for %s", log.ColoredMethod(req.Method), req.URL.RequestURI(), req.RemoteAddr) + + next.ServeHTTP(w, req) + + var status int + if v, ok := w.(context.ResponseWriter); ok { + status = v.Status() + } + + _ = log.GetLogger("router").Log(0, level, "Completed %s %s %v %s in %v", log.ColoredMethod(req.Method), req.URL.RequestURI(), log.ColoredStatus(status), log.ColoredStatus(status, http.StatusText(status)), log.ColoredTime(time.Since(start))) + }) + } +} diff --git a/routers/common/middleware.go b/routers/common/middleware.go new file mode 100644 index 0000000000000..1d96522dd9d19 --- /dev/null +++ b/routers/common/middleware.go @@ -0,0 +1,76 @@ +// 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 common + +import ( + "fmt" + "net/http" + "strings" + + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + + "github.com/chi-middleware/proxy" + "github.com/go-chi/chi/middleware" +) + +// Middlewares returns common middlewares +func Middlewares() []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) + }) + }, + } + + if setting.ReverseProxyLimit > 0 { + opt := proxy.NewForwardedHeadersOptions(). + WithForwardLimit(setting.ReverseProxyLimit). + ClearTrustedProxies() + for _, n := range setting.ReverseProxyTrustedProxies { + if !strings.Contains(n, "/") { + opt.AddTrustedProxy(n) + } else { + opt.AddTrustedNetwork(n) + } + } + handlers = append(handlers, proxy.ForwardedHeaders(opt)) + } + + handlers = append(handlers, middleware.StripSlashes) + + if !setting.DisableRouterLog && setting.RouterLogLevel != log.NONE { + if log.GetLogger("router").GetLevel() <= setting.RouterLogLevel { + handlers = append(handlers, LoggerHandler(setting.RouterLogLevel)) + } + } + if setting.EnableAccessLog { + handlers = append(handlers, context.AccessLogger()) + } + + 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) + }) + }) + return handlers +} diff --git a/routers/common/repo.go b/routers/common/repo.go new file mode 100644 index 0000000000000..c61b5ec57f525 --- /dev/null +++ b/routers/common/repo.go @@ -0,0 +1,127 @@ +// 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 common + +import ( + "fmt" + "io" + "net/http" + "path" + "path/filepath" + "strings" + + "code.gitea.io/gitea/modules/charset" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/httpcache" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/typesniffer" + "code.gitea.io/gitea/services/archiver" +) + +// ServeBlob download a git.Blob +func ServeBlob(ctx *context.Context, blob *git.Blob) error { + if httpcache.HandleGenericETagCache(ctx.Req, ctx.Resp, `"`+blob.ID.String()+`"`) { + return nil + } + + dataRc, err := blob.DataAsync() + if err != nil { + return err + } + defer func() { + if err = dataRc.Close(); err != nil { + log.Error("ServeBlob: Close: %v", err) + } + }() + + return ServeData(ctx, ctx.Repo.TreePath, blob.Size(), dataRc) +} + +// Download an archive of a repository +func Download(ctx *context.Context) { + uri := ctx.Params("*") + aReq := archiver.DeriveRequestFrom(ctx, uri) + + if aReq == nil { + ctx.Error(http.StatusNotFound) + return + } + + downloadName := ctx.Repo.Repository.Name + "-" + aReq.GetArchiveName() + complete := aReq.IsComplete() + if !complete { + aReq = archiver.ArchiveRepository(aReq) + complete = aReq.WaitForCompletion(ctx) + } + + if complete { + ctx.ServeFile(aReq.GetArchivePath(), downloadName) + } else { + ctx.Error(http.StatusNotFound) + } +} + +// ServeData download file from io.Reader +func ServeData(ctx *context.Context, name string, size int64, reader io.Reader) error { + buf := make([]byte, 1024) + n, err := reader.Read(buf) + if err != nil && err != io.EOF { + return err + } + if n >= 0 { + buf = buf[:n] + } + + ctx.Resp.Header().Set("Cache-Control", "public,max-age=86400") + + if size >= 0 { + ctx.Resp.Header().Set("Content-Length", fmt.Sprintf("%d", size)) + } else { + log.Error("ServeData called to serve data: %s with size < 0: %d", name, size) + } + name = path.Base(name) + + // Google Chrome dislike commas in filenames, so let's change it to a space + name = strings.ReplaceAll(name, ",", " ") + + st := typesniffer.DetectContentType(buf) + + if st.IsText() || ctx.QueryBool("render") { + cs, err := charset.DetectEncoding(buf) + if err != nil { + log.Error("Detect raw file %s charset failed: %v, using by default utf-8", name, err) + cs = "utf-8" + } + ctx.Resp.Header().Set("Content-Type", "text/plain; charset="+strings.ToLower(cs)) + } else { + ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition") + + if (st.IsImage() || st.IsPDF()) && (setting.UI.SVG.Enabled || !st.IsSvgImage()) { + ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`inline; filename="%s"`, name)) + if st.IsSvgImage() { + ctx.Resp.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox") + ctx.Resp.Header().Set("X-Content-Type-Options", "nosniff") + ctx.Resp.Header().Set("Content-Type", typesniffer.SvgMimeType) + } + } else { + ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, name)) + if setting.MimeTypeMap.Enabled { + fileExtension := strings.ToLower(filepath.Ext(name)) + if mimetype, ok := setting.MimeTypeMap.Map[fileExtension]; ok { + ctx.Resp.Header().Set("Content-Type", mimetype) + } + } + } + } + + _, err = ctx.Resp.Write(buf) + if err != nil { + return err + } + _, err = io.Copy(ctx.Resp, reader) + return err +} diff --git a/routers/home.go b/routers/home.go deleted file mode 100644 index 7eaebc081fd4e..0000000000000 --- a/routers/home.go +++ /dev/null @@ -1,413 +0,0 @@ -// Copyright 2014 The Gogs Authors. All rights reserved. -// 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 routers - -import ( - "bytes" - "net/http" - "strings" - - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" - code_indexer "code.gitea.io/gitea/modules/indexer/code" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/modules/web/middleware" - "code.gitea.io/gitea/routers/user" -) - -const ( - // tplHome home page template - tplHome base.TplName = "home" - // tplExploreRepos explore repositories page template - tplExploreRepos base.TplName = "explore/repos" - // tplExploreUsers explore users page template - tplExploreUsers base.TplName = "explore/users" - // tplExploreOrganizations explore organizations page template - tplExploreOrganizations base.TplName = "explore/organizations" - // tplExploreCode explore code page template - tplExploreCode base.TplName = "explore/code" -) - -// Home render home page -func Home(ctx *context.Context) { - if ctx.IsSigned { - if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm { - ctx.Data["Title"] = ctx.Tr("auth.active_your_account") - ctx.HTML(http.StatusOK, user.TplActivate) - } else 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.HTML(http.StatusOK, "user/auth/prohibit_login") - } else if ctx.User.MustChangePassword { - ctx.Data["Title"] = ctx.Tr("auth.must_change_password") - ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/change_password" - middleware.SetRedirectToCookie(ctx.Resp, setting.AppSubURL+ctx.Req.URL.RequestURI()) - ctx.Redirect(setting.AppSubURL + "/user/settings/change_password") - } else { - user.Dashboard(ctx) - } - return - // Check non-logged users landing page. - } else if setting.LandingPageURL != setting.LandingPageHome { - ctx.Redirect(setting.AppSubURL + string(setting.LandingPageURL)) - return - } - - // Check auto-login. - uname := ctx.GetCookie(setting.CookieUserName) - if len(uname) != 0 { - ctx.Redirect(setting.AppSubURL + "/user/login") - return - } - - ctx.Data["PageIsHome"] = true - ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled - ctx.HTML(http.StatusOK, tplHome) -} - -// RepoSearchOptions when calling search repositories -type RepoSearchOptions struct { - OwnerID int64 - Private bool - Restricted bool - PageSize int - TplName base.TplName -} - -var ( - nullByte = []byte{0x00} -) - -func isKeywordValid(keyword string) bool { - return !bytes.Contains([]byte(keyword), nullByte) -} - -// RenderRepoSearch render repositories search page -func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) { - page := ctx.QueryInt("page") - if page <= 0 { - page = 1 - } - - var ( - repos []*models.Repository - count int64 - err error - orderBy models.SearchOrderBy - ) - - ctx.Data["SortType"] = ctx.Query("sort") - switch ctx.Query("sort") { - case "newest": - orderBy = models.SearchOrderByNewest - case "oldest": - orderBy = models.SearchOrderByOldest - case "recentupdate": - orderBy = models.SearchOrderByRecentUpdated - case "leastupdate": - orderBy = models.SearchOrderByLeastUpdated - case "reversealphabetically": - orderBy = models.SearchOrderByAlphabeticallyReverse - case "alphabetically": - orderBy = models.SearchOrderByAlphabetically - case "reversesize": - orderBy = models.SearchOrderBySizeReverse - case "size": - orderBy = models.SearchOrderBySize - case "moststars": - orderBy = models.SearchOrderByStarsReverse - case "feweststars": - orderBy = models.SearchOrderByStars - case "mostforks": - orderBy = models.SearchOrderByForksReverse - case "fewestforks": - orderBy = models.SearchOrderByForks - default: - ctx.Data["SortType"] = "recentupdate" - orderBy = models.SearchOrderByRecentUpdated - } - - keyword := strings.Trim(ctx.Query("q"), " ") - topicOnly := ctx.QueryBool("topic") - ctx.Data["TopicOnly"] = topicOnly - - repos, count, err = models.SearchRepository(&models.SearchRepoOptions{ - ListOptions: models.ListOptions{ - Page: page, - PageSize: opts.PageSize, - }, - Actor: ctx.User, - OrderBy: orderBy, - Private: opts.Private, - Keyword: keyword, - OwnerID: opts.OwnerID, - AllPublic: true, - AllLimited: true, - TopicOnly: topicOnly, - IncludeDescription: setting.UI.SearchRepoDescription, - }) - if err != nil { - ctx.ServerError("SearchRepository", err) - return - } - ctx.Data["Keyword"] = keyword - ctx.Data["Total"] = count - ctx.Data["Repos"] = repos - ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled - - pager := context.NewPagination(int(count), opts.PageSize, page, 5) - pager.SetDefaultParams(ctx) - pager.AddParam(ctx, "topic", "TopicOnly") - ctx.Data["Page"] = pager - - ctx.HTML(http.StatusOK, opts.TplName) -} - -// ExploreRepos render explore repositories page -func ExploreRepos(ctx *context.Context) { - ctx.Data["UsersIsDisabled"] = setting.Service.Explore.DisableUsersPage - ctx.Data["Title"] = ctx.Tr("explore") - ctx.Data["PageIsExplore"] = true - ctx.Data["PageIsExploreRepositories"] = true - ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled - - var ownerID int64 - if ctx.User != nil && !ctx.User.IsAdmin { - ownerID = ctx.User.ID - } - - RenderRepoSearch(ctx, &RepoSearchOptions{ - PageSize: setting.UI.ExplorePagingNum, - OwnerID: ownerID, - Private: ctx.User != nil, - TplName: tplExploreRepos, - }) -} - -// RenderUserSearch render user search page -func RenderUserSearch(ctx *context.Context, opts *models.SearchUserOptions, tplName base.TplName) { - opts.Page = ctx.QueryInt("page") - if opts.Page <= 1 { - opts.Page = 1 - } - - var ( - users []*models.User - count int64 - err error - orderBy models.SearchOrderBy - ) - - ctx.Data["SortType"] = ctx.Query("sort") - switch ctx.Query("sort") { - case "newest": - orderBy = models.SearchOrderByIDReverse - case "oldest": - orderBy = models.SearchOrderByID - case "recentupdate": - orderBy = models.SearchOrderByRecentUpdated - case "leastupdate": - orderBy = models.SearchOrderByLeastUpdated - case "reversealphabetically": - orderBy = models.SearchOrderByAlphabeticallyReverse - case "alphabetically": - orderBy = models.SearchOrderByAlphabetically - default: - ctx.Data["SortType"] = "alphabetically" - orderBy = models.SearchOrderByAlphabetically - } - - opts.Keyword = strings.Trim(ctx.Query("q"), " ") - opts.OrderBy = orderBy - if len(opts.Keyword) == 0 || isKeywordValid(opts.Keyword) { - users, count, err = models.SearchUsers(opts) - if err != nil { - ctx.ServerError("SearchUsers", err) - return - } - } - ctx.Data["Keyword"] = opts.Keyword - ctx.Data["Total"] = count - ctx.Data["Users"] = users - ctx.Data["UsersTwoFaStatus"] = models.UserList(users).GetTwoFaStatus() - ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail - ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled - - pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5) - pager.SetDefaultParams(ctx) - ctx.Data["Page"] = pager - - ctx.HTML(http.StatusOK, tplName) -} - -// ExploreUsers render explore users page -func ExploreUsers(ctx *context.Context) { - if setting.Service.Explore.DisableUsersPage { - ctx.Redirect(setting.AppSubURL + "/explore/repos") - return - } - ctx.Data["Title"] = ctx.Tr("explore") - ctx.Data["PageIsExplore"] = true - ctx.Data["PageIsExploreUsers"] = true - ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled - - RenderUserSearch(ctx, &models.SearchUserOptions{ - Actor: ctx.User, - Type: models.UserTypeIndividual, - ListOptions: models.ListOptions{PageSize: setting.UI.ExplorePagingNum}, - IsActive: util.OptionalBoolTrue, - Visible: []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate}, - }, tplExploreUsers) -} - -// ExploreOrganizations render explore organizations page -func ExploreOrganizations(ctx *context.Context) { - ctx.Data["UsersIsDisabled"] = setting.Service.Explore.DisableUsersPage - ctx.Data["Title"] = ctx.Tr("explore") - ctx.Data["PageIsExplore"] = true - ctx.Data["PageIsExploreOrganizations"] = true - ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled - - visibleTypes := []structs.VisibleType{structs.VisibleTypePublic} - if ctx.User != nil { - visibleTypes = append(visibleTypes, structs.VisibleTypeLimited, structs.VisibleTypePrivate) - } - - RenderUserSearch(ctx, &models.SearchUserOptions{ - Actor: ctx.User, - Type: models.UserTypeOrganization, - ListOptions: models.ListOptions{PageSize: setting.UI.ExplorePagingNum}, - Visible: visibleTypes, - }, tplExploreOrganizations) -} - -// ExploreCode render explore code page -func ExploreCode(ctx *context.Context) { - if !setting.Indexer.RepoIndexerEnabled { - ctx.Redirect(setting.AppSubURL+"/explore", 302) - return - } - - ctx.Data["UsersIsDisabled"] = setting.Service.Explore.DisableUsersPage - ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled - ctx.Data["Title"] = ctx.Tr("explore") - ctx.Data["PageIsExplore"] = true - ctx.Data["PageIsExploreCode"] = true - - language := strings.TrimSpace(ctx.Query("l")) - keyword := strings.TrimSpace(ctx.Query("q")) - page := ctx.QueryInt("page") - if page <= 0 { - page = 1 - } - - queryType := strings.TrimSpace(ctx.Query("t")) - isMatch := queryType == "match" - - var ( - repoIDs []int64 - err error - isAdmin bool - ) - if ctx.User != nil { - isAdmin = ctx.User.IsAdmin - } - - // guest user or non-admin user - if ctx.User == nil || !isAdmin { - repoIDs, err = models.FindUserAccessibleRepoIDs(ctx.User) - if err != nil { - ctx.ServerError("SearchResults", err) - return - } - } - - var ( - total int - searchResults []*code_indexer.Result - searchResultLanguages []*code_indexer.SearchResultLanguages - ) - - // if non-admin login user, we need check UnitTypeCode at first - if ctx.User != nil && len(repoIDs) > 0 { - repoMaps, err := models.GetRepositoriesMapByIDs(repoIDs) - if err != nil { - ctx.ServerError("SearchResults", err) - return - } - - var rightRepoMap = make(map[int64]*models.Repository, len(repoMaps)) - repoIDs = make([]int64, 0, len(repoMaps)) - for id, repo := range repoMaps { - if repo.CheckUnitUser(ctx.User, models.UnitTypeCode) { - rightRepoMap[id] = repo - repoIDs = append(repoIDs, id) - } - } - - ctx.Data["RepoMaps"] = rightRepoMap - - total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(repoIDs, language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch) - if err != nil { - ctx.ServerError("SearchResults", err) - return - } - // if non-login user or isAdmin, no need to check UnitTypeCode - } else if (ctx.User == nil && len(repoIDs) > 0) || isAdmin { - total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(repoIDs, language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch) - if err != nil { - ctx.ServerError("SearchResults", err) - return - } - - var loadRepoIDs = make([]int64, 0, len(searchResults)) - for _, result := range searchResults { - var find bool - for _, id := range loadRepoIDs { - if id == result.RepoID { - find = true - break - } - } - if !find { - loadRepoIDs = append(loadRepoIDs, result.RepoID) - } - } - - repoMaps, err := models.GetRepositoriesMapByIDs(loadRepoIDs) - if err != nil { - ctx.ServerError("SearchResults", err) - return - } - - ctx.Data["RepoMaps"] = repoMaps - } - - ctx.Data["Keyword"] = keyword - ctx.Data["Language"] = language - ctx.Data["queryType"] = queryType - ctx.Data["SearchResults"] = searchResults - ctx.Data["SearchResultLanguages"] = searchResultLanguages - ctx.Data["RequireHighlightJS"] = true - ctx.Data["PageIsViewCode"] = true - - pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5) - pager.SetDefaultParams(ctx) - pager.AddParam(ctx, "l", "Language") - ctx.Data["Page"] = pager - - ctx.HTML(http.StatusOK, tplExploreCode) -} - -// NotFound render 404 page -func NotFound(ctx *context.Context) { - ctx.Data["Title"] = "Page Not Found" - ctx.NotFound("home.NotFound", nil) -} diff --git a/routers/init.go b/routers/init.go index 220d87a29da87..4c28a953955ba 100644 --- a/routers/init.go +++ b/routers/init.go @@ -6,13 +6,9 @@ package routers import ( "context" - "fmt" "strings" - "time" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/models/migrations" - "code.gitea.io/gitea/modules/auth/sso" "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/cron" "code.gitea.io/gitea/modules/eventsource" @@ -32,6 +28,12 @@ import ( "code.gitea.io/gitea/modules/svg" "code.gitea.io/gitea/modules/task" "code.gitea.io/gitea/modules/translation" + "code.gitea.io/gitea/modules/web" + apiv1 "code.gitea.io/gitea/routers/api/v1" + "code.gitea.io/gitea/routers/common" + "code.gitea.io/gitea/routers/private" + web_routers "code.gitea.io/gitea/routers/web" + "code.gitea.io/gitea/services/auth" "code.gitea.io/gitea/services/mailer" mirror_service "code.gitea.io/gitea/services/mirror" pull_service "code.gitea.io/gitea/services/pull" @@ -63,63 +65,6 @@ func NewServices() { notification.NewContext() } -// In case of problems connecting to DB, retry connection. Eg, PGSQL in Docker Container on Synology -func initDBEngine(ctx context.Context) (err error) { - log.Info("Beginning ORM engine initialization.") - for i := 0; i < setting.Database.DBConnectRetries; i++ { - select { - case <-ctx.Done(): - return fmt.Errorf("Aborted due to shutdown:\nin retry ORM engine initialization") - default: - } - log.Info("ORM engine initialization attempt #%d/%d...", i+1, setting.Database.DBConnectRetries) - if err = models.NewEngine(ctx, migrations.Migrate); err == nil { - break - } else if i == setting.Database.DBConnectRetries-1 { - return err - } - log.Error("ORM engine initialization attempt #%d/%d failed. Error: %v", i+1, setting.Database.DBConnectRetries, err) - log.Info("Backing off for %d seconds", int64(setting.Database.DBConnectBackoff/time.Second)) - time.Sleep(setting.Database.DBConnectBackoff) - } - models.HasEngine = true - return nil -} - -// PreInstallInit preloads the configuration to check if we need to run install -func PreInstallInit(ctx context.Context) bool { - setting.NewContext() - if !setting.InstallLock { - log.Trace("AppPath: %s", setting.AppPath) - log.Trace("AppWorkPath: %s", setting.AppWorkPath) - log.Trace("Custom path: %s", setting.CustomPath) - log.Trace("Log path: %s", setting.LogRootPath) - log.Trace("Preparing to run install page") - translation.InitLocales() - if setting.EnableSQLite3 { - log.Info("SQLite3 Supported") - } - setting.InitDBConfig() - svg.Init() - } - - return !setting.InstallLock -} - -// PostInstallInit rereads the settings and starts up the database -func PostInstallInit(ctx context.Context) { - setting.NewContext() - setting.InitDBConfig() - if setting.InstallLock { - if err := initDBEngine(ctx); err == nil { - log.Info("ORM engine initialization successful!") - } else { - log.Fatal("ORM engine initialization failed: %v", err) - } - svg.Init() - } -} - // GlobalInit is for global configuration reload-able. func GlobalInit(ctx context.Context) { setting.NewContext() @@ -151,7 +96,7 @@ func GlobalInit(ctx context.Context) { } else if setting.Database.UseSQLite3 { log.Fatal("SQLite3 is set in settings but NOT Supported") } - if err := initDBEngine(ctx); err == nil { + if err := common.InitDBEngine(ctx); err == nil { log.Info("ORM engine initialization successful!") } else { log.Fatal("ORM engine initialization failed: %v", err) @@ -189,7 +134,20 @@ func GlobalInit(ctx context.Context) { } else { ssh.Unused() } - sso.Init() + auth.Init() svg.Init() } + +// NormalRoutes represents non install routes +func NormalRoutes() *web.Route { + r := web.NewRoute() + for _, middle := range common.Middlewares() { + r.Use(middle) + } + + r.Mount("/", web_routers.Routes()) + r.Mount("/api/v1", apiv1.Routes()) + r.Mount("/api/internal", private.Routes()) + return r +} diff --git a/routers/install.go b/routers/install/install.go similarity index 97% rename from routers/install.go rename to routers/install/install.go index 6c460a887d81c..ad985cf184882 100644 --- a/routers/install.go +++ b/routers/install/install.go @@ -1,8 +1,9 @@ // Copyright 2014 The Gogs Authors. All rights reserved. +// 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 routers +package install import ( "fmt" @@ -38,8 +39,8 @@ const ( tplPostInstall base.TplName = "post-install" ) -// InstallInit prepare for rendering installation page -func InstallInit(next http.Handler) http.Handler { +// Init prepare for rendering installation page +func Init(next http.Handler) http.Handler { var rnd = templates.HTMLRenderer() return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { @@ -158,8 +159,8 @@ func Install(ctx *context.Context) { ctx.HTML(http.StatusOK, tplInstall) } -// InstallPost response for submit install items -func InstallPost(ctx *context.Context) { +// SubmitInstall response for submit install items +func SubmitInstall(ctx *context.Context) { form := *web.GetForm(ctx).(*forms.InstallForm) var err error ctx.Data["CurDbOption"] = form.DbType @@ -342,7 +343,7 @@ func InstallPost(ctx *context.Context) { cfg.Section("server").Key("LFS_START_SERVER").SetValue("true") cfg.Section("server").Key("LFS_CONTENT_PATH").SetValue(form.LFSRootPath) var secretKey string - if secretKey, err = generate.NewJwtSecret(); err != nil { + if secretKey, err = generate.NewJwtSecretBase64(); err != nil { ctx.RenderWithErr(ctx.Tr("install.lfs_jwt_secret_failed", err), tplInstall, &form) return } @@ -409,7 +410,7 @@ func InstallPost(ctx *context.Context) { } // Re-read settings - PostInstallInit(ctx) + ReloadSettings(ctx) // Create admin account if len(form.AdminName) > 0 { diff --git a/routers/routes/install.go b/routers/install/routes.go similarity index 81% rename from routers/routes/install.go rename to routers/install/routes.go index 0918da1a4b8ab..36130d4b3f398 100644 --- a/routers/routes/install.go +++ b/routers/install/routes.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 routes +package install import ( "fmt" @@ -15,12 +15,18 @@ import ( "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/modules/web/middleware" - "code.gitea.io/gitea/routers" + "code.gitea.io/gitea/routers/common" "code.gitea.io/gitea/services/forms" "gitea.com/go-chi/session" ) +type dataStore map[string]interface{} + +func (d *dataStore) GetData() map[string]interface{} { + return *d +} + func installRecovery() func(next http.Handler) http.Handler { var rnd = templates.HTMLRenderer() return func(next http.Handler) http.Handler { @@ -48,21 +54,19 @@ func installRecovery() func(next http.Handler) http.Handler { lc := middleware.Locale(w, req) var store = dataStore{ - Data: templates.Vars{ - "Language": lc.Language(), - "CurrentURL": setting.AppSubURL + req.URL.RequestURI(), - "i18n": lc, - "SignedUserID": int64(0), - "SignedUserName": "", - }, + "Language": lc.Language(), + "CurrentURL": setting.AppSubURL + req.URL.RequestURI(), + "i18n": lc, + "SignedUserID": int64(0), + "SignedUserName": "", } w.Header().Set(`X-Frame-Options`, `SAMEORIGIN`) if !setting.IsProd() { - store.Data["ErrorMsg"] = combinedErr + store["ErrorMsg"] = combinedErr } - err = rnd.HTML(w, 500, "status/500", templates.BaseVars().Merge(store.Data)) + err = rnd.HTML(w, 500, "status/500", templates.BaseVars().Merge(store)) if err != nil { log.Error("%v", err) } @@ -74,10 +78,10 @@ func installRecovery() func(next http.Handler) http.Handler { } } -// InstallRoutes registers the install routes -func InstallRoutes() *web.Route { +// Routes registers the install routes +func Routes() *web.Route { r := web.NewRoute() - for _, middle := range commonMiddlewares() { + for _, middle := range common.Middlewares() { r.Use(middle) } @@ -99,9 +103,9 @@ func InstallRoutes() *web.Route { })) r.Use(installRecovery()) - r.Use(routers.InstallInit) - r.Get("/", routers.Install) - r.Post("/", web.Bind(forms.InstallForm{}), routers.InstallPost) + r.Use(Init) + r.Get("/", Install) + r.Post("/", web.Bind(forms.InstallForm{}), SubmitInstall) r.NotFound(func(w http.ResponseWriter, req *http.Request) { http.Redirect(w, req, setting.AppURL, http.StatusFound) }) diff --git a/routers/install/setting.go b/routers/install/setting.go new file mode 100644 index 0000000000000..53d166ba1de7f --- /dev/null +++ b/routers/install/setting.go @@ -0,0 +1,50 @@ +// 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 install + +import ( + "context" + + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/svg" + "code.gitea.io/gitea/modules/translation" + "code.gitea.io/gitea/routers/common" +) + +// PreloadSettings preloads the configuration to check if we need to run install +func PreloadSettings(ctx context.Context) bool { + setting.NewContext() + if !setting.InstallLock { + log.Trace("AppPath: %s", setting.AppPath) + log.Trace("AppWorkPath: %s", setting.AppWorkPath) + log.Trace("Custom path: %s", setting.CustomPath) + log.Trace("Log path: %s", setting.LogRootPath) + log.Trace("Preparing to run install page") + translation.InitLocales() + if setting.EnableSQLite3 { + log.Info("SQLite3 Supported") + } + setting.InitDBConfig() + setting.NewServicesForInstall() + svg.Init() + } + + return !setting.InstallLock +} + +// ReloadSettings rereads the settings and starts up the database +func ReloadSettings(ctx context.Context) { + setting.NewContext() + setting.InitDBConfig() + if setting.InstallLock { + if err := common.InitDBEngine(ctx); err == nil { + log.Info("ORM engine initialization successful!") + } else { + log.Fatal("ORM engine initialization failed: %v", err) + } + svg.Init() + } +} diff --git a/routers/admin/admin.go b/routers/web/admin/admin.go similarity index 100% rename from routers/admin/admin.go rename to routers/web/admin/admin.go diff --git a/routers/admin/admin_test.go b/routers/web/admin/admin_test.go similarity index 100% rename from routers/admin/admin_test.go rename to routers/web/admin/admin_test.go diff --git a/routers/admin/auths.go b/routers/web/admin/auths.go similarity index 100% rename from routers/admin/auths.go rename to routers/web/admin/auths.go diff --git a/routers/admin/emails.go b/routers/web/admin/emails.go similarity index 100% rename from routers/admin/emails.go rename to routers/web/admin/emails.go diff --git a/routers/admin/hooks.go b/routers/web/admin/hooks.go similarity index 100% rename from routers/admin/hooks.go rename to routers/web/admin/hooks.go diff --git a/routers/user/setting/main_test.go b/routers/web/admin/main_test.go similarity index 95% rename from routers/user/setting/main_test.go rename to routers/web/admin/main_test.go index d343c02f484ba..352907c73717c 100644 --- a/routers/user/setting/main_test.go +++ b/routers/web/admin/main_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 setting +package admin import ( "path/filepath" diff --git a/routers/admin/notice.go b/routers/web/admin/notice.go similarity index 100% rename from routers/admin/notice.go rename to routers/web/admin/notice.go diff --git a/routers/admin/orgs.go b/routers/web/admin/orgs.go similarity index 90% rename from routers/admin/orgs.go rename to routers/web/admin/orgs.go index 627f56eaecdfc..618f945704492 100644 --- a/routers/admin/orgs.go +++ b/routers/web/admin/orgs.go @@ -11,7 +11,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/routers" + "code.gitea.io/gitea/routers/web/explore" ) const ( @@ -24,7 +24,7 @@ func Organizations(ctx *context.Context) { ctx.Data["PageIsAdmin"] = true ctx.Data["PageIsAdminOrganizations"] = true - routers.RenderUserSearch(ctx, &models.SearchUserOptions{ + explore.RenderUserSearch(ctx, &models.SearchUserOptions{ Type: models.UserTypeOrganization, ListOptions: models.ListOptions{ PageSize: setting.UI.Admin.OrgPagingNum, diff --git a/routers/admin/repos.go b/routers/web/admin/repos.go similarity index 97% rename from routers/admin/repos.go rename to routers/web/admin/repos.go index d23f7c3d5a613..6128992f5a336 100644 --- a/routers/admin/repos.go +++ b/routers/web/admin/repos.go @@ -17,7 +17,7 @@ import ( "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/routers" + "code.gitea.io/gitea/routers/web/explore" repo_service "code.gitea.io/gitea/services/repository" ) @@ -32,7 +32,7 @@ func Repos(ctx *context.Context) { ctx.Data["PageIsAdmin"] = true ctx.Data["PageIsAdminRepositories"] = true - routers.RenderRepoSearch(ctx, &routers.RepoSearchOptions{ + explore.RenderRepoSearch(ctx, &explore.RepoSearchOptions{ Private: true, PageSize: setting.UI.Admin.RepoPagingNum, TplName: tplRepos, diff --git a/routers/admin/users.go b/routers/web/admin/users.go similarity index 98% rename from routers/admin/users.go rename to routers/web/admin/users.go index a71a11dd8a225..1b65795865fa7 100644 --- a/routers/admin/users.go +++ b/routers/web/admin/users.go @@ -18,8 +18,8 @@ import ( "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/routers/web/explore" + router_user_setting "code.gitea.io/gitea/routers/web/user/setting" "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/mailer" ) @@ -36,7 +36,7 @@ func Users(ctx *context.Context) { ctx.Data["PageIsAdmin"] = true ctx.Data["PageIsAdminUsers"] = true - routers.RenderUserSearch(ctx, &models.SearchUserOptions{ + explore.RenderUserSearch(ctx, &models.SearchUserOptions{ Type: models.UserTypeIndividual, ListOptions: models.ListOptions{ PageSize: setting.UI.Admin.UserPagingNum, diff --git a/routers/admin/users_test.go b/routers/web/admin/users_test.go similarity index 100% rename from routers/admin/users_test.go rename to routers/web/admin/users_test.go diff --git a/routers/routes/base.go b/routers/web/base.go similarity index 75% rename from routers/routes/base.go rename to routers/web/base.go index 0b784508a7902..f079be51f046a 100644 --- a/routers/routes/base.go +++ b/routers/web/base.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 routes +package web import ( "errors" @@ -13,10 +13,8 @@ import ( "path" "path/filepath" "strings" - "time" "code.gitea.io/gitea/models" - "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" @@ -24,30 +22,11 @@ import ( "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/web/middleware" + "code.gitea.io/gitea/services/auth" "gitea.com/go-chi/session" ) -// LoggerHandler is a handler that will log the routing to the default gitea log -func LoggerHandler(level log.Level) 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() - - _ = log.GetLogger("router").Log(0, level, "Started %s %s for %s", log.ColoredMethod(req.Method), req.URL.RequestURI(), req.RemoteAddr) - - next.ServeHTTP(w, req) - - var status int - if v, ok := w.(context.ResponseWriter); ok { - status = v.Status() - } - - _ = log.GetLogger("router").Log(0, level, "Completed %s %s %v %s in %v", log.ColoredMethod(req.Method), req.URL.RequestURI(), log.ColoredStatus(status), log.ColoredStatus(status, http.StatusText(status)), log.ColoredTime(time.Since(start))) - }) - } -} - func storageHandler(storageSetting setting.Storage, prefix string, objStore storage.ObjectStorage) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { if storageSetting.ServeDirect { @@ -134,12 +113,10 @@ func storageHandler(storageSetting setting.Storage, prefix string, objStore stor } } -type dataStore struct { - Data map[string]interface{} -} +type dataStore map[string]interface{} func (d *dataStore) GetData() map[string]interface{} { - return d.Data + return *d } // Recovery returns a middleware that recovers from any panics and writes a 500 and a log if so. @@ -165,11 +142,9 @@ func Recovery() func(next http.Handler) http.Handler { var lc = middleware.Locale(w, req) 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, } var user *models.User @@ -183,25 +158,25 @@ func Recovery() func(next http.Handler) http.Handler { } if user == nil { // Get user from session if logged in - do not attempt to sign-in - user = sso.SessionUser(sessionStore) + user = auth.SessionUser(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 + store["IsSigned"] = true + store["SignedUser"] = user + store["SignedUserID"] = user.ID + store["SignedUserName"] = user.Name + store["IsAdmin"] = user.IsAdmin } else { - store.Data["SignedUserID"] = int64(0) - store.Data["SignedUserName"] = "" + store["SignedUserID"] = int64(0) + store["SignedUserName"] = "" } w.Header().Set(`X-Frame-Options`, `SAMEORIGIN`) if !setting.IsProd() { - store.Data["ErrorMsg"] = combinedErr + store["ErrorMsg"] = combinedErr } - err = rnd.HTML(w, 500, "status/500", templates.BaseVars().Merge(store.Data)) + err = rnd.HTML(w, 500, "status/500", templates.BaseVars().Merge(store)) if err != nil { log.Error("%v", err) } diff --git a/routers/dev/template.go b/routers/web/dev/template.go similarity index 100% rename from routers/dev/template.go rename to routers/web/dev/template.go diff --git a/routers/events/events.go b/routers/web/events/events.go similarity index 98% rename from routers/events/events.go rename to routers/web/events/events.go index b140bf660cace..f9cc274851756 100644 --- a/routers/events/events.go +++ b/routers/web/events/events.go @@ -15,7 +15,7 @@ import ( "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/routers/user" + "code.gitea.io/gitea/routers/web/user" jsoniter "github.com/json-iterator/go" ) diff --git a/routers/web/explore/code.go b/routers/web/explore/code.go new file mode 100644 index 0000000000000..bf15b93cffd4c --- /dev/null +++ b/routers/web/explore/code.go @@ -0,0 +1,139 @@ +// 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 explore + +import ( + "net/http" + "strings" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/context" + code_indexer "code.gitea.io/gitea/modules/indexer/code" + "code.gitea.io/gitea/modules/setting" +) + +const ( + // tplExploreCode explore code page template + tplExploreCode base.TplName = "explore/code" +) + +// Code render explore code page +func Code(ctx *context.Context) { + if !setting.Indexer.RepoIndexerEnabled { + ctx.Redirect(setting.AppSubURL+"/explore", 302) + return + } + + ctx.Data["UsersIsDisabled"] = setting.Service.Explore.DisableUsersPage + ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled + ctx.Data["Title"] = ctx.Tr("explore") + ctx.Data["PageIsExplore"] = true + ctx.Data["PageIsExploreCode"] = true + + language := strings.TrimSpace(ctx.Query("l")) + keyword := strings.TrimSpace(ctx.Query("q")) + page := ctx.QueryInt("page") + if page <= 0 { + page = 1 + } + + queryType := strings.TrimSpace(ctx.Query("t")) + isMatch := queryType == "match" + + var ( + repoIDs []int64 + err error + isAdmin bool + ) + if ctx.User != nil { + isAdmin = ctx.User.IsAdmin + } + + // guest user or non-admin user + if ctx.User == nil || !isAdmin { + repoIDs, err = models.FindUserAccessibleRepoIDs(ctx.User) + if err != nil { + ctx.ServerError("SearchResults", err) + return + } + } + + var ( + total int + searchResults []*code_indexer.Result + searchResultLanguages []*code_indexer.SearchResultLanguages + ) + + // if non-admin login user, we need check UnitTypeCode at first + if ctx.User != nil && len(repoIDs) > 0 { + repoMaps, err := models.GetRepositoriesMapByIDs(repoIDs) + if err != nil { + ctx.ServerError("SearchResults", err) + return + } + + var rightRepoMap = make(map[int64]*models.Repository, len(repoMaps)) + repoIDs = make([]int64, 0, len(repoMaps)) + for id, repo := range repoMaps { + if repo.CheckUnitUser(ctx.User, models.UnitTypeCode) { + rightRepoMap[id] = repo + repoIDs = append(repoIDs, id) + } + } + + ctx.Data["RepoMaps"] = rightRepoMap + + total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(repoIDs, language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch) + if err != nil { + ctx.ServerError("SearchResults", err) + return + } + // if non-login user or isAdmin, no need to check UnitTypeCode + } else if (ctx.User == nil && len(repoIDs) > 0) || isAdmin { + total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(repoIDs, language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch) + if err != nil { + ctx.ServerError("SearchResults", err) + return + } + + var loadRepoIDs = make([]int64, 0, len(searchResults)) + for _, result := range searchResults { + var find bool + for _, id := range loadRepoIDs { + if id == result.RepoID { + find = true + break + } + } + if !find { + loadRepoIDs = append(loadRepoIDs, result.RepoID) + } + } + + repoMaps, err := models.GetRepositoriesMapByIDs(loadRepoIDs) + if err != nil { + ctx.ServerError("SearchResults", err) + return + } + + ctx.Data["RepoMaps"] = repoMaps + } + + ctx.Data["Keyword"] = keyword + ctx.Data["Language"] = language + ctx.Data["queryType"] = queryType + ctx.Data["SearchResults"] = searchResults + ctx.Data["SearchResultLanguages"] = searchResultLanguages + ctx.Data["RequireHighlightJS"] = true + ctx.Data["PageIsViewCode"] = true + + pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5) + pager.SetDefaultParams(ctx) + pager.AddParam(ctx, "l", "Language") + ctx.Data["Page"] = pager + + ctx.HTML(http.StatusOK, tplExploreCode) +} diff --git a/routers/web/explore/org.go b/routers/web/explore/org.go new file mode 100644 index 0000000000000..470e0eb8530b1 --- /dev/null +++ b/routers/web/explore/org.go @@ -0,0 +1,39 @@ +// 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 explore + +import ( + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/structs" +) + +const ( + // tplExploreOrganizations explore organizations page template + tplExploreOrganizations base.TplName = "explore/organizations" +) + +// Organizations render explore organizations page +func Organizations(ctx *context.Context) { + ctx.Data["UsersIsDisabled"] = setting.Service.Explore.DisableUsersPage + ctx.Data["Title"] = ctx.Tr("explore") + ctx.Data["PageIsExplore"] = true + ctx.Data["PageIsExploreOrganizations"] = true + ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled + + visibleTypes := []structs.VisibleType{structs.VisibleTypePublic} + if ctx.User != nil { + visibleTypes = append(visibleTypes, structs.VisibleTypeLimited, structs.VisibleTypePrivate) + } + + RenderUserSearch(ctx, &models.SearchUserOptions{ + Actor: ctx.User, + Type: models.UserTypeOrganization, + ListOptions: models.ListOptions{PageSize: setting.UI.ExplorePagingNum}, + Visible: visibleTypes, + }, tplExploreOrganizations) +} diff --git a/routers/web/explore/repo.go b/routers/web/explore/repo.go new file mode 100644 index 0000000000000..e9efae5688d7d --- /dev/null +++ b/routers/web/explore/repo.go @@ -0,0 +1,131 @@ +// 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 explore + +import ( + "net/http" + "strings" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/setting" +) + +const ( + // tplExploreRepos explore repositories page template + tplExploreRepos base.TplName = "explore/repos" +) + +// RepoSearchOptions when calling search repositories +type RepoSearchOptions struct { + OwnerID int64 + Private bool + Restricted bool + PageSize int + TplName base.TplName +} + +// RenderRepoSearch render repositories search page +func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) { + page := ctx.QueryInt("page") + if page <= 0 { + page = 1 + } + + var ( + repos []*models.Repository + count int64 + err error + orderBy models.SearchOrderBy + ) + + ctx.Data["SortType"] = ctx.Query("sort") + switch ctx.Query("sort") { + case "newest": + orderBy = models.SearchOrderByNewest + case "oldest": + orderBy = models.SearchOrderByOldest + case "recentupdate": + orderBy = models.SearchOrderByRecentUpdated + case "leastupdate": + orderBy = models.SearchOrderByLeastUpdated + case "reversealphabetically": + orderBy = models.SearchOrderByAlphabeticallyReverse + case "alphabetically": + orderBy = models.SearchOrderByAlphabetically + case "reversesize": + orderBy = models.SearchOrderBySizeReverse + case "size": + orderBy = models.SearchOrderBySize + case "moststars": + orderBy = models.SearchOrderByStarsReverse + case "feweststars": + orderBy = models.SearchOrderByStars + case "mostforks": + orderBy = models.SearchOrderByForksReverse + case "fewestforks": + orderBy = models.SearchOrderByForks + default: + ctx.Data["SortType"] = "recentupdate" + orderBy = models.SearchOrderByRecentUpdated + } + + keyword := strings.Trim(ctx.Query("q"), " ") + topicOnly := ctx.QueryBool("topic") + ctx.Data["TopicOnly"] = topicOnly + + repos, count, err = models.SearchRepository(&models.SearchRepoOptions{ + ListOptions: models.ListOptions{ + Page: page, + PageSize: opts.PageSize, + }, + Actor: ctx.User, + OrderBy: orderBy, + Private: opts.Private, + Keyword: keyword, + OwnerID: opts.OwnerID, + AllPublic: true, + AllLimited: true, + TopicOnly: topicOnly, + IncludeDescription: setting.UI.SearchRepoDescription, + }) + if err != nil { + ctx.ServerError("SearchRepository", err) + return + } + ctx.Data["Keyword"] = keyword + ctx.Data["Total"] = count + ctx.Data["Repos"] = repos + ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled + + pager := context.NewPagination(int(count), opts.PageSize, page, 5) + pager.SetDefaultParams(ctx) + pager.AddParam(ctx, "topic", "TopicOnly") + ctx.Data["Page"] = pager + + ctx.HTML(http.StatusOK, opts.TplName) +} + +// Repos render explore repositories page +func Repos(ctx *context.Context) { + ctx.Data["UsersIsDisabled"] = setting.Service.Explore.DisableUsersPage + ctx.Data["Title"] = ctx.Tr("explore") + ctx.Data["PageIsExplore"] = true + ctx.Data["PageIsExploreRepositories"] = true + ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled + + var ownerID int64 + if ctx.User != nil && !ctx.User.IsAdmin { + ownerID = ctx.User.ID + } + + RenderRepoSearch(ctx, &RepoSearchOptions{ + PageSize: setting.UI.ExplorePagingNum, + OwnerID: ownerID, + Private: ctx.User != nil, + TplName: tplExploreRepos, + }) +} diff --git a/routers/web/explore/user.go b/routers/web/explore/user.go new file mode 100644 index 0000000000000..52f543fe6696a --- /dev/null +++ b/routers/web/explore/user.go @@ -0,0 +1,107 @@ +// 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 explore + +import ( + "bytes" + "net/http" + "strings" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/util" +) + +const ( + // tplExploreUsers explore users page template + tplExploreUsers base.TplName = "explore/users" +) + +var ( + nullByte = []byte{0x00} +) + +func isKeywordValid(keyword string) bool { + return !bytes.Contains([]byte(keyword), nullByte) +} + +// RenderUserSearch render user search page +func RenderUserSearch(ctx *context.Context, opts *models.SearchUserOptions, tplName base.TplName) { + opts.Page = ctx.QueryInt("page") + if opts.Page <= 1 { + opts.Page = 1 + } + + var ( + users []*models.User + count int64 + err error + orderBy models.SearchOrderBy + ) + + ctx.Data["SortType"] = ctx.Query("sort") + switch ctx.Query("sort") { + case "newest": + orderBy = models.SearchOrderByIDReverse + case "oldest": + orderBy = models.SearchOrderByID + case "recentupdate": + orderBy = models.SearchOrderByRecentUpdated + case "leastupdate": + orderBy = models.SearchOrderByLeastUpdated + case "reversealphabetically": + orderBy = models.SearchOrderByAlphabeticallyReverse + case "alphabetically": + orderBy = models.SearchOrderByAlphabetically + default: + ctx.Data["SortType"] = "alphabetically" + orderBy = models.SearchOrderByAlphabetically + } + + opts.Keyword = strings.Trim(ctx.Query("q"), " ") + opts.OrderBy = orderBy + if len(opts.Keyword) == 0 || isKeywordValid(opts.Keyword) { + users, count, err = models.SearchUsers(opts) + if err != nil { + ctx.ServerError("SearchUsers", err) + return + } + } + ctx.Data["Keyword"] = opts.Keyword + ctx.Data["Total"] = count + ctx.Data["Users"] = users + ctx.Data["UsersTwoFaStatus"] = models.UserList(users).GetTwoFaStatus() + ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail + ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled + + pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5) + pager.SetDefaultParams(ctx) + ctx.Data["Page"] = pager + + ctx.HTML(http.StatusOK, tplName) +} + +// Users render explore users page +func Users(ctx *context.Context) { + if setting.Service.Explore.DisableUsersPage { + ctx.Redirect(setting.AppSubURL + "/explore/repos") + return + } + ctx.Data["Title"] = ctx.Tr("explore") + ctx.Data["PageIsExplore"] = true + ctx.Data["PageIsExploreUsers"] = true + ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled + + RenderUserSearch(ctx, &models.SearchUserOptions{ + Actor: ctx.User, + Type: models.UserTypeIndividual, + ListOptions: models.ListOptions{PageSize: setting.UI.ExplorePagingNum}, + IsActive: util.OptionalBoolTrue, + Visible: []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate}, + }, tplExploreUsers) +} diff --git a/routers/routes/goget.go b/routers/web/goget.go similarity index 99% rename from routers/routes/goget.go rename to routers/web/goget.go index 518f5e3073402..77934e7f55efc 100644 --- a/routers/routes/goget.go +++ b/routers/web/goget.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 routes +package web import ( "net/http" diff --git a/routers/web/home.go b/routers/web/home.go new file mode 100644 index 0000000000000..f50197691ffdd --- /dev/null +++ b/routers/web/home.go @@ -0,0 +1,65 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// 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 web + +import ( + "net/http" + + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web/middleware" + "code.gitea.io/gitea/routers/web/user" +) + +const ( + // tplHome home page template + tplHome base.TplName = "home" +) + +// Home render home page +func Home(ctx *context.Context) { + if ctx.IsSigned { + if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm { + ctx.Data["Title"] = ctx.Tr("auth.active_your_account") + ctx.HTML(http.StatusOK, user.TplActivate) + } else 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.HTML(http.StatusOK, "user/auth/prohibit_login") + } else if ctx.User.MustChangePassword { + ctx.Data["Title"] = ctx.Tr("auth.must_change_password") + ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/change_password" + middleware.SetRedirectToCookie(ctx.Resp, setting.AppSubURL+ctx.Req.URL.RequestURI()) + ctx.Redirect(setting.AppSubURL + "/user/settings/change_password") + } else { + user.Dashboard(ctx) + } + return + // Check non-logged users landing page. + } else if setting.LandingPageURL != setting.LandingPageHome { + ctx.Redirect(setting.AppSubURL + string(setting.LandingPageURL)) + return + } + + // Check auto-login. + uname := ctx.GetCookie(setting.CookieUserName) + if len(uname) != 0 { + ctx.Redirect(setting.AppSubURL + "/user/login") + return + } + + ctx.Data["PageIsHome"] = true + ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled + ctx.HTML(http.StatusOK, tplHome) +} + +// NotFound render 404 page +func NotFound(ctx *context.Context) { + ctx.Data["Title"] = "Page Not Found" + ctx.NotFound("home.NotFound", nil) +} diff --git a/routers/metrics.go b/routers/web/metrics.go similarity index 98% rename from routers/metrics.go rename to routers/web/metrics.go index db2fb8de44386..37558ee337646 100644 --- a/routers/metrics.go +++ b/routers/web/metrics.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 routers +package web import ( "crypto/subtle" diff --git a/routers/org/home.go b/routers/web/org/home.go similarity index 100% rename from routers/org/home.go rename to routers/web/org/home.go diff --git a/routers/org/members.go b/routers/web/org/members.go similarity index 100% rename from routers/org/members.go rename to routers/web/org/members.go diff --git a/routers/org/org.go b/routers/web/org/org.go similarity index 100% rename from routers/org/org.go rename to routers/web/org/org.go diff --git a/routers/org/org_labels.go b/routers/web/org/org_labels.go similarity index 100% rename from routers/org/org_labels.go rename to routers/web/org/org_labels.go diff --git a/routers/org/setting.go b/routers/web/org/setting.go similarity index 99% rename from routers/org/setting.go rename to routers/web/org/setting.go index 0e28a93acef95..aed90c66f7455 100644 --- a/routers/org/setting.go +++ b/routers/web/org/setting.go @@ -15,7 +15,7 @@ import ( "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" + userSetting "code.gitea.io/gitea/routers/web/user/setting" "code.gitea.io/gitea/services/forms" ) diff --git a/routers/org/teams.go b/routers/web/org/teams.go similarity index 100% rename from routers/org/teams.go rename to routers/web/org/teams.go diff --git a/routers/repo/activity.go b/routers/web/repo/activity.go similarity index 100% rename from routers/repo/activity.go rename to routers/web/repo/activity.go diff --git a/routers/repo/attachment.go b/routers/web/repo/attachment.go similarity index 97% rename from routers/repo/attachment.go rename to routers/web/repo/attachment.go index f53e7450ae028..5becbea2713a1 100644 --- a/routers/repo/attachment.go +++ b/routers/web/repo/attachment.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/upload" + "code.gitea.io/gitea/routers/common" ) // UploadIssueAttachment response for Issue/PR attachments @@ -152,7 +153,7 @@ func GetAttachment(ctx *context.Context) { } defer fr.Close() - if err = ServeData(ctx, attach.Name, attach.Size, fr); err != nil { + if err = common.ServeData(ctx, attach.Name, attach.Size, fr); err != nil { ctx.ServerError("ServeData", err) return } diff --git a/routers/repo/blame.go b/routers/web/repo/blame.go similarity index 100% rename from routers/repo/blame.go rename to routers/web/repo/blame.go diff --git a/routers/repo/branch.go b/routers/web/repo/branch.go similarity index 86% rename from routers/repo/branch.go rename to routers/web/repo/branch.go index eecaa888210cd..4625b1a272fc8 100644 --- a/routers/repo/branch.go +++ b/routers/web/repo/branch.go @@ -6,6 +6,7 @@ package repo import ( + "errors" "fmt" "net/http" "strings" @@ -83,34 +84,23 @@ func Branches(ctx *context.Context) { func DeleteBranchPost(ctx *context.Context) { defer redirect(ctx) branchName := ctx.Query("name") - if branchName == ctx.Repo.Repository.DefaultBranch { - log.Debug("DeleteBranch: Can't delete default branch '%s'", branchName) - ctx.Flash.Error(ctx.Tr("repo.branch.default_deletion_failed", branchName)) - return - } - - isProtected, err := ctx.Repo.Repository.IsProtectedBranch(branchName, ctx.User) - if err != nil { - log.Error("DeleteBranch: %v", err) - ctx.Flash.Error(ctx.Tr("repo.branch.deletion_failed", branchName)) - return - } - - if isProtected { - log.Debug("DeleteBranch: Can't delete protected branch '%s'", branchName) - ctx.Flash.Error(ctx.Tr("repo.branch.protected_deletion_failed", branchName)) - return - } - if !ctx.Repo.GitRepo.IsBranchExist(branchName) { - log.Debug("DeleteBranch: Can't delete non existing branch '%s'", branchName) - ctx.Flash.Error(ctx.Tr("repo.branch.deletion_failed", branchName)) - return - } + if err := repo_service.DeleteBranch(ctx.User, ctx.Repo.Repository, ctx.Repo.GitRepo, branchName); err != nil { + switch { + case git.IsErrBranchNotExist(err): + log.Debug("DeleteBranch: Can't delete non existing branch '%s'", branchName) + ctx.Flash.Error(ctx.Tr("repo.branch.deletion_failed", branchName)) + case errors.Is(err, repo_service.ErrBranchIsDefault): + log.Debug("DeleteBranch: Can't delete default branch '%s'", branchName) + ctx.Flash.Error(ctx.Tr("repo.branch.default_deletion_failed", branchName)) + case errors.Is(err, repo_service.ErrBranchIsProtected): + log.Debug("DeleteBranch: Can't delete protected branch '%s'", branchName) + ctx.Flash.Error(ctx.Tr("repo.branch.protected_deletion_failed", branchName)) + default: + log.Error("DeleteBranch: %v", err) + ctx.Flash.Error(ctx.Tr("repo.branch.deletion_failed", branchName)) + } - if err := deleteBranch(ctx, branchName); err != nil { - log.Error("DeleteBranch: %v", err) - ctx.Flash.Error(ctx.Tr("repo.branch.deletion_failed", branchName)) return } @@ -169,41 +159,6 @@ func redirect(ctx *context.Context) { }) } -func deleteBranch(ctx *context.Context, branchName string) error { - commit, err := ctx.Repo.GitRepo.GetBranchCommit(branchName) - if err != nil { - log.Error("GetBranchCommit: %v", err) - return err - } - - if err := ctx.Repo.GitRepo.DeleteBranch(branchName, git.DeleteBranchOptions{ - Force: true, - }); err != nil { - log.Error("DeleteBranch: %v", err) - return err - } - - // Don't return error below this - if err := repo_service.PushUpdate( - &repo_module.PushUpdateOptions{ - RefFullName: git.BranchPrefix + branchName, - OldCommitID: commit.ID.String(), - NewCommitID: git.EmptySHA, - PusherID: ctx.User.ID, - PusherName: ctx.User.Name, - RepoUserName: ctx.Repo.Owner.Name, - RepoName: ctx.Repo.Repository.Name, - }); err != nil { - log.Error("Update: %v", err) - } - - if err := ctx.Repo.Repository.AddDeletedBranch(branchName, commit.ID.String(), ctx.User.ID); err != nil { - log.Warn("AddDeletedBranch: %v", err) - } - - return nil -} - // loadBranches loads branches from the repository limited by page & pageSize. // NOTE: May write to context on error. func loadBranches(ctx *context.Context, skip, limit int) ([]*Branch, int) { diff --git a/routers/repo/commit.go b/routers/web/repo/commit.go similarity index 99% rename from routers/repo/commit.go rename to routers/web/repo/commit.go index c4719526376f7..3e6148bcbbc76 100644 --- a/routers/repo/commit.go +++ b/routers/web/repo/commit.go @@ -355,7 +355,7 @@ func Diff(ctx *context.Context) { } note := &git.Note{} - err = git.GetNote(ctx.Repo.GitRepo, commitID, note) + err = git.GetNote(ctx, ctx.Repo.GitRepo, commitID, note) if err == nil { ctx.Data["Note"] = string(charset.ToUTF8WithFallback(note.Message)) ctx.Data["NoteCommit"] = note.Commit diff --git a/routers/repo/compare.go b/routers/web/repo/compare.go similarity index 97% rename from routers/repo/compare.go rename to routers/web/repo/compare.go index d02ea0b1606bb..f53a31769df19 100644 --- a/routers/repo/compare.go +++ b/routers/web/repo/compare.go @@ -37,8 +37,20 @@ func setCompareContext(ctx *context.Context, base *git.Commit, head *git.Commit, ctx.Data["BaseCommit"] = base ctx.Data["HeadCommit"] = head + ctx.Data["GetBlobByPathForCommit"] = func(commit *git.Commit, path string) *git.Blob { + if commit == nil { + return nil + } + + blob, err := commit.GetBlobByPath(path) + if err != nil { + return nil + } + return blob + } + setPathsCompareContext(ctx, base, head, headTarget) - setImageCompareContext(ctx, base, head) + setImageCompareContext(ctx) setCsvCompareContext(ctx) } @@ -57,27 +69,18 @@ func setPathsCompareContext(ctx *context.Context, base *git.Commit, head *git.Co } // setImageCompareContext sets context data that is required by image compare template -func setImageCompareContext(ctx *context.Context, base *git.Commit, head *git.Commit) { - ctx.Data["IsImageFileInHead"] = head.IsImageFile - ctx.Data["IsImageFileInBase"] = base.IsImageFile - ctx.Data["ImageInfoBase"] = func(name string) *git.ImageMetaData { - if base == nil { - return nil - } - result, err := base.ImageInfo(name) - if err != nil { - log.Error("ImageInfo failed: %v", err) - return nil +func setImageCompareContext(ctx *context.Context) { + ctx.Data["IsBlobAnImage"] = func(blob *git.Blob) bool { + if blob == nil { + return false } - return result - } - ctx.Data["ImageInfo"] = func(name string) *git.ImageMetaData { - result, err := head.ImageInfo(name) + + st, err := blob.GuessContentType() if err != nil { - log.Error("ImageInfo failed: %v", err) - return nil + log.Error("GuessContentType failed: %v", err) + return false } - return result + return st.IsImage() && (setting.UI.SVG.Enabled || !st.IsSvgImage()) } } diff --git a/routers/repo/download.go b/routers/web/repo/download.go similarity index 52% rename from routers/repo/download.go rename to routers/web/repo/download.go index 4917c233ae4de..6f43d4b839198 100644 --- a/routers/repo/download.go +++ b/routers/web/repo/download.go @@ -6,98 +6,14 @@ package repo import ( - "fmt" - "io" - "path" - "path/filepath" - "strings" - - "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/httpcache" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/routers/common" ) -// ServeData download file from io.Reader -func ServeData(ctx *context.Context, name string, size int64, reader io.Reader) error { - buf := make([]byte, 1024) - n, err := reader.Read(buf) - if err != nil && err != io.EOF { - return err - } - if n >= 0 { - buf = buf[:n] - } - - ctx.Resp.Header().Set("Cache-Control", "public,max-age=86400") - - if size >= 0 { - ctx.Resp.Header().Set("Content-Length", fmt.Sprintf("%d", size)) - } else { - log.Error("ServeData called to serve data: %s with size < 0: %d", name, size) - } - name = path.Base(name) - - // Google Chrome dislike commas in filenames, so let's change it to a space - name = strings.ReplaceAll(name, ",", " ") - - if base.IsTextFile(buf) || ctx.QueryBool("render") { - cs, err := charset.DetectEncoding(buf) - if err != nil { - log.Error("Detect raw file %s charset failed: %v, using by default utf-8", name, err) - cs = "utf-8" - } - ctx.Resp.Header().Set("Content-Type", "text/plain; charset="+strings.ToLower(cs)) - } else if base.IsImageFile(buf) || base.IsPDFFile(buf) { - ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`inline; filename="%s"`, name)) - ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition") - if base.IsSVGImageFile(buf) { - ctx.Resp.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox") - ctx.Resp.Header().Set("X-Content-Type-Options", "nosniff") - ctx.Resp.Header().Set("Content-Type", base.SVGMimeType) - } - } else { - ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, name)) - ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition") - if setting.MimeTypeMap.Enabled { - fileExtension := strings.ToLower(filepath.Ext(name)) - if mimetype, ok := setting.MimeTypeMap.Map[fileExtension]; ok { - ctx.Resp.Header().Set("Content-Type", mimetype) - } - } - } - - _, err = ctx.Resp.Write(buf) - if err != nil { - return err - } - _, err = io.Copy(ctx.Resp, reader) - return err -} - -// ServeBlob download a git.Blob -func ServeBlob(ctx *context.Context, blob *git.Blob) error { - if httpcache.HandleGenericETagCache(ctx.Req, ctx.Resp, `"`+blob.ID.String()+`"`) { - return nil - } - - dataRc, err := blob.DataAsync() - if err != nil { - return err - } - defer func() { - if err = dataRc.Close(); err != nil { - log.Error("ServeBlob: Close: %v", err) - } - }() - - return ServeData(ctx, ctx.Repo.TreePath, blob.Size(), dataRc) -} - // ServeBlobOrLFS download a git.Blob redirecting to LFS if necessary func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob) error { if httpcache.HandleGenericETagCache(ctx.Req, ctx.Resp, `"`+blob.ID.String()+`"`) { @@ -126,7 +42,7 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob) error { log.Error("ServeBlobOrLFS: Close: %v", err) } closed = true - return ServeBlob(ctx, blob) + return common.ServeBlob(ctx, blob) } if httpcache.HandleGenericETagCache(ctx.Req, ctx.Resp, `"`+pointer.Oid+`"`) { return nil @@ -140,14 +56,14 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob) error { log.Error("ServeBlobOrLFS: Close: %v", err) } }() - return ServeData(ctx, ctx.Repo.TreePath, meta.Size, lfsDataRc) + return common.ServeData(ctx, ctx.Repo.TreePath, meta.Size, lfsDataRc) } if err = dataRc.Close(); err != nil { log.Error("ServeBlobOrLFS: Close: %v", err) } closed = true - return ServeBlob(ctx, blob) + return common.ServeBlob(ctx, blob) } // SingleDownload download a file by repos path @@ -161,7 +77,7 @@ func SingleDownload(ctx *context.Context) { } return } - if err = ServeBlob(ctx, blob); err != nil { + if err = common.ServeBlob(ctx, blob); err != nil { ctx.ServerError("ServeBlob", err) } } @@ -193,7 +109,7 @@ func DownloadByID(ctx *context.Context) { } return } - if err = ServeBlob(ctx, blob); err != nil { + if err = common.ServeBlob(ctx, blob); err != nil { ctx.ServerError("ServeBlob", err) } } diff --git a/routers/repo/editor.go b/routers/web/repo/editor.go similarity index 99% rename from routers/repo/editor.go rename to routers/web/repo/editor.go index 2a2c56952d086..0f978c7b01c59 100644 --- a/routers/repo/editor.go +++ b/routers/web/repo/editor.go @@ -20,6 +20,7 @@ import ( "code.gitea.io/gitea/modules/repofiles" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/typesniffer" "code.gitea.io/gitea/modules/upload" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" @@ -117,8 +118,8 @@ func editFile(ctx *context.Context, isNewFile bool) { buf = buf[:n] // Only some file types are editable online as text. - if !base.IsRepresentableAsText(buf) { - ctx.NotFound("base.IsRepresentableAsText", nil) + if !typesniffer.DetectContentType(buf).IsRepresentableAsText() { + ctx.NotFound("typesniffer.IsRepresentableAsText", nil) return } diff --git a/routers/repo/editor_test.go b/routers/web/repo/editor_test.go similarity index 100% rename from routers/repo/editor_test.go rename to routers/web/repo/editor_test.go diff --git a/routers/repo/http.go b/routers/web/repo/http.go similarity index 97% rename from routers/repo/http.go rename to routers/web/repo/http.go index 30d382b8ef18d..649d6d1eb1a35 100644 --- a/routers/repo/http.go +++ b/routers/web/repo/http.go @@ -366,7 +366,26 @@ func (h *serviceHandler) setHeaderCacheForever() { h.w.Header().Set("Cache-Control", "public, max-age=31536000") } +func containsParentDirectorySeparator(v string) bool { + if !strings.Contains(v, "..") { + return false + } + for _, ent := range strings.FieldsFunc(v, isSlashRune) { + if ent == ".." { + return true + } + } + return false +} + +func isSlashRune(r rune) bool { return r == '/' || r == '\\' } + func (h *serviceHandler) sendFile(contentType, file string) { + if containsParentDirectorySeparator(file) { + log.Error("request file path contains invalid path: %v", file) + h.w.WriteHeader(http.StatusBadRequest) + return + } reqFile := path.Join(h.dir, file) fi, err := os.Stat(reqFile) diff --git a/routers/web/repo/http_test.go b/routers/web/repo/http_test.go new file mode 100644 index 0000000000000..58ac1c07a129f --- /dev/null +++ b/routers/web/repo/http_test.go @@ -0,0 +1,43 @@ +// 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 repo + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestContainsParentDirectorySeparator(t *testing.T) { + tests := []struct { + v string + b bool + }{ + { + v: `user2/repo1/info/refs`, + b: false, + }, + { + v: `user2/repo1/HEAD`, + b: false, + }, + { + v: `user2/repo1/some.../strange_file...mp3`, + b: false, + }, + { + v: `user2/repo1/../../custom/conf/app.ini`, + b: true, + }, + { + v: `user2/repo1/objects/info/..\..\..\..\custom\conf\app.ini`, + b: true, + }, + } + + for i := range tests { + assert.EqualValues(t, tests[i].b, containsParentDirectorySeparator(tests[i].v)) + } +} diff --git a/routers/repo/issue.go b/routers/web/repo/issue.go similarity index 100% rename from routers/repo/issue.go rename to routers/web/repo/issue.go diff --git a/routers/repo/issue_dependency.go b/routers/web/repo/issue_dependency.go similarity index 100% rename from routers/repo/issue_dependency.go rename to routers/web/repo/issue_dependency.go diff --git a/routers/repo/issue_label.go b/routers/web/repo/issue_label.go similarity index 100% rename from routers/repo/issue_label.go rename to routers/web/repo/issue_label.go diff --git a/routers/repo/issue_label_test.go b/routers/web/repo/issue_label_test.go similarity index 100% rename from routers/repo/issue_label_test.go rename to routers/web/repo/issue_label_test.go diff --git a/routers/repo/issue_lock.go b/routers/web/repo/issue_lock.go similarity index 100% rename from routers/repo/issue_lock.go rename to routers/web/repo/issue_lock.go diff --git a/routers/repo/issue_stopwatch.go b/routers/web/repo/issue_stopwatch.go similarity index 100% rename from routers/repo/issue_stopwatch.go rename to routers/web/repo/issue_stopwatch.go diff --git a/routers/repo/issue_test.go b/routers/web/repo/issue_test.go similarity index 100% rename from routers/repo/issue_test.go rename to routers/web/repo/issue_test.go diff --git a/routers/repo/issue_timetrack.go b/routers/web/repo/issue_timetrack.go similarity index 100% rename from routers/repo/issue_timetrack.go rename to routers/web/repo/issue_timetrack.go diff --git a/routers/repo/issue_watch.go b/routers/web/repo/issue_watch.go similarity index 100% rename from routers/repo/issue_watch.go rename to routers/web/repo/issue_watch.go diff --git a/routers/repo/lfs.go b/routers/web/repo/lfs.go similarity index 97% rename from routers/repo/lfs.go rename to routers/web/repo/lfs.go index c17bd2f87a2d8..173ffb773f880 100644 --- a/routers/repo/lfs.go +++ b/routers/web/repo/lfs.go @@ -25,6 +25,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" + "code.gitea.io/gitea/modules/typesniffer" ) const ( @@ -278,16 +279,16 @@ func LFSFileGet(ctx *context.Context) { } buf = buf[:n] - ctx.Data["IsTextFile"] = base.IsTextFile(buf) - isRepresentableAsText := base.IsRepresentableAsText(buf) + st := typesniffer.DetectContentType(buf) + ctx.Data["IsTextFile"] = st.IsText() + isRepresentableAsText := st.IsRepresentableAsText() fileSize := meta.Size ctx.Data["FileSize"] = meta.Size ctx.Data["RawFileLink"] = fmt.Sprintf("%s%s.git/info/lfs/objects/%s/%s", setting.AppURL, ctx.Repo.Repository.FullName(), meta.Oid, "direct") switch { case isRepresentableAsText: - // This will be true for SVGs. - if base.IsImageFile(buf) { + if st.IsSvgImage() { ctx.Data["IsImageFile"] = true } @@ -322,13 +323,13 @@ func LFSFileGet(ctx *context.Context) { } ctx.Data["LineNums"] = gotemplate.HTML(output.String()) - case base.IsPDFFile(buf): + case st.IsPDF(): ctx.Data["IsPDFFile"] = true - case base.IsVideoFile(buf): + case st.IsVideo(): ctx.Data["IsVideoFile"] = true - case base.IsAudioFile(buf): + case st.IsAudio(): ctx.Data["IsAudioFile"] = true - case base.IsImageFile(buf): + case st.IsImage() && (setting.UI.SVG.Enabled || !st.IsSvgImage()): ctx.Data["IsImageFile"] = true } ctx.HTML(http.StatusOK, tplSettingsLFSFile) diff --git a/routers/repo/main_test.go b/routers/web/repo/main_test.go similarity index 84% rename from routers/repo/main_test.go rename to routers/web/repo/main_test.go index 04bbeeb211780..47f266365fd7f 100644 --- a/routers/repo/main_test.go +++ b/routers/web/repo/main_test.go @@ -12,5 +12,5 @@ import ( ) func TestMain(m *testing.M) { - models.MainTest(m, filepath.Join("..", "..")) + models.MainTest(m, filepath.Join("..", "..", "..")) } diff --git a/routers/repo/middlewares.go b/routers/web/repo/middlewares.go similarity index 100% rename from routers/repo/middlewares.go rename to routers/web/repo/middlewares.go diff --git a/routers/repo/migrate.go b/routers/web/repo/migrate.go similarity index 99% rename from routers/repo/migrate.go rename to routers/web/repo/migrate.go index 24d4ef4099bd9..521a856dae444 100644 --- a/routers/repo/migrate.go +++ b/routers/web/repo/migrate.go @@ -101,7 +101,7 @@ func handleMigrateError(ctx *context.Context, owner *models.User, err error, nam ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tpl, form) default: remoteAddr, _ := forms.ParseRemoteAddr(form.CloneAddr, form.AuthUsername, form.AuthPassword) - err = util.URLSanitizedError(err, remoteAddr) + err = util.NewStringURLSanitizedError(err, remoteAddr, true) if strings.Contains(err.Error(), "Authentication failed") || strings.Contains(err.Error(), "Bad credentials") || strings.Contains(err.Error(), "could not read Username") { diff --git a/routers/repo/milestone.go b/routers/web/repo/milestone.go similarity index 100% rename from routers/repo/milestone.go rename to routers/web/repo/milestone.go diff --git a/routers/repo/projects.go b/routers/web/repo/projects.go similarity index 100% rename from routers/repo/projects.go rename to routers/web/repo/projects.go diff --git a/routers/repo/projects_test.go b/routers/web/repo/projects_test.go similarity index 100% rename from routers/repo/projects_test.go rename to routers/web/repo/projects_test.go diff --git a/routers/repo/pull.go b/routers/web/repo/pull.go similarity index 97% rename from routers/repo/pull.go rename to routers/web/repo/pull.go index bb166c68a60da..e5554e9664444 100644 --- a/routers/repo/pull.go +++ b/routers/web/repo/pull.go @@ -9,6 +9,7 @@ package repo import ( "container/list" "crypto/subtle" + "errors" "fmt" "net/http" "path" @@ -21,7 +22,6 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "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" @@ -694,6 +694,10 @@ func ViewPullFiles(ctx *context.Context) { getBranchData(ctx, issue) ctx.Data["IsIssuePoster"] = ctx.IsSigned && issue.IsPoster(ctx.User.ID) ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) + + ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled + upload.AddUploadContext(ctx, "comment") + ctx.HTML(http.StatusOK, tplPullFiles) } @@ -1186,20 +1190,6 @@ func CleanUpPullRequest(ctx *context.Context) { }) }() - if pr.HeadBranch == pr.HeadRepo.DefaultBranch || !gitRepo.IsBranchExist(pr.HeadBranch) { - ctx.Flash.Error(ctx.Tr("repo.branch.deletion_failed", fullBranchName)) - return - } - - // Check if branch is not protected - if protected, err := pr.HeadRepo.IsProtectedBranch(pr.HeadBranch, ctx.User); err != nil || protected { - if err != nil { - log.Error("HeadRepo.IsProtectedBranch: %v", err) - } - ctx.Flash.Error(ctx.Tr("repo.branch.deletion_failed", fullBranchName)) - return - } - // Check if branch has no new commits headCommitID, err := gitBaseRepo.GetRefCommitID(pr.GetGitRefName()) if err != nil { @@ -1218,27 +1208,21 @@ func CleanUpPullRequest(ctx *context.Context) { return } - if err := gitRepo.DeleteBranch(pr.HeadBranch, git.DeleteBranchOptions{ - Force: true, - }); err != nil { - log.Error("DeleteBranch: %v", err) - ctx.Flash.Error(ctx.Tr("repo.branch.deletion_failed", fullBranchName)) + if err := repo_service.DeleteBranch(ctx.User, pr.HeadRepo, gitRepo, pr.HeadBranch); err != nil { + switch { + case git.IsErrBranchNotExist(err): + ctx.Flash.Error(ctx.Tr("repo.branch.deletion_failed", fullBranchName)) + case errors.Is(err, repo_service.ErrBranchIsDefault): + ctx.Flash.Error(ctx.Tr("repo.branch.deletion_failed", fullBranchName)) + case errors.Is(err, repo_service.ErrBranchIsProtected): + ctx.Flash.Error(ctx.Tr("repo.branch.deletion_failed", fullBranchName)) + default: + log.Error("DeleteBranch: %v", err) + ctx.Flash.Error(ctx.Tr("repo.branch.deletion_failed", fullBranchName)) + } return } - if err := repo_service.PushUpdate( - &repo_module.PushUpdateOptions{ - RefFullName: git.BranchPrefix + pr.HeadBranch, - OldCommitID: branchCommitID, - NewCommitID: git.EmptySHA, - PusherID: ctx.User.ID, - PusherName: ctx.User.Name, - RepoUserName: pr.HeadRepo.Owner.Name, - RepoName: pr.HeadRepo.Name, - }); err != nil { - log.Error("Update: %v", err) - } - if err := models.AddDeletePRBranchComment(ctx.User, pr.BaseRepo, issue.ID, pr.HeadBranch); err != nil { // Do not fail here as branch has already been deleted log.Error("DeleteBranch: %v", err) diff --git a/routers/repo/pull_review.go b/routers/web/repo/pull_review.go similarity index 97% rename from routers/repo/pull_review.go rename to routers/web/repo/pull_review.go index 9e505c3db3738..36eee3f377b89 100644 --- a/routers/repo/pull_review.go +++ b/routers/web/repo/pull_review.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/forms" pull_service "code.gitea.io/gitea/services/pull" @@ -211,7 +212,12 @@ func SubmitReview(ctx *context.Context) { } } - _, comm, err := pull_service.SubmitReview(ctx.User, ctx.Repo.GitRepo, issue, reviewType, form.Content, form.CommitID) + var attachments []string + if setting.Attachment.Enabled { + attachments = form.Files + } + + _, comm, err := pull_service.SubmitReview(ctx.User, ctx.Repo.GitRepo, issue, reviewType, form.Content, form.CommitID, attachments) if err != nil { if models.IsContentEmptyErr(err) { ctx.Flash.Error(ctx.Tr("repo.issues.review.content.empty")) diff --git a/routers/repo/release.go b/routers/web/repo/release.go similarity index 100% rename from routers/repo/release.go rename to routers/web/repo/release.go diff --git a/routers/repo/release_test.go b/routers/web/repo/release_test.go similarity index 100% rename from routers/repo/release_test.go rename to routers/web/repo/release_test.go diff --git a/routers/repo/repo.go b/routers/web/repo/repo.go similarity index 95% rename from routers/repo/repo.go rename to routers/web/repo/repo.go index 69471a83d398b..f149e92a8b6b5 100644 --- a/routers/repo/repo.go +++ b/routers/web/repo/repo.go @@ -364,30 +364,6 @@ func RedirectDownload(ctx *context.Context) { ctx.Error(http.StatusNotFound) } -// Download an archive of a repository -func Download(ctx *context.Context) { - uri := ctx.Params("*") - aReq := archiver_service.DeriveRequestFrom(ctx, uri) - - if aReq == nil { - ctx.Error(http.StatusNotFound) - return - } - - downloadName := ctx.Repo.Repository.Name + "-" + aReq.GetArchiveName() - complete := aReq.IsComplete() - if !complete { - aReq = archiver_service.ArchiveRepository(aReq) - complete = aReq.WaitForCompletion(ctx) - } - - if complete { - ctx.ServeFile(aReq.GetArchivePath(), downloadName) - } else { - ctx.Error(http.StatusNotFound) - } -} - // InitiateDownload will enqueue an archival request, as needed. It may submit // a request that's already in-progress, but the archiver service will just // kind of drop it on the floor if this is the case. diff --git a/routers/repo/search.go b/routers/web/repo/search.go similarity index 100% rename from routers/repo/search.go rename to routers/web/repo/search.go diff --git a/routers/repo/setting.go b/routers/web/repo/setting.go similarity index 90% rename from routers/repo/setting.go rename to routers/web/repo/setting.go index 51a0e01164756..c48b19b63c1bc 100644 --- a/routers/repo/setting.go +++ b/routers/web/repo/setting.go @@ -10,6 +10,7 @@ import ( "fmt" "io/ioutil" "net/http" + "strconv" "strings" "time" @@ -24,6 +25,8 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/typesniffer" + "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/validation" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/utils" @@ -48,6 +51,8 @@ func Settings(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsOptions"] = true ctx.Data["ForcePrivate"] = setting.Repository.ForcePrivate + ctx.Data["DisabledMirrors"] = setting.Repository.DisableMirrors + ctx.Data["DefaultMirrorInterval"] = setting.Mirror.DefaultInterval signing, _ := models.SigningKey(ctx.Repo.Repository.RepoPath()) ctx.Data["SigningKeyAvailable"] = len(signing) > 0 @@ -166,10 +171,9 @@ func SettingsPost(ctx *context.Context) { } } - oldUsername := mirror_service.Username(ctx.Repo.Mirror) - oldPassword := mirror_service.Password(ctx.Repo.Mirror) - if form.MirrorPassword == "" && form.MirrorUsername == oldUsername { - form.MirrorPassword = oldPassword + u, _ := git.GetRemoteAddress(ctx.Repo.Repository.RepoPath(), ctx.Repo.Mirror.GetRemoteName()) + if u.User != nil && form.MirrorPassword == "" && form.MirrorUsername == u.User.Username() { + form.MirrorPassword, _ = u.User.Password() } address, err := forms.ParseRemoteAddr(form.MirrorAddress, form.MirrorUsername, form.MirrorPassword) @@ -225,6 +229,92 @@ func SettingsPost(ctx *context.Context) { ctx.Flash.Info(ctx.Tr("repo.settings.mirror_sync_in_progress")) ctx.Redirect(repo.Link() + "/settings") + case "push-mirror-sync": + m, err := selectPushMirrorByForm(form, repo) + if err != nil { + ctx.NotFound("", nil) + return + } + + mirror_service.AddPushMirrorToQueue(m.ID) + + ctx.Flash.Info(ctx.Tr("repo.settings.mirror_sync_in_progress")) + ctx.Redirect(repo.Link() + "/settings") + + case "push-mirror-remove": + // This section doesn't require repo_name/RepoName to be set in the form, don't show it + // as an error on the UI for this action + ctx.Data["Err_RepoName"] = nil + + m, err := selectPushMirrorByForm(form, repo) + if err != nil { + ctx.NotFound("", nil) + return + } + + if err = mirror_service.RemovePushMirrorRemote(m); err != nil { + ctx.ServerError("RemovePushMirrorRemote", err) + return + } + + if err = models.DeletePushMirrorByID(m.ID); err != nil { + ctx.ServerError("DeletePushMirrorByID", err) + return + } + + ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success")) + ctx.Redirect(repo.Link() + "/settings") + + case "push-mirror-add": + // This section doesn't require repo_name/RepoName to be set in the form, don't show it + // as an error on the UI for this action + ctx.Data["Err_RepoName"] = nil + + interval, err := time.ParseDuration(form.PushMirrorInterval) + if err != nil || (interval != 0 && interval < setting.Mirror.MinInterval) { + ctx.Data["Err_PushMirrorInterval"] = true + ctx.RenderWithErr(ctx.Tr("repo.mirror_interval_invalid"), tplSettingsOptions, &form) + return + } + + address, err := forms.ParseRemoteAddr(form.PushMirrorAddress, form.PushMirrorUsername, form.PushMirrorPassword) + if err == nil { + err = migrations.IsMigrateURLAllowed(address, ctx.User) + } + if err != nil { + ctx.Data["Err_PushMirrorAddress"] = true + handleSettingRemoteAddrError(ctx, err, form) + return + } + + remoteSuffix, err := util.RandomString(10) + if err != nil { + ctx.ServerError("RandomString", err) + return + } + + m := &models.PushMirror{ + RepoID: repo.ID, + Repo: repo, + RemoteName: fmt.Sprintf("remote_mirror_%s", remoteSuffix), + Interval: interval, + } + if err := models.InsertPushMirror(m); err != nil { + ctx.ServerError("InsertPushMirror", err) + return + } + + if err := mirror_service.AddPushMirrorRemote(m, address); err != nil { + if err := models.DeletePushMirrorByID(m.ID); err != nil { + log.Error("DeletePushMirrorByID %v", err) + } + ctx.ServerError("AddPushMirrorRemote", err) + return + } + + ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success")) + ctx.Redirect(repo.Link() + "/settings") + case "advanced": var repoChanged bool var units []models.RepoUnit @@ -1021,7 +1111,8 @@ func UpdateAvatarSetting(ctx *context.Context, form forms.AvatarForm) error { if err != nil { return fmt.Errorf("ioutil.ReadAll: %v", err) } - if !base.IsImageFile(data) { + st := typesniffer.DetectContentType(data) + if !(st.IsImage() && !st.IsSvgImage()) { return errors.New(ctx.Tr("settings.uploaded_avatar_not_a_image")) } if err = ctxRepo.UploadAvatar(data); err != nil { @@ -1049,3 +1140,22 @@ func SettingsDeleteAvatar(ctx *context.Context) { } ctx.Redirect(ctx.Repo.RepoLink + "/settings") } + +func selectPushMirrorByForm(form *forms.RepoSettingForm, repo *models.Repository) (*models.PushMirror, error) { + id, err := strconv.ParseInt(form.PushMirrorID, 10, 64) + if err != nil { + return nil, err + } + + if err = repo.LoadPushMirrors(); err != nil { + return nil, err + } + + for _, m := range repo.PushMirrors { + if m.ID == id { + return m, nil + } + } + + return nil, fmt.Errorf("PushMirror[%v] not associated to repository %v", id, repo) +} diff --git a/routers/repo/setting_protected_branch.go b/routers/web/repo/setting_protected_branch.go similarity index 100% rename from routers/repo/setting_protected_branch.go rename to routers/web/repo/setting_protected_branch.go diff --git a/routers/repo/settings_test.go b/routers/web/repo/settings_test.go similarity index 100% rename from routers/repo/settings_test.go rename to routers/web/repo/settings_test.go diff --git a/routers/repo/topic.go b/routers/web/repo/topic.go similarity index 100% rename from routers/repo/topic.go rename to routers/web/repo/topic.go diff --git a/routers/repo/view.go b/routers/web/repo/view.go similarity index 97% rename from routers/repo/view.go rename to routers/web/repo/view.go index 285cacc2dfa96..cd5b0f43edbc7 100644 --- a/routers/repo/view.go +++ b/routers/web/repo/view.go @@ -29,6 +29,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/typesniffer" ) const ( @@ -145,7 +146,7 @@ func renderDirectory(ctx *context.Context, treeLink string) { } var latestCommit *git.Commit - ctx.Data["Files"], latestCommit, err = entries.GetCommitsInfo(ctx.Repo.Commit, ctx.Repo.TreePath, c) + ctx.Data["Files"], latestCommit, err = entries.GetCommitsInfo(ctx, ctx.Repo.Commit, ctx.Repo.TreePath, c) if err != nil { ctx.ServerError("GetCommitsInfo", err) return @@ -265,7 +266,9 @@ func renderDirectory(ctx *context.Context, treeLink string) { n, _ := dataRc.Read(buf) buf = buf[:n] - isTextFile := base.IsTextFile(buf) + st := typesniffer.DetectContentType(buf) + isTextFile := st.IsText() + ctx.Data["FileIsText"] = isTextFile ctx.Data["FileName"] = readmeFile.name fileSize := int64(0) @@ -302,7 +305,8 @@ func renderDirectory(ctx *context.Context, treeLink string) { } buf = buf[:n] - isTextFile = base.IsTextFile(buf) + st = typesniffer.DetectContentType(buf) + isTextFile = st.IsText() ctx.Data["IsTextFile"] = isTextFile fileSize = meta.Size @@ -405,7 +409,9 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st n, _ := dataRc.Read(buf) buf = buf[:n] - isTextFile := base.IsTextFile(buf) + st := typesniffer.DetectContentType(buf) + isTextFile := st.IsText() + isLFSFile := false isDisplayingSource := ctx.Query("display") == "source" isDisplayingRendered := !isDisplayingSource @@ -441,14 +447,16 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st } buf = buf[:n] - isTextFile = base.IsTextFile(buf) + st = typesniffer.DetectContentType(buf) + isTextFile = st.IsText() + fileSize = meta.Size ctx.Data["RawFileLink"] = fmt.Sprintf("%s/media/%s/%s", ctx.Repo.RepoLink, ctx.Repo.BranchNameSubURL(), ctx.Repo.TreePath) } } } - isRepresentableAsText := base.IsRepresentableAsText(buf) + isRepresentableAsText := st.IsRepresentableAsText() if !isRepresentableAsText { // If we can't show plain text, always try to render. isDisplayingSource = false @@ -483,8 +491,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st switch { case isRepresentableAsText: - // This will be true for SVGs. - if base.IsImageFile(buf) { + if st.IsSvgImage() { ctx.Data["IsImageFile"] = true ctx.Data["HasSourceRenderedToggle"] = true } @@ -540,13 +547,13 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st } } - case base.IsPDFFile(buf): + case st.IsPDF(): ctx.Data["IsPDFFile"] = true - case base.IsVideoFile(buf): + case st.IsVideo(): ctx.Data["IsVideoFile"] = true - case base.IsAudioFile(buf): + case st.IsAudio(): ctx.Data["IsAudioFile"] = true - case base.IsImageFile(buf): + case st.IsImage() && (setting.UI.SVG.Enabled || !st.IsSvgImage()): ctx.Data["IsImageFile"] = true default: if fileSize >= setting.UI.MaxDisplayFileSize { diff --git a/routers/repo/webhook.go b/routers/web/repo/webhook.go similarity index 100% rename from routers/repo/webhook.go rename to routers/web/repo/webhook.go diff --git a/routers/repo/wiki.go b/routers/web/repo/wiki.go similarity index 99% rename from routers/repo/wiki.go rename to routers/web/repo/wiki.go index 1bdd06dce57d7..cceb8451e58f5 100644 --- a/routers/repo/wiki.go +++ b/routers/web/repo/wiki.go @@ -24,6 +24,7 @@ import ( "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/routers/common" "code.gitea.io/gitea/services/forms" wiki_service "code.gitea.io/gitea/services/wiki" ) @@ -558,7 +559,7 @@ func WikiRaw(ctx *context.Context) { } if entry != nil { - if err = ServeBlob(ctx, entry.Blob()); err != nil { + if err = common.ServeBlob(ctx, entry.Blob()); err != nil { ctx.ServerError("ServeBlob", err) } return diff --git a/routers/repo/wiki_test.go b/routers/web/repo/wiki_test.go similarity index 98% rename from routers/repo/wiki_test.go rename to routers/web/repo/wiki_test.go index 4b28a5af86e08..8934a6619f0fd 100644 --- a/routers/repo/wiki_test.go +++ b/routers/web/repo/wiki_test.go @@ -64,7 +64,7 @@ func assertPagesMetas(t *testing.T, expectedNames []string, metas interface{}) { if !assert.True(t, ok) { return } - if !assert.EqualValues(t, len(expectedNames), len(pageMetas)) { + if !assert.Len(t, pageMetas, len(expectedNames)) { return } for i, pageMeta := range pageMetas { diff --git a/routers/swagger_json.go b/routers/web/swagger_json.go similarity index 97% rename from routers/swagger_json.go rename to routers/web/swagger_json.go index 78c7fb1e24b33..82d72698c606c 100644 --- a/routers/swagger_json.go +++ b/routers/web/swagger_json.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 routers +package web import ( "net/http" diff --git a/routers/user/auth.go b/routers/web/user/auth.go similarity index 100% rename from routers/user/auth.go rename to routers/web/user/auth.go diff --git a/routers/user/auth_openid.go b/routers/web/user/auth_openid.go similarity index 100% rename from routers/user/auth_openid.go rename to routers/web/user/auth_openid.go diff --git a/routers/user/avatar.go b/routers/web/user/avatar.go similarity index 100% rename from routers/user/avatar.go rename to routers/web/user/avatar.go diff --git a/routers/user/home.go b/routers/web/user/home.go similarity index 99% rename from routers/user/home.go rename to routers/web/user/home.go index acf73f82fe362..d3fc36c73012e 100644 --- a/routers/user/home.go +++ b/routers/web/user/home.go @@ -49,11 +49,12 @@ func getDashboardContextUser(ctx *context.Context) *models.User { } ctx.Data["ContextUser"] = ctxUser - if err := ctx.User.GetOrganizations(&models.SearchOrganizationsOptions{All: true}); err != nil { - ctx.ServerError("GetOrganizations", err) + orgs, err := models.GetUserOrgsList(ctx.User.ID) + if err != nil { + ctx.ServerError("GetUserOrgsList", err) return nil } - ctx.Data["Orgs"] = ctx.User.Orgs + ctx.Data["Orgs"] = orgs return ctxUser } diff --git a/routers/user/home_test.go b/routers/web/user/home_test.go similarity index 97% rename from routers/user/home_test.go rename to routers/web/user/home_test.go index ecc02fd33a83c..b0109c354f436 100644 --- a/routers/user/home_test.go +++ b/routers/web/user/home_test.go @@ -33,9 +33,9 @@ func TestArchivedIssues(t *testing.T) { IsArchived[repo.ID] = repo.IsArchived NumIssues[repo.ID] = repo.NumIssues } - assert.EqualValues(t, false, IsArchived[50]) + assert.False(t, IsArchived[50]) assert.EqualValues(t, 1, NumIssues[50]) - assert.EqualValues(t, true, IsArchived[51]) + assert.True(t, IsArchived[51]) assert.EqualValues(t, 1, NumIssues[51]) // Act diff --git a/routers/user/main_test.go b/routers/web/user/main_test.go similarity index 84% rename from routers/user/main_test.go rename to routers/web/user/main_test.go index ed0724dc7733e..be17dd1f3135f 100644 --- a/routers/user/main_test.go +++ b/routers/web/user/main_test.go @@ -12,5 +12,5 @@ import ( ) func TestMain(m *testing.M) { - models.MainTest(m, filepath.Join("..", "..")) + models.MainTest(m, filepath.Join("..", "..", "..")) } diff --git a/routers/user/notification.go b/routers/web/user/notification.go similarity index 100% rename from routers/user/notification.go rename to routers/web/user/notification.go diff --git a/routers/user/oauth.go b/routers/web/user/oauth.go similarity index 89% rename from routers/user/oauth.go rename to routers/web/user/oauth.go index 3ef5a56c01734..72295b4447c28 100644 --- a/routers/user/oauth.go +++ b/routers/web/user/oauth.go @@ -13,17 +13,19 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth/sso" + "code.gitea.io/gitea/modules/auth/oauth2" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/auth" "code.gitea.io/gitea/services/forms" "gitea.com/go-chi/binding" "github.com/dgrijalva/jwt-go" + jsoniter "github.com/json-iterator/go" ) const ( @@ -131,7 +133,7 @@ type AccessTokenResponse struct { IDToken string `json:"id_token,omitempty"` } -func newAccessTokenResponse(grant *models.OAuth2Grant, clientSecret string) (*AccessTokenResponse, *AccessTokenError) { +func newAccessTokenResponse(grant *models.OAuth2Grant, signingKey oauth2.JWTSigningKey) (*AccessTokenResponse, *AccessTokenError) { if setting.OAuth2.InvalidateRefreshTokens { if err := grant.IncreaseCounter(); err != nil { return nil, &AccessTokenError{ @@ -185,6 +187,21 @@ func newAccessTokenResponse(grant *models.OAuth2Grant, clientSecret string) (*Ac ErrorDescription: "cannot find application", } } + err = app.LoadUser() + if err != nil { + if models.IsErrUserNotExist(err) { + return nil, &AccessTokenError{ + ErrorCode: AccessTokenErrorCodeInvalidRequest, + ErrorDescription: "cannot find user", + } + } + log.Error("Error loading user: %v", err) + return nil, &AccessTokenError{ + ErrorCode: AccessTokenErrorCodeInvalidRequest, + ErrorDescription: "server error", + } + } + idToken := &models.OIDCToken{ StandardClaims: jwt.StandardClaims{ ExpiresAt: expirationDate.AsTime().Unix(), @@ -194,7 +211,21 @@ func newAccessTokenResponse(grant *models.OAuth2Grant, clientSecret string) (*Ac }, Nonce: grant.Nonce, } - signedIDToken, err = idToken.SignToken(clientSecret) + if grant.ScopeContains("profile") { + idToken.Name = app.User.FullName + idToken.PreferredUsername = app.User.Name + idToken.Profile = app.User.HTMLURL() + idToken.Picture = app.User.AvatarLink() + idToken.Website = app.User.Website + idToken.Locale = app.User.Language + idToken.UpdatedAt = app.User.UpdatedUnix + } + if grant.ScopeContains("email") { + idToken.Email = app.User.Email + idToken.EmailVerified = app.User.IsActive + } + + signedIDToken, err = idToken.SignToken(signingKey) if err != nil { return nil, &AccessTokenError{ ErrorCode: AccessTokenErrorCodeInvalidRequest, @@ -228,7 +259,7 @@ func InfoOAuth(ctx *context.Context) { ctx.HandleText(http.StatusUnauthorized, "no valid auth token authorization") return } - uid := sso.CheckOAuthAccessToken(auths[1]) + uid := auth.CheckOAuthAccessToken(auths[1]) if uid == 0 { handleBearerTokenError(ctx, BearerTokenError{ ErrorCode: BearerTokenErrorCodeInvalidToken, @@ -451,12 +482,37 @@ func GrantApplicationOAuth(ctx *context.Context) { func OIDCWellKnown(ctx *context.Context) { t := ctx.Render.TemplateLookup("user/auth/oidc_wellknown") ctx.Resp.Header().Set("Content-Type", "application/json") + ctx.Data["SigningKey"] = oauth2.DefaultSigningKey if err := t.Execute(ctx.Resp, ctx.Data); err != nil { log.Error("%v", err) ctx.Error(http.StatusInternalServerError) } } +// OIDCKeys generates the JSON Web Key Set +func OIDCKeys(ctx *context.Context) { + jwk, err := oauth2.DefaultSigningKey.ToJWK() + if err != nil { + log.Error("Error converting signing key to JWK: %v", err) + ctx.Error(http.StatusInternalServerError) + return + } + + jwk["use"] = "sig" + + jwks := map[string][]map[string]string{ + "keys": { + jwk, + }, + } + + ctx.Resp.Header().Set("Content-Type", "application/json") + enc := jsoniter.NewEncoder(ctx.Resp) + if err := enc.Encode(jwks); err != nil { + log.Error("Failed to encode representation as json. Error: %v", err) + } +} + // AccessTokenOAuth manages all access token requests by the client func AccessTokenOAuth(ctx *context.Context) { form := *web.GetForm(ctx).(*forms.AccessTokenForm) @@ -484,13 +540,25 @@ func AccessTokenOAuth(ctx *context.Context) { form.ClientSecret = pair[1] } } + + signingKey := oauth2.DefaultSigningKey + if signingKey.IsSymmetric() { + clientKey, err := oauth2.CreateJWTSingingKey(signingKey.SigningMethod().Alg(), []byte(form.ClientSecret)) + if err != nil { + handleAccessTokenError(ctx, AccessTokenError{ + ErrorCode: AccessTokenErrorCodeInvalidRequest, + ErrorDescription: "Error creating signing key", + }) + return + } + signingKey = clientKey + } + switch form.GrantType { case "refresh_token": - handleRefreshToken(ctx, form) - return + handleRefreshToken(ctx, form, signingKey) case "authorization_code": - handleAuthorizationCode(ctx, form) - return + handleAuthorizationCode(ctx, form, signingKey) default: handleAccessTokenError(ctx, AccessTokenError{ ErrorCode: AccessTokenErrorCodeUnsupportedGrantType, @@ -499,7 +567,7 @@ func AccessTokenOAuth(ctx *context.Context) { } } -func handleRefreshToken(ctx *context.Context, form forms.AccessTokenForm) { +func handleRefreshToken(ctx *context.Context, form forms.AccessTokenForm, signingKey oauth2.JWTSigningKey) { token, err := models.ParseOAuth2Token(form.RefreshToken) if err != nil { handleAccessTokenError(ctx, AccessTokenError{ @@ -527,7 +595,7 @@ func handleRefreshToken(ctx *context.Context, form forms.AccessTokenForm) { log.Warn("A client tried to use a refresh token for grant_id = %d was used twice!", grant.ID) return } - accessToken, tokenErr := newAccessTokenResponse(grant, form.ClientSecret) + accessToken, tokenErr := newAccessTokenResponse(grant, signingKey) if tokenErr != nil { handleAccessTokenError(ctx, *tokenErr) return @@ -535,7 +603,7 @@ func handleRefreshToken(ctx *context.Context, form forms.AccessTokenForm) { ctx.JSON(http.StatusOK, accessToken) } -func handleAuthorizationCode(ctx *context.Context, form forms.AccessTokenForm) { +func handleAuthorizationCode(ctx *context.Context, form forms.AccessTokenForm, signingKey oauth2.JWTSigningKey) { app, err := models.GetOAuth2ApplicationByClientID(form.ClientID) if err != nil { handleAccessTokenError(ctx, AccessTokenError{ @@ -589,7 +657,7 @@ func handleAuthorizationCode(ctx *context.Context, form forms.AccessTokenForm) { ErrorDescription: "cannot proceed your request", }) } - resp, tokenErr := newAccessTokenResponse(authorizationCode.Grant, form.ClientSecret) + resp, tokenErr := newAccessTokenResponse(authorizationCode.Grant, signingKey) if tokenErr != nil { handleAccessTokenError(ctx, *tokenErr) return diff --git a/routers/user/profile.go b/routers/web/user/profile.go similarity index 99% rename from routers/user/profile.go rename to routers/web/user/profile.go index 8ff1ee24adc80..e66820e1317bc 100644 --- a/routers/user/profile.go +++ b/routers/web/user/profile.go @@ -17,7 +17,7 @@ import ( "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/routers/org" + "code.gitea.io/gitea/routers/web/org" ) // GetUserByName get user by name diff --git a/routers/user/setting/account.go b/routers/web/user/setting/account.go similarity index 100% rename from routers/user/setting/account.go rename to routers/web/user/setting/account.go diff --git a/routers/user/setting/account_test.go b/routers/web/user/setting/account_test.go similarity index 100% rename from routers/user/setting/account_test.go rename to routers/web/user/setting/account_test.go diff --git a/routers/user/setting/adopt.go b/routers/web/user/setting/adopt.go similarity index 100% rename from routers/user/setting/adopt.go rename to routers/web/user/setting/adopt.go diff --git a/routers/user/setting/applications.go b/routers/web/user/setting/applications.go similarity index 100% rename from routers/user/setting/applications.go rename to routers/web/user/setting/applications.go diff --git a/routers/user/setting/keys.go b/routers/web/user/setting/keys.go similarity index 100% rename from routers/user/setting/keys.go rename to routers/web/user/setting/keys.go diff --git a/routers/admin/main_test.go b/routers/web/user/setting/main_test.go similarity index 78% rename from routers/admin/main_test.go rename to routers/web/user/setting/main_test.go index 9a7191d471f54..daa3f7fe5bf16 100644 --- a/routers/admin/main_test.go +++ b/routers/web/user/setting/main_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 admin +package setting import ( "path/filepath" @@ -12,5 +12,5 @@ import ( ) func TestMain(m *testing.M) { - models.MainTest(m, filepath.Join("..", "..")) + models.MainTest(m, filepath.Join("..", "..", "..", "..")) } diff --git a/routers/user/setting/oauth2.go b/routers/web/user/setting/oauth2.go similarity index 100% rename from routers/user/setting/oauth2.go rename to routers/web/user/setting/oauth2.go diff --git a/routers/user/setting/profile.go b/routers/web/user/setting/profile.go similarity index 98% rename from routers/user/setting/profile.go rename to routers/web/user/setting/profile.go index 8cde81f2954b9..20042caca4f72 100644 --- a/routers/user/setting/profile.go +++ b/routers/web/user/setting/profile.go @@ -19,6 +19,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/typesniffer" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/modules/web/middleware" @@ -159,7 +160,9 @@ func UpdateAvatarSetting(ctx *context.Context, form *forms.AvatarForm, ctxUser * if err != nil { return fmt.Errorf("ioutil.ReadAll: %v", err) } - if !base.IsImageFile(data) { + + st := typesniffer.DetectContentType(data) + if !(st.IsImage() && !st.IsSvgImage()) { return errors.New(ctx.Tr("settings.uploaded_avatar_not_a_image")) } if err = ctxUser.UploadAvatar(data); err != nil { diff --git a/routers/user/setting/security.go b/routers/web/user/setting/security.go similarity index 100% rename from routers/user/setting/security.go rename to routers/web/user/setting/security.go diff --git a/routers/user/setting/security_openid.go b/routers/web/user/setting/security_openid.go similarity index 100% rename from routers/user/setting/security_openid.go rename to routers/web/user/setting/security_openid.go diff --git a/routers/user/setting/security_twofa.go b/routers/web/user/setting/security_twofa.go similarity index 100% rename from routers/user/setting/security_twofa.go rename to routers/web/user/setting/security_twofa.go diff --git a/routers/user/setting/security_u2f.go b/routers/web/user/setting/security_u2f.go similarity index 100% rename from routers/user/setting/security_u2f.go rename to routers/web/user/setting/security_u2f.go diff --git a/routers/user/task.go b/routers/web/user/task.go similarity index 55% rename from routers/user/task.go rename to routers/web/user/task.go index b8df5d99c7b40..8e7b66ef9538e 100644 --- a/routers/user/task.go +++ b/routers/web/user/task.go @@ -9,6 +9,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" + jsoniter "github.com/json-iterator/go" ) // TaskStatus returns task's status @@ -21,9 +22,24 @@ func TaskStatus(ctx *context.Context) { return } + message := task.Message + + if task.Message != "" && task.Message[0] == '{' { + // assume message is actually a translatable string + json := jsoniter.ConfigCompatibleWithStandardLibrary + var translatableMessage models.TranslatableMessage + if err := json.Unmarshal([]byte(message), &translatableMessage); err != nil { + translatableMessage = models.TranslatableMessage{ + Format: "migrate.migrating_failed.error", + Args: []interface{}{task.Message}, + } + } + message = ctx.Tr(translatableMessage.Format, translatableMessage.Args...) + } + ctx.JSON(http.StatusOK, map[string]interface{}{ "status": task.Status, - "err": task.Errors, + "message": message, "repo-id": task.RepoID, "repo-name": opts.RepoName, "start": task.StartTime, diff --git a/routers/routes/web.go b/routers/web/web.go similarity index 92% rename from routers/routes/web.go rename to routers/web/web.go index 6d91eb1b3c80e..2c8a6411a1d10 100644 --- a/routers/routes/web.go +++ b/routers/web/web.go @@ -2,15 +2,13 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package routes +package web import ( "encoding/gob" - "fmt" "net/http" "os" "path" - "strings" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" @@ -23,17 +21,17 @@ import ( "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" - "code.gitea.io/gitea/routers/private" - "code.gitea.io/gitea/routers/repo" - "code.gitea.io/gitea/routers/user" - userSetting "code.gitea.io/gitea/routers/user/setting" + "code.gitea.io/gitea/routers/common" + "code.gitea.io/gitea/routers/web/admin" + "code.gitea.io/gitea/routers/web/dev" + "code.gitea.io/gitea/routers/web/events" + "code.gitea.io/gitea/routers/web/explore" + "code.gitea.io/gitea/routers/web/org" + "code.gitea.io/gitea/routers/web/repo" + "code.gitea.io/gitea/routers/web/user" + userSetting "code.gitea.io/gitea/routers/web/user/setting" + "code.gitea.io/gitea/services/auth" "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/lfs" "code.gitea.io/gitea/services/mailer" @@ -44,7 +42,6 @@ import ( "gitea.com/go-chi/captcha" "gitea.com/go-chi/session" "github.com/NYTimes/gziphandler" - "github.com/chi-middleware/proxy" "github.com/go-chi/chi/middleware" "github.com/go-chi/cors" "github.com/prometheus/client_golang/prometheus" @@ -56,74 +53,10 @@ const ( GzipMinSize = 1400 ) -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) - }) - }, - } - - if setting.ReverseProxyLimit > 0 { - opt := proxy.NewForwardedHeadersOptions(). - WithForwardLimit(setting.ReverseProxyLimit). - ClearTrustedProxies() - for _, n := range setting.ReverseProxyTrustedProxies { - if !strings.Contains(n, "/") { - opt.AddTrustedProxy(n) - } else { - opt.AddTrustedNetwork(n) - } - } - handlers = append(handlers, proxy.ForwardedHeaders(opt)) - } - - handlers = append(handlers, middleware.StripSlashes) - - if !setting.DisableRouterLog && setting.RouterLogLevel != log.NONE { - if log.GetLogger("router").GetLevel() <= setting.RouterLogLevel { - handlers = append(handlers, LoggerHandler(setting.RouterLogLevel)) - } - } - if setting.EnableAccessLog { - handlers = append(handlers, context.AccessLogger()) - } - - 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) - }) - }) - return handlers -} - -var corsHandler func(http.Handler) http.Handler - -// NormalRoutes represents non install routes -func NormalRoutes() *web.Route { - r := web.NewRoute() - for _, middle := range commonMiddlewares() { - r.Use(middle) - } - +// CorsHandler return a http handler who set CORS options if enabled by config +func CorsHandler() func(next http.Handler) http.Handler { if setting.CORSConfig.Enabled { - corsHandler = cors.Handler(cors.Options{ + return 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 @@ -131,26 +64,21 @@ func NormalRoutes() *web.Route { AllowCredentials: setting.CORSConfig.AllowCredentials, MaxAge: int(setting.CORSConfig.MaxAge.Seconds()), }) - } else { - corsHandler = func(next http.Handler) http.Handler { - return next - } } - r.Mount("/", WebRoutes()) - r.Mount("/api/v1", apiv1.Routes()) - r.Mount("/api/internal", private.Routes()) - return r + return func(next http.Handler) http.Handler { + return next + } } -// WebRoutes returns all web routes -func WebRoutes() *web.Route { +// Routes returns all web routes +func Routes() *web.Route { routes := web.NewRoute() routes.Use(public.AssetsHandler(&public.Options{ Directory: path.Join(setting.StaticRootPath, "public"), Prefix: "/assets", - CorsHandler: corsHandler, + CorsHandler: CorsHandler(), })) routes.Use(session.Sessioner(session.Options{ @@ -216,12 +144,15 @@ func WebRoutes() *web.Route { c := metrics.NewCollector() prometheus.MustRegister(c) - routes.Get("/metrics", append(common, routers.Metrics)...) + routes.Get("/metrics", append(common, Metrics)...) } // Removed: toolbox.Toolboxer middleware will provide debug informations which seems unnecessary common = append(common, context.Contexter()) + // Get user from session if logged in. + common = append(common, context.Auth(auth.NewGroup(auth.Methods()...))) + // GetHead allows a HEAD request redirect to GET if HEAD method is not defined for that route common = append(common, middleware.GetHead) @@ -286,20 +217,27 @@ func RegisterRoutes(m *web.Route) { } } + lfsServerEnabled := func(ctx *context.Context) { + if !setting.LFS.StartServer { + ctx.Error(http.StatusNotFound) + return + } + } + // FIXME: not all routes need go through same middleware. // Especially some AJAX requests, we can reduce middleware number to improve performance. // Routers. // for health check - m.Get("/", routers.Home) + m.Get("/", Home) m.Get("/.well-known/openid-configuration", user.OIDCWellKnown) m.Group("/explore", func() { m.Get("", func(ctx *context.Context) { ctx.Redirect(setting.AppSubURL + "/explore/repos") }) - m.Get("/repos", routers.ExploreRepos) - m.Get("/users", routers.ExploreUsers) - m.Get("/organizations", routers.ExploreOrganizations) - m.Get("/code", routers.ExploreCode) + m.Get("/repos", explore.Repos) + m.Get("/users", explore.Users) + m.Get("/organizations", explore.Organizations) + m.Get("/code", explore.Code) }, ignExploreSignIn) m.Get("/issues", reqSignIn, user.Issues) m.Get("/pulls", reqSignIn, user.Pulls) @@ -356,7 +294,8 @@ func RegisterRoutes(m *web.Route) { m.Post("/authorize", bindIgnErr(forms.AuthorizationForm{}), user.AuthorizeOAuth) }, ignSignInAndCsrf, reqSignIn) m.Get("/login/oauth/userinfo", ignSignInAndCsrf, user.InfoOAuth) - m.Post("/login/oauth/access_token", corsHandler, bindIgnErr(forms.AccessTokenForm{}), ignSignInAndCsrf, user.AccessTokenOAuth) + m.Post("/login/oauth/access_token", CorsHandler(), bindIgnErr(forms.AccessTokenForm{}), ignSignInAndCsrf, user.AccessTokenOAuth) + m.Get("/login/oauth/keys", ignSignInAndCsrf, user.OIDCKeys) m.Group("/user/settings", func() { m.Get("", userSetting.Profile) @@ -949,7 +888,7 @@ func RegisterRoutes(m *web.Route) { }, context.RepoRef(), repo.MustBeNotEmpty, context.RequireRepoReaderOr(models.UnitTypeCode)) m.Group("/archive", func() { - m.Get("/*", repo.Download) + m.Get("/*", common.Download) m.Post("/*", repo.InitiateDownload) }, repo.MustBeNotEmpty, reqRepoCodeReader) @@ -1042,21 +981,21 @@ func RegisterRoutes(m *web.Route) { 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.Post("/objects", lfs.PostHandler) - m.Post("/verify", lfs.VerifyHandler) + m.Post("/objects/batch", lfs.CheckAcceptMediaType, lfs.BatchHandler) + m.Put("/objects/{oid}/{size}", lfs.UploadHandler) + m.Get("/objects/{oid}/{filename}", lfs.DownloadHandler) + m.Get("/objects/{oid}", lfs.DownloadHandler) + m.Post("/verify", lfs.CheckAcceptMediaType, 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) - }) + }, lfs.CheckAcceptMediaType) m.Any("/*", func(ctx *context.Context) { ctx.NotFound("", nil) }) - }, ignSignInAndCsrf) + }, ignSignInAndCsrf, lfsServerEnabled) m.Group("", func() { m.Post("/git-upload-pack", repo.ServiceUploadPack) @@ -1084,9 +1023,6 @@ func RegisterRoutes(m *web.Route) { }, reqSignIn) if setting.API.EnableSwagger { - m.Get("/swagger.v1.json", routers.SwaggerV1Json) + m.Get("/swagger.v1.json", SwaggerV1Json) } - - // Not found handler. - m.NotFound(web.Wrap(routers.NotFound)) } diff --git a/services/archiver/archiver_test.go b/services/archiver/archiver_test.go index 84bab148182a3..6dcd942bf5ef0 100644 --- a/services/archiver/archiver_test.go +++ b/services/archiver/archiver_test.go @@ -35,7 +35,7 @@ func waitForCount(t *testing.T, num int) { } } - assert.Equal(t, num, len(archiveInProgress)) + assert.Len(t, archiveInProgress, num) } func releaseOneEntry(t *testing.T, inFlight []*ArchiveRequest) { @@ -128,7 +128,7 @@ func TestArchive_Basic(t *testing.T) { // Sleep two seconds to make sure the queue doesn't change. time.Sleep(2 * time.Second) - assert.Equal(t, 3, len(archiveInProgress)) + assert.Len(t, archiveInProgress, 3) // Release them all, they'll then stall at the archiveQueueReleaseCond while // we examine the queue state. @@ -160,7 +160,7 @@ func TestArchive_Basic(t *testing.T) { // Queues should not have drained yet, because we haven't released them. // Do so now. - assert.Equal(t, 3, len(archiveInProgress)) + assert.Len(t, archiveInProgress, 3) zipReq2 := DeriveRequestFrom(ctx, firstCommit+".zip") // This zipReq should match what's sitting in the queue, as we haven't @@ -174,15 +174,15 @@ func TestArchive_Basic(t *testing.T) { ArchiveRepository(zipReq2) // Make sure the queue hasn't grown any. - assert.Equal(t, 3, len(archiveInProgress)) + assert.Len(t, archiveInProgress, 3) // Make sure the queue drains properly releaseOneEntry(t, inFlight) - assert.Equal(t, 2, len(archiveInProgress)) + assert.Len(t, archiveInProgress, 2) releaseOneEntry(t, inFlight) - assert.Equal(t, 1, len(archiveInProgress)) + assert.Len(t, archiveInProgress, 1) releaseOneEntry(t, inFlight) - assert.Equal(t, 0, len(archiveInProgress)) + assert.Empty(t, archiveInProgress) // Now we'll submit a request and TimedWaitForCompletion twice, before and // after we release it. We should trigger both the timeout and non-timeout @@ -194,8 +194,8 @@ func TestArchive_Basic(t *testing.T) { // Guaranteed to timeout; we haven't signalled the request to start.. completed, timedout = timedReq.TimedWaitForCompletion(ctx, 2*time.Second) - assert.Equal(t, false, completed) - assert.Equal(t, true, timedout) + assert.False(t, completed) + assert.True(t, timedout) queueMutex.Lock() archiveQueueStartCond.Broadcast() @@ -203,8 +203,8 @@ func TestArchive_Basic(t *testing.T) { // Shouldn't timeout, we've now signalled it and it's a small request. completed, timedout = timedReq.TimedWaitForCompletion(ctx, 15*time.Second) - assert.Equal(t, true, completed) - assert.Equal(t, false, timedout) + assert.True(t, completed) + assert.False(t, timedout) zipReq2 = DeriveRequestFrom(ctx, firstCommit+".zip") // Now, we're guaranteed to have released the original zipReq from the queue. diff --git a/modules/auth/sso/sso.go b/services/auth/auth.go similarity index 74% rename from modules/auth/sso/sso.go rename to services/auth/auth.go index 8543ceb2ffc0b..5492a8b74ede3 100644 --- a/modules/auth/sso/sso.go +++ b/services/auth/auth.go @@ -3,7 +3,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package sso +package auth import ( "fmt" @@ -18,7 +18,7 @@ import ( "code.gitea.io/gitea/modules/web/middleware" ) -// ssoMethods contains the list of SSO authentication plugins in the order they are expected to be +// authMethods contains the list of authentication plugins in the order they are expected to be // executed. // // The OAuth2 plugin is expected to be executed first, as it must ignore the user id stored @@ -27,11 +27,10 @@ import ( // // The Session plugin is expected to be executed second, in order to skip authentication // for users that have already signed in. -var ssoMethods = []SingleSignOn{ +var authMethods = []Auth{ &OAuth2{}, &Basic{}, &Session{}, - &ReverseProxy{}, } // The purpose of the following three function variables is to let the linter know that @@ -40,65 +39,42 @@ var ( _ = handleSignIn ) -// Methods returns the instances of all registered SSO methods -func Methods() []SingleSignOn { - return ssoMethods +// Methods returns the instances of all registered methods +func Methods() []Auth { + return authMethods } -// Register adds the specified instance to the list of available SSO methods -func Register(method SingleSignOn) { - ssoMethods = append(ssoMethods, method) +// Register adds the specified instance to the list of available methods +func Register(method Auth) { + authMethods = append(authMethods, method) } -// Init should be called exactly once when the application starts to allow SSO plugins +// Init should be called exactly once when the application starts to allow plugins // to allocate necessary resources func Init() { + if setting.Service.EnableReverseProxyAuth { + Register(&ReverseProxy{}) + } + specialInit() for _, method := range Methods() { err := method.Init() if err != nil { - log.Error("Could not initialize '%s' SSO method, error: %s", reflect.TypeOf(method).String(), err) + log.Error("Could not initialize '%s' auth method, error: %s", reflect.TypeOf(method).String(), err) } } } -// Free should be called exactly once when the application is terminating to allow SSO plugins +// Free should be called exactly once when the application is terminating to allow Auth plugins // to release necessary resources func Free() { for _, method := range Methods() { err := method.Free() if err != nil { - log.Error("Could not free '%s' SSO method, error: %s", reflect.TypeOf(method).String(), err) + log.Error("Could not free '%s' auth method, error: %s", reflect.TypeOf(method).String(), err) } } } -// SessionUser returns the user object corresponding to the "uid" session variable. -func SessionUser(sess SessionStore) *models.User { - // Get user ID - uid := sess.Get("uid") - if uid == nil { - return nil - } - log.Trace("Session Authorization: Found user[%d]", uid) - - id, ok := uid.(int64) - if !ok { - return nil - } - - // Get user object - user, err := models.GetUserByID(id) - if err != nil { - if !models.IsErrUserNotExist(err) { - log.Error("GetUserById: %v", err) - } - return nil - } - - log.Trace("Session Authorization: Logged in user %-v", user) - return user -} - // isAttachmentDownload check if request is a file download (GET) with URL to an attachment func isAttachmentDownload(req *http.Request) bool { return strings.HasPrefix(req.URL.Path, "/attachments/") && req.Method == "GET" diff --git a/modules/auth/sso/sso_test.go b/services/auth/auth_test.go similarity index 99% rename from modules/auth/sso/sso_test.go rename to services/auth/auth_test.go index e57788f35aecc..f6b43835f45f3 100644 --- a/modules/auth/sso/sso_test.go +++ b/services/auth/auth_test.go @@ -3,7 +3,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package sso +package auth import ( "net/http" diff --git a/modules/auth/sso/basic.go b/services/auth/basic.go similarity index 83% rename from modules/auth/sso/basic.go rename to services/auth/basic.go index 555128812851f..0bce4f1d067a2 100644 --- a/modules/auth/sso/basic.go +++ b/services/auth/basic.go @@ -3,7 +3,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package sso +package auth import ( "net/http" @@ -19,15 +19,20 @@ import ( // Ensure the struct implements the interface. var ( - _ SingleSignOn = &Basic{} + _ Auth = &Basic{} ) -// Basic implements the SingleSignOn interface and authenticates requests (API requests +// Basic implements the Auth interface and authenticates requests (API requests // only) by looking for Basic authentication data or "x-oauth-basic" token in the "Authorization" // header. type Basic struct { } +// Name represents the name of auth method +func (b *Basic) Name() string { + return "basic" +} + // Init does nothing as the Basic implementation does not need to allocate any resources func (b *Basic) Init() error { return nil @@ -38,20 +43,13 @@ func (b *Basic) Free() error { return nil } -// IsEnabled returns true as this plugin is enabled by default and its not possible to disable -// it from settings. -func (b *Basic) IsEnabled() bool { - return true -} - -// VerifyAuthData extracts and validates Basic data (username and password/token) from the +// Verify extracts and validates Basic data (username and password/token) from the // "Authorization" header of the request and returns the corresponding user object for that // name/token on successful validation. // Returns nil if header is empty or validation fails. -func (b *Basic) VerifyAuthData(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *models.User { - +func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *models.User { // Basic authentication should only fire on API, Download or on Git or LFSPaths - if middleware.IsInternalPath(req) || !middleware.IsAPIPath(req) && !isAttachmentDownload(req) && !isGitRawOrLFSPath(req) { + if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) && !isGitRawOrLFSPath(req) { return nil } diff --git a/services/auth/group.go b/services/auth/group.go new file mode 100644 index 0000000000000..b61949de7dea7 --- /dev/null +++ b/services/auth/group.go @@ -0,0 +1,73 @@ +// 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 auth + +import ( + "net/http" + + "code.gitea.io/gitea/models" +) + +// Ensure the struct implements the interface. +var ( + _ Auth = &Group{} +) + +// Group implements the Auth interface with serval Auth. +type Group struct { + methods []Auth +} + +// NewGroup creates a new auth group +func NewGroup(methods ...Auth) *Group { + return &Group{ + methods: methods, + } +} + +// Name represents the name of auth method +func (b *Group) Name() string { + return "group" +} + +// Init does nothing as the Basic implementation does not need to allocate any resources +func (b *Group) Init() error { + for _, m := range b.methods { + if err := m.Init(); err != nil { + return err + } + } + return nil +} + +// Free does nothing as the Basic implementation does not have to release any resources +func (b *Group) Free() error { + for _, m := range b.methods { + if err := m.Free(); err != nil { + return err + } + } + return nil +} + +// Verify extracts and validates +func (b *Group) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *models.User { + if !models.HasEngine { + return nil + } + + // Try to sign in with each of the enabled plugins + for _, ssoMethod := range b.methods { + user := ssoMethod.Verify(req, w, store, sess) + if user != nil { + if store.GetData()["AuthedMethod"] == nil { + store.GetData()["AuthedMethod"] = ssoMethod.Name() + } + return user + } + } + + return nil +} diff --git a/modules/auth/sso/interface.go b/services/auth/interface.go similarity index 71% rename from modules/auth/sso/interface.go rename to services/auth/interface.go index 9b1472f2b37fb..a305bdfc226c7 100644 --- a/modules/auth/sso/interface.go +++ b/services/auth/interface.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 sso +package auth import ( "net/http" @@ -18,8 +18,10 @@ type DataStore middleware.DataStore // SessionStore represents a session store type SessionStore session.Store -// SingleSignOn represents a SSO authentication method (plugin) for HTTP requests. -type SingleSignOn interface { +// Auth represents an authentication method (plugin) for HTTP requests. +type Auth interface { + Name() string + // Init should be called exactly once before using any of the other methods, // in order to allow the plugin to allocate necessary resources Init() error @@ -28,13 +30,10 @@ type SingleSignOn interface { // give chance to the plugin to free any allocated resources Free() error - // IsEnabled checks if the current SSO method has been enabled in settings. - IsEnabled() bool - - // VerifyAuthData tries to verify the SSO authentication data contained in the request. + // Verify tries to verify the authentication data contained in the request. // If verification is successful returns either an existing user object (with id > 0) // or a new user object (with id = 0) populated with the information that was found // in the authentication data (username or email). // Returns nil if verification fails. - VerifyAuthData(http *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *models.User + Verify(http *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *models.User } diff --git a/modules/auth/sso/oauth2.go b/services/auth/oauth2.go similarity index 85% rename from modules/auth/sso/oauth2.go rename to services/auth/oauth2.go index b052b5599a885..c6b98c144f0ef 100644 --- a/modules/auth/sso/oauth2.go +++ b/services/auth/oauth2.go @@ -3,7 +3,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package sso +package auth import ( "net/http" @@ -18,7 +18,7 @@ import ( // Ensure the struct implements the interface. var ( - _ SingleSignOn = &OAuth2{} + _ Auth = &OAuth2{} ) // CheckOAuthAccessToken returns uid of user from oauth token @@ -45,7 +45,7 @@ func CheckOAuthAccessToken(accessToken string) int64 { return grant.UserID } -// OAuth2 implements the SingleSignOn interface and authenticates requests +// OAuth2 implements the Auth interface and authenticates requests // (API requests only) by looking for an OAuth token in query parameters or the // "Authorization" header. type OAuth2 struct { @@ -56,6 +56,11 @@ func (o *OAuth2) Init() error { return nil } +// Name represents the name of auth method +func (o *OAuth2) Name() string { + return "oauth2" +} + // Free does nothing as the OAuth2 implementation does not have to release any resources func (o *OAuth2) Free() error { return nil @@ -107,22 +112,16 @@ func (o *OAuth2) userIDFromToken(req *http.Request, store DataStore) int64 { return t.UID } -// IsEnabled returns true as this plugin is enabled by default and its not possible -// to disable it from settings. -func (o *OAuth2) IsEnabled() bool { - return true -} - -// VerifyAuthData extracts the user ID from the OAuth token in the query parameters +// Verify extracts the user ID from the OAuth token in the query parameters // or the "Authorization" header and returns the corresponding user object for that ID. // If verification is successful returns an existing user object. // Returns nil if verification fails. -func (o *OAuth2) VerifyAuthData(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *models.User { +func (o *OAuth2) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *models.User { if !models.HasEngine { return nil } - if middleware.IsInternalPath(req) || !middleware.IsAPIPath(req) && !isAttachmentDownload(req) { + if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) { return nil } diff --git a/services/auth/placeholder.go b/services/auth/placeholder.go new file mode 100644 index 0000000000000..50e3061885f80 --- /dev/null +++ b/services/auth/placeholder.go @@ -0,0 +1,9 @@ +// 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. + +// +build !windows + +package auth + +func specialInit() {} diff --git a/modules/auth/sso/reverseproxy.go b/services/auth/reverseproxy.go similarity index 87% rename from modules/auth/sso/reverseproxy.go rename to services/auth/reverseproxy.go index 3fffee0320d23..f958d28c9a664 100644 --- a/modules/auth/sso/reverseproxy.go +++ b/services/auth/reverseproxy.go @@ -3,7 +3,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package sso +package auth import ( "net/http" @@ -19,10 +19,10 @@ import ( // Ensure the struct implements the interface. var ( - _ SingleSignOn = &ReverseProxy{} + _ Auth = &ReverseProxy{} ) -// ReverseProxy implements the SingleSignOn interface, but actually relies on +// ReverseProxy implements the Auth interface, but actually relies on // a reverse proxy for authentication of users. // On successful authentication the proxy is expected to populate the username in the // "setting.ReverseProxyAuthUser" header. Optionally it can also populate the email of the @@ -39,6 +39,11 @@ func (r *ReverseProxy) getUserName(req *http.Request) string { return webAuthUser } +// Name represents the name of auth method +func (r *ReverseProxy) Name() string { + return "reverse_proxy" +} + // Init does nothing as the ReverseProxy implementation does not need initialization func (r *ReverseProxy) Init() error { return nil @@ -49,19 +54,14 @@ func (r *ReverseProxy) Free() error { return nil } -// IsEnabled checks if EnableReverseProxyAuth setting is true -func (r *ReverseProxy) IsEnabled() bool { - return setting.Service.EnableReverseProxyAuth -} - -// VerifyAuthData extracts the username from the "setting.ReverseProxyAuthUser" header +// Verify extracts the username from the "setting.ReverseProxyAuthUser" header // of the request and returns the corresponding user object for that name. // Verification of header data is not performed as it should have already been done by // the revese proxy. // If a username is available in the "setting.ReverseProxyAuthUser" header an existing // user object is returned (populated with username or email found in header). // Returns nil if header is empty. -func (r *ReverseProxy) VerifyAuthData(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *models.User { +func (r *ReverseProxy) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *models.User { username := r.getUserName(req) if len(username) == 0 { return nil diff --git a/services/auth/session.go b/services/auth/session.go new file mode 100644 index 0000000000000..9f08f43363009 --- /dev/null +++ b/services/auth/session.go @@ -0,0 +1,75 @@ +// 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 auth + +import ( + "net/http" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/log" +) + +// Ensure the struct implements the interface. +var ( + _ Auth = &Session{} +) + +// Session checks if there is a user uid stored in the session and returns the user +// object for that uid. +type Session struct { +} + +// Init does nothing as the Session implementation does not need to allocate any resources +func (s *Session) Init() error { + return nil +} + +// Name represents the name of auth method +func (s *Session) Name() string { + return "session" +} + +// Free does nothing as the Session implementation does not have to release any resources +func (s *Session) Free() error { + return nil +} + +// Verify checks if there is a user uid stored in the session and returns the user +// object for that uid. +// Returns nil if there is no user uid stored in the session. +func (s *Session) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *models.User { + user := SessionUser(sess) + if user != nil { + return user + } + return nil +} + +// SessionUser returns the user object corresponding to the "uid" session variable. +func SessionUser(sess SessionStore) *models.User { + // Get user ID + uid := sess.Get("uid") + if uid == nil { + return nil + } + log.Trace("Session Authorization: Found user[%d]", uid) + + id, ok := uid.(int64) + if !ok { + return nil + } + + // Get user object + user, err := models.GetUserByID(id) + if err != nil { + if !models.IsErrUserNotExist(err) { + log.Error("GetUserById: %v", err) + } + return nil + } + + log.Trace("Session Authorization: Logged in user %-v", user) + return user +} diff --git a/modules/auth/sso/sspi_windows.go b/services/auth/sspi_windows.go similarity index 92% rename from modules/auth/sso/sspi_windows.go rename to services/auth/sspi_windows.go index 2092a5e28976b..d1289a76174b2 100644 --- a/modules/auth/sso/sspi_windows.go +++ b/services/auth/sspi_windows.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 sso +package auth import ( "errors" @@ -32,7 +32,7 @@ var ( sspiAuth *websspi.Authenticator // Ensure the struct implements the interface. - _ SingleSignOn = &SSPI{} + _ Auth = &SSPI{} ) // SSPI implements the SingleSignOn interface and authenticates requests @@ -62,21 +62,21 @@ func (s *SSPI) Init() error { return nil } +// Name represents the name of auth method +func (s *SSPI) Name() string { + return "sspi" +} + // Free releases resources used by the global websspi.Authenticator object func (s *SSPI) Free() error { return sspiAuth.Free() } -// IsEnabled checks if there is an active SSPI authentication source -func (s *SSPI) IsEnabled() bool { - return models.IsSSPIEnabled() -} - -// VerifyAuthData uses SSPI (Windows implementation of SPNEGO) to authenticate the request. +// Verify uses SSPI (Windows implementation of SPNEGO) to authenticate the request. // If authentication is successful, returs the corresponding user object. // If negotiation should continue or authentication fails, immediately returns a 401 HTTP // response code, as required by the SPNEGO protocol. -func (s *SSPI) VerifyAuthData(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *models.User { +func (s *SSPI) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *models.User { if !s.shouldAuthenticate(req) { return nil } @@ -169,8 +169,6 @@ func (s *SSPI) shouldAuthenticate(req *http.Request) (shouldAuth bool) { } else if req.FormValue("auth_with_sspi") == "1" { shouldAuth = true } - } else if middleware.IsInternalPath(req) { - shouldAuth = false } else if middleware.IsAPIPath(req) || isAttachmentDownload(req) { shouldAuth = true } @@ -237,10 +235,12 @@ func sanitizeUsername(username string, cfg *models.SSPIConfig) string { return username } -// init registers the SSPI auth method as the last method in the list. +// specialInit registers the SSPI auth method as the last method in the list. // The SSPI plugin is expected to be executed last, as it returns 401 status code if negotiation // fails (or if negotiation should continue), which would prevent other authentication methods // to execute at all. -func init() { - Register(&SSPI{}) +func specialInit() { + if models.IsSSPIEnabled() { + Register(&SSPI{}) + } } diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index 55d1f6e3bc386..71a83a8be36e2 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -113,18 +113,23 @@ func ParseRemoteAddr(remoteAddr, authUsername, authPassword string) (string, err // RepoSettingForm form for changing repository settings type RepoSettingForm struct { - RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"` - Description string `binding:"MaxSize(255)"` - Website string `binding:"ValidUrl;MaxSize(255)"` - Interval string - MirrorAddress string - MirrorUsername string - MirrorPassword string - LFS bool `form:"mirror_lfs"` - LFSEndpoint string `form:"mirror_lfs_endpoint"` - Private bool - Template bool - EnablePrune bool + RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"` + Description string `binding:"MaxSize(255)"` + Website string `binding:"ValidUrl;MaxSize(255)"` + Interval string + MirrorAddress string + MirrorUsername string + MirrorPassword string + LFS bool `form:"mirror_lfs"` + LFSEndpoint string `form:"mirror_lfs_endpoint"` + PushMirrorID string + PushMirrorAddress string + PushMirrorUsername string + PushMirrorPassword string + PushMirrorInterval string + Private bool + Template bool + EnablePrune bool // Advanced settings EnableWiki bool @@ -582,6 +587,7 @@ type SubmitReviewForm struct { Content string Type string `binding:"Required;In(approve,comment,reject)"` CommitID string + Files []string } // Validate validates the fields diff --git a/services/gitdiff/csv_test.go b/services/gitdiff/csv_test.go index f3dc0c2a2c46d..fb84d6ed06320 100644 --- a/services/gitdiff/csv_test.go +++ b/services/gitdiff/csv_test.go @@ -110,13 +110,13 @@ func TestCSVDiff(t *testing.T) { result, err := CreateCsvDiff(diff.Files[0], baseReader, headReader) assert.NoError(t, err) - assert.Equal(t, 1, len(result), "case %d: should be one section", n) + assert.Len(t, result, 1, "case %d: should be one section", n) section := result[0] - assert.Equal(t, len(c.cells), len(section.Rows), "case %d: should be %d rows", n, len(c.cells)) + assert.Len(t, section.Rows, len(c.cells), "case %d: should be %d rows", n, len(c.cells)) for i, row := range section.Rows { - assert.Equal(t, 2, len(row.Cells), "case %d: row %d should have two cells", n, i) + assert.Len(t, row.Cells, 2, "case %d: row %d should have two cells", n, i) for j, cell := range row.Cells { assert.Equal(t, c.cells[i][j], cell.Type, "case %d: row %d cell %d should be equal", n, i, j) } diff --git a/services/issue/assignee_test.go b/services/issue/assignee_test.go index bdd2009bf0a81..2d96368ec705f 100644 --- a/services/issue/assignee_test.go +++ b/services/issue/assignee_test.go @@ -33,5 +33,5 @@ func TestDeleteNotPassedAssignee(t *testing.T) { // Check they're gone assignees, err := models.GetAssigneesByIssue(issue) assert.NoError(t, err) - assert.Equal(t, 0, len(assignees)) + assert.Empty(t, assignees) } diff --git a/services/lfs/locks.go b/services/lfs/locks.go index ad204c46e2b26..20ba12e65bb20 100644 --- a/services/lfs/locks.go +++ b/services/lfs/locks.go @@ -19,21 +19,6 @@ import ( jsoniter "github.com/json-iterator/go" ) -//checkIsValidRequest check if it a valid request in case of bad request it write the response to ctx. -func checkIsValidRequest(ctx *context.Context) bool { - if !setting.LFS.StartServer { - log.Debug("Attempt to access LFS server but LFS server is disabled") - writeStatus(ctx, http.StatusNotFound) - return false - } - if !MetaMatcher(ctx.Req) { - log.Info("Attempt access LOCKs without accepting the correct media type: %s", lfs_module.MediaType) - writeStatus(ctx, http.StatusBadRequest) - return false - } - return true -} - func handleLockListOut(ctx *context.Context, repo *models.Repository, lock *models.LFSLock, err error) { if err != nil { if models.IsErrLFSLockNotExist(err) { @@ -60,12 +45,7 @@ func handleLockListOut(ctx *context.Context, repo *models.Repository, lock *mode // GetListLockHandler list locks func GetListLockHandler(ctx *context.Context) { - if !checkIsValidRequest(ctx) { - // Status is written in checkIsValidRequest - return - } - - rv, _ := unpack(ctx) + rv := getRequestContext(ctx) repository, err := models.GetRepositoryByOwnerAndName(rv.User, rv.Repo) if err != nil { @@ -150,11 +130,6 @@ func GetListLockHandler(ctx *context.Context) { // PostLockHandler create lock func PostLockHandler(ctx *context.Context) { - if !checkIsValidRequest(ctx) { - // Status is written in checkIsValidRequest - return - } - userName := ctx.Params("username") repoName := strings.TrimSuffix(ctx.Params("reponame"), ".git") authorization := ctx.Req.Header.Get("Authorization") @@ -223,11 +198,6 @@ func PostLockHandler(ctx *context.Context) { // VerifyLockHandler list locks for verification func VerifyLockHandler(ctx *context.Context) { - if !checkIsValidRequest(ctx) { - // Status is written in checkIsValidRequest - return - } - userName := ctx.Params("username") repoName := strings.TrimSuffix(ctx.Params("reponame"), ".git") authorization := ctx.Req.Header.Get("Authorization") @@ -294,11 +264,6 @@ func VerifyLockHandler(ctx *context.Context) { // UnLockHandler delete locks func UnLockHandler(ctx *context.Context) { - if !checkIsValidRequest(ctx) { - // Status is written in checkIsValidRequest - return - } - userName := ctx.Params("username") repoName := strings.TrimSuffix(ctx.Params("reponame"), ".git") authorization := ctx.Req.Header.Get("Authorization") diff --git a/services/lfs/server.go b/services/lfs/server.go index ee7d3bc79a3ee..9954534b5e9ae 100644 --- a/services/lfs/server.go +++ b/services/lfs/server.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Gitea Authors. All rights reserved. +// 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. @@ -6,6 +6,7 @@ package lfs import ( "encoding/base64" + "errors" "fmt" "io" "net/http" @@ -39,95 +40,51 @@ type Claims struct { jwt.StandardClaims } -// ObjectLink builds a URL linking to the object. -func (rc *requestContext) ObjectLink(oid string) string { - return setting.AppURL + path.Join(rc.User, rc.Repo+".git", "info/lfs/objects", oid) +// DownloadLink builds a URL to download the object. +func (rc *requestContext) DownloadLink(p lfs_module.Pointer) string { + return setting.AppURL + path.Join(rc.User, rc.Repo+".git", "info/lfs/objects", p.Oid) } -// VerifyLink builds a URL for verifying the object. -func (rc *requestContext) VerifyLink() string { - return setting.AppURL + path.Join(rc.User, rc.Repo+".git", "info/lfs/verify") +// UploadLink builds a URL to upload the object. +func (rc *requestContext) UploadLink(p lfs_module.Pointer) string { + return setting.AppURL + path.Join(rc.User, rc.Repo+".git", "info/lfs/objects", p.Oid, strconv.FormatInt(p.Size, 10)) } -var oidRegExp = regexp.MustCompile(`^[A-Fa-f0-9]+$`) - -func isOidValid(oid string) bool { - return oidRegExp.MatchString(oid) +// VerifyLink builds a URL for verifying the object. +func (rc *requestContext) VerifyLink(p lfs_module.Pointer) string { + return setting.AppURL + path.Join(rc.User, rc.Repo+".git", "info/lfs/verify") } -// ObjectOidHandler is the main request routing entry point into LFS server functions -func ObjectOidHandler(ctx *context.Context) { - if !setting.LFS.StartServer { - log.Debug("Attempt to access LFS server but LFS server is disabled") - writeStatus(ctx, 404) - return - } - - if ctx.Req.Method == "GET" || ctx.Req.Method == "HEAD" { - if MetaMatcher(ctx.Req) { - getMetaHandler(ctx) - return - } +// CheckAcceptMediaType checks if the client accepts the LFS media type. +func CheckAcceptMediaType(ctx *context.Context) { + mediaParts := strings.Split(ctx.Req.Header.Get("Accept"), ";") - getContentHandler(ctx) - return - } else if ctx.Req.Method == "PUT" { - PutHandler(ctx) + if mediaParts[0] != lfs_module.MediaType { + log.Trace("Calling a LFS method without accepting the correct media type: %s", lfs_module.MediaType) + writeStatus(ctx, http.StatusUnsupportedMediaType) return } - - log.Warn("Unhandled LFS method: %s for %s/%s OID[%s]", ctx.Req.Method, ctx.Params("username"), ctx.Params("reponame"), ctx.Params("oid")) - writeStatus(ctx, 404) } -func getAuthenticatedRepoAndMeta(ctx *context.Context, rc *requestContext, p lfs_module.Pointer, requireWrite bool) (*models.LFSMetaObject, *models.Repository) { - if !isOidValid(p.Oid) { - log.Info("Attempt to access invalid LFS OID[%s] in %s/%s", p.Oid, rc.User, rc.Repo) - writeStatus(ctx, 404) - return nil, nil - } - - repository, err := models.GetRepositoryByOwnerAndName(rc.User, rc.Repo) - if err != nil { - log.Error("Unable to get repository: %s/%s Error: %v", rc.User, rc.Repo, err) - writeStatus(ctx, 404) - return nil, nil - } - - if !authenticate(ctx, repository, rc.Authorization, false, requireWrite) { - requireAuth(ctx) - return nil, nil - } - - meta, err := repository.GetLFSMetaObjectByOid(p.Oid) - if err != nil { - log.Error("Unable to get LFS OID[%s] Error: %v", p.Oid, err) - writeStatus(ctx, 404) - return nil, nil - } - - return meta, repository -} - -// getContentHandler gets the content from the content store -func getContentHandler(ctx *context.Context) { - rc, p := unpack(ctx) +// DownloadHandler gets the content from the content store +func DownloadHandler(ctx *context.Context) { + rc := getRequestContext(ctx) + p := lfs_module.Pointer{Oid: ctx.Params("oid")} - meta, _ := getAuthenticatedRepoAndMeta(ctx, rc, p, false) + meta := getAuthenticatedMeta(ctx, rc, p, false) if meta == nil { - // Status already written in getAuthenticatedRepoAndMeta return } // Support resume download using Range header var fromByte, toByte int64 toByte = meta.Size - 1 - statusCode := 200 + statusCode := http.StatusOK if rangeHdr := ctx.Req.Header.Get("Range"); rangeHdr != "" { regex := regexp.MustCompile(`bytes=(\d+)\-(\d*).*`) match := regex.FindStringSubmatch(rangeHdr) if len(match) > 1 { - statusCode = 206 + statusCode = http.StatusPartialContent fromByte, _ = strconv.ParseInt(match[1], 10, 32) if fromByte >= meta.Size { @@ -150,7 +107,6 @@ func getContentHandler(ctx *context.Context) { contentStore := lfs_module.NewContentStore() content, err := contentStore.Get(meta.Pointer) if err != nil { - // Errors are logged in contentStore.Get writeStatus(ctx, http.StatusNotFound) return } @@ -183,380 +139,300 @@ func getContentHandler(ctx *context.Context) { if written, err := io.CopyN(ctx.Resp, content, contentLength); err != nil { log.Error("Error whilst copying LFS OID[%s] to the response after %d bytes. Error: %v", meta.Oid, written, err) } - logRequest(ctx.Req, statusCode) -} - -// getMetaHandler retrieves metadata about the object -func getMetaHandler(ctx *context.Context) { - rc, p := unpack(ctx) - - meta, _ := getAuthenticatedRepoAndMeta(ctx, rc, p, false) - if meta == nil { - // Status already written in getAuthenticatedRepoAndMeta - return - } - - ctx.Resp.Header().Set("Content-Type", lfs_module.MediaType) - - if ctx.Req.Method == "GET" { - json := jsoniter.ConfigCompatibleWithStandardLibrary - enc := json.NewEncoder(ctx.Resp) - if err := enc.Encode(represent(rc, meta.Pointer, true, false)); err != nil { - log.Error("Failed to encode representation as json. Error: %v", err) - } - } - - logRequest(ctx.Req, 200) } -// PostHandler instructs the client how to upload data -func PostHandler(ctx *context.Context) { - if !setting.LFS.StartServer { - log.Debug("Attempt to access LFS server but LFS server is disabled") - writeStatus(ctx, 404) - return - } - - if !MetaMatcher(ctx.Req) { - log.Info("Attempt to POST without accepting the correct media type: %s", lfs_module.MediaType) - writeStatus(ctx, 400) - return - } - - rc, p := unpack(ctx) - - repository, err := models.GetRepositoryByOwnerAndName(rc.User, rc.Repo) - if err != nil { - log.Error("Unable to get repository: %s/%s Error: %v", rc.User, rc.Repo, err) - writeStatus(ctx, 404) - return - } - - if !authenticate(ctx, repository, rc.Authorization, false, true) { - requireAuth(ctx) +// BatchHandler provides the batch api +func BatchHandler(ctx *context.Context) { + var br lfs_module.BatchRequest + if err := decodeJSON(ctx.Req, &br); err != nil { + log.Trace("Unable to decode BATCH request vars: Error: %v", err) + writeStatus(ctx, http.StatusBadRequest) return } - if !isOidValid(p.Oid) { - log.Info("Invalid LFS OID[%s] attempt to POST in %s/%s", p.Oid, rc.User, rc.Repo) - writeStatus(ctx, 404) + var isUpload bool + if br.Operation == "upload" { + isUpload = true + } else if br.Operation == "download" { + isUpload = false + } else { + log.Trace("Attempt to BATCH with invalid operation: %s", br.Operation) + writeStatus(ctx, http.StatusBadRequest) return } - if setting.LFS.MaxFileSize > 0 && p.Size > setting.LFS.MaxFileSize { - log.Info("Denied LFS OID[%s] upload of size %d to %s/%s because of LFS_MAX_FILE_SIZE=%d", p.Oid, p.Size, rc.User, rc.Repo, setting.LFS.MaxFileSize) - writeStatus(ctx, 413) - return - } + rc := getRequestContext(ctx) - meta, err := models.NewLFSMetaObject(&models.LFSMetaObject{Pointer: p, RepositoryID: repository.ID}) - if err != nil { - log.Error("Unable to write LFS OID[%s] size %d meta object in %v/%v to database. Error: %v", p.Oid, p.Size, rc.User, rc.Repo, err) - writeStatus(ctx, 404) + repository := getAuthenticatedRepository(ctx, rc, isUpload) + if repository == nil { return } - ctx.Resp.Header().Set("Content-Type", lfs_module.MediaType) - - sentStatus := 202 contentStore := lfs_module.NewContentStore() - exist, err := contentStore.Exists(p) - if err != nil { - log.Error("Unable to check if LFS OID[%s] exist on %s / %s. Error: %v", p.Oid, rc.User, rc.Repo, err) - writeStatus(ctx, 500) - return - } - if meta.Existing && exist { - sentStatus = 200 - } - ctx.Resp.WriteHeader(sentStatus) - - json := jsoniter.ConfigCompatibleWithStandardLibrary - enc := json.NewEncoder(ctx.Resp) - if err := enc.Encode(represent(rc, meta.Pointer, meta.Existing, true)); err != nil { - log.Error("Failed to encode representation as json. Error: %v", err) - } - logRequest(ctx.Req, sentStatus) -} - -// BatchHandler provides the batch api -func BatchHandler(ctx *context.Context) { - if !setting.LFS.StartServer { - log.Debug("Attempt to access LFS server but LFS server is disabled") - writeStatus(ctx, 404) - return - } - - if !MetaMatcher(ctx.Req) { - log.Info("Attempt to BATCH without accepting the correct media type: %s", lfs_module.MediaType) - writeStatus(ctx, 400) - return - } - - bv := unpackbatch(ctx) - - reqCtx := &requestContext{ - User: ctx.Params("username"), - Repo: strings.TrimSuffix(ctx.Params("reponame"), ".git"), - Authorization: ctx.Req.Header.Get("Authorization"), - } var responseObjects []*lfs_module.ObjectResponse - // Create a response object - for _, object := range bv.Objects { - if !isOidValid(object.Oid) { - log.Info("Invalid LFS OID[%s] attempt to BATCH in %s/%s", object.Oid, reqCtx.User, reqCtx.Repo) + for _, p := range br.Objects { + if !p.IsValid() { + responseObjects = append(responseObjects, buildObjectResponse(rc, p, false, false, &lfs_module.ObjectError{ + Code: http.StatusUnprocessableEntity, + Message: "Oid or size are invalid", + })) continue } - repository, err := models.GetRepositoryByOwnerAndName(reqCtx.User, reqCtx.Repo) + exists, err := contentStore.Exists(p) if err != nil { - log.Error("Unable to get repository: %s/%s Error: %v", reqCtx.User, reqCtx.Repo, err) - writeStatus(ctx, 404) + log.Error("Unable to check if LFS OID[%s] exist. Error: %v", p.Oid, rc.User, rc.Repo, err) + writeStatus(ctx, http.StatusInternalServerError) return } - requireWrite := false - if bv.Operation == "upload" { - requireWrite = true - } - - if !authenticate(ctx, repository, reqCtx.Authorization, false, requireWrite) { - requireAuth(ctx) + meta, err := repository.GetLFSMetaObjectByOid(p.Oid) + if err != nil && err != models.ErrLFSObjectNotExist { + log.Error("Unable to get LFS MetaObject [%s] for %s/%s. Error: %v", p.Oid, rc.User, rc.Repo, err) + writeStatus(ctx, http.StatusInternalServerError) return } - contentStore := lfs_module.NewContentStore() - - meta, err := repository.GetLFSMetaObjectByOid(object.Oid) - if err == nil { // Object is found and exists - exist, err := contentStore.Exists(meta.Pointer) - if err != nil { - log.Error("Unable to check if LFS OID[%s] exist on %s / %s. Error: %v", object.Oid, reqCtx.User, reqCtx.Repo, err) - writeStatus(ctx, 500) - return - } - if exist { - responseObjects = append(responseObjects, represent(reqCtx, meta.Pointer, true, false)) - continue - } + if meta != nil && p.Size != meta.Size { + responseObjects = append(responseObjects, buildObjectResponse(rc, p, false, false, &lfs_module.ObjectError{ + Code: http.StatusUnprocessableEntity, + Message: fmt.Sprintf("Object %s is not %d bytes", p.Oid, p.Size), + })) + continue } - if requireWrite && setting.LFS.MaxFileSize > 0 && object.Size > setting.LFS.MaxFileSize { - log.Info("Denied LFS OID[%s] upload of size %d to %s/%s because of LFS_MAX_FILE_SIZE=%d", object.Oid, object.Size, reqCtx.User, reqCtx.Repo, setting.LFS.MaxFileSize) - writeStatus(ctx, 413) - return - } + var responseObject *lfs_module.ObjectResponse + if isUpload { + var err *lfs_module.ObjectError + if !exists && setting.LFS.MaxFileSize > 0 && p.Size > setting.LFS.MaxFileSize { + err = &lfs_module.ObjectError{ + Code: http.StatusUnprocessableEntity, + Message: fmt.Sprintf("Size must be less than or equal to %d", setting.LFS.MaxFileSize), + } + } - // Object is not found - meta, err = models.NewLFSMetaObject(&models.LFSMetaObject{Pointer: object, RepositoryID: repository.ID}) - if err == nil { - exist, err := contentStore.Exists(meta.Pointer) - if err != nil { - log.Error("Unable to check if LFS OID[%s] exist on %s / %s. Error: %v", object.Oid, reqCtx.User, reqCtx.Repo, err) - writeStatus(ctx, 500) - return + if exists { + if meta == nil { + _, err := models.NewLFSMetaObject(&models.LFSMetaObject{Pointer: p, RepositoryID: repository.ID}) + if err != nil { + log.Error("Unable to create LFS MetaObject [%s] for %s/%s. Error: %v", p.Oid, rc.User, rc.Repo, err) + writeStatus(ctx, http.StatusInternalServerError) + return + } + } } - responseObjects = append(responseObjects, represent(reqCtx, meta.Pointer, meta.Existing, !exist)) + + responseObject = buildObjectResponse(rc, p, false, !exists, err) } else { - log.Error("Unable to write LFS OID[%s] size %d meta object in %v/%v to database. Error: %v", object.Oid, object.Size, reqCtx.User, reqCtx.Repo, err) + var err *lfs_module.ObjectError + if !exists || meta == nil { + err = &lfs_module.ObjectError{ + Code: http.StatusNotFound, + Message: http.StatusText(http.StatusNotFound), + } + } + + responseObject = buildObjectResponse(rc, p, true, false, err) } + responseObjects = append(responseObjects, responseObject) } - ctx.Resp.Header().Set("Content-Type", lfs_module.MediaType) - respobj := &lfs_module.BatchResponse{Objects: responseObjects} - json := jsoniter.ConfigCompatibleWithStandardLibrary - enc := json.NewEncoder(ctx.Resp) + ctx.Resp.Header().Set("Content-Type", lfs_module.MediaType) + + enc := jsoniter.NewEncoder(ctx.Resp) if err := enc.Encode(respobj); err != nil { log.Error("Failed to encode representation as json. Error: %v", err) } - logRequest(ctx.Req, 200) } -// PutHandler receives data from the client and puts it into the content store -func PutHandler(ctx *context.Context) { - rc, p := unpack(ctx) +// UploadHandler receives data from the client and puts it into the content store +func UploadHandler(ctx *context.Context) { + rc := getRequestContext(ctx) - meta, repository := getAuthenticatedRepoAndMeta(ctx, rc, p, true) - if meta == nil { - // Status already written in getAuthenticatedRepoAndMeta + p := lfs_module.Pointer{Oid: ctx.Params("oid")} + var err error + if p.Size, err = strconv.ParseInt(ctx.Params("size"), 10, 64); err != nil { + writeStatusMessage(ctx, http.StatusUnprocessableEntity, err.Error()) + } + + if !p.IsValid() { + log.Trace("Attempt to access invalid LFS OID[%s] in %s/%s", p.Oid, rc.User, rc.Repo) + writeStatus(ctx, http.StatusUnprocessableEntity) + return + } + + repository := getAuthenticatedRepository(ctx, rc, true) + if repository == nil { + return + } + + meta, err := models.NewLFSMetaObject(&models.LFSMetaObject{Pointer: p, RepositoryID: repository.ID}) + if err != nil { + log.Error("Unable to create LFS MetaObject [%s] for %s/%s. Error: %v", p.Oid, rc.User, rc.Repo, err) + writeStatus(ctx, http.StatusInternalServerError) return } contentStore := lfs_module.NewContentStore() + + exists, err := contentStore.Exists(p) + if err != nil { + log.Error("Unable to check if LFS OID[%s] exist. Error: %v", p.Oid, err) + writeStatus(ctx, http.StatusInternalServerError) + return + } + if meta.Existing || exists { + ctx.Resp.WriteHeader(http.StatusOK) + return + } + defer ctx.Req.Body.Close() if err := contentStore.Put(meta.Pointer, ctx.Req.Body); err != nil { - // Put will log the error itself - ctx.Resp.WriteHeader(500) - if err == lfs_module.ErrSizeMismatch || err == lfs_module.ErrHashMismatch { - fmt.Fprintf(ctx.Resp, `{"message":"%s"}`, err) + if errors.Is(err, lfs_module.ErrSizeMismatch) || errors.Is(err, lfs_module.ErrHashMismatch) { + writeStatusMessage(ctx, http.StatusUnprocessableEntity, err.Error()) } else { - fmt.Fprintf(ctx.Resp, `{"message":"Internal Server Error"}`) + writeStatus(ctx, http.StatusInternalServerError) } if _, err = repository.RemoveLFSMetaObjectByOid(p.Oid); err != nil { - log.Error("Whilst removing metaobject for LFS OID[%s] due to preceding error there was another Error: %v", p.Oid, err) + log.Error("Error whilst removing metaobject for LFS OID[%s]: %v", p.Oid, err) } return } - logRequest(ctx.Req, 200) + writeStatus(ctx, http.StatusOK) } // VerifyHandler verify oid and its size from the content store func VerifyHandler(ctx *context.Context) { - if !setting.LFS.StartServer { - log.Debug("Attempt to access LFS server but LFS server is disabled") - writeStatus(ctx, 404) - return - } - - if !MetaMatcher(ctx.Req) { - log.Info("Attempt to VERIFY without accepting the correct media type: %s", lfs_module.MediaType) - writeStatus(ctx, 400) + var p lfs_module.Pointer + if err := decodeJSON(ctx.Req, &p); err != nil { + writeStatus(ctx, http.StatusUnprocessableEntity) return } - rc, p := unpack(ctx) + rc := getRequestContext(ctx) - meta, _ := getAuthenticatedRepoAndMeta(ctx, rc, p, true) + meta := getAuthenticatedMeta(ctx, rc, p, true) if meta == nil { - // Status already written in getAuthenticatedRepoAndMeta return } contentStore := lfs_module.NewContentStore() ok, err := contentStore.Verify(meta.Pointer) + + status := http.StatusOK if err != nil { - // Error will be logged in Verify - ctx.Resp.WriteHeader(500) - fmt.Fprintf(ctx.Resp, `{"message":"Internal Server Error"}`) - return - } - if !ok { - writeStatus(ctx, 422) - return + status = http.StatusInternalServerError + } else if !ok { + status = http.StatusNotFound } + writeStatus(ctx, status) +} + +func decodeJSON(req *http.Request, v interface{}) error { + defer req.Body.Close() - logRequest(ctx.Req, 200) + dec := jsoniter.NewDecoder(req.Body) + return dec.Decode(v) } -// represent takes a requestContext and Meta and turns it into a ObjectResponse suitable -// for json encoding -func represent(rc *requestContext, pointer lfs_module.Pointer, download, upload bool) *lfs_module.ObjectResponse { - rep := &lfs_module.ObjectResponse{ - Pointer: pointer, - Actions: make(map[string]*lfs_module.Link), +func getRequestContext(ctx *context.Context) *requestContext { + return &requestContext{ + User: ctx.Params("username"), + Repo: strings.TrimSuffix(ctx.Params("reponame"), ".git"), + Authorization: ctx.Req.Header.Get("Authorization"), } +} - header := make(map[string]string) - - if rc.Authorization == "" { - //https://github.com/github/git-lfs/issues/1088 - header["Authorization"] = "Authorization: Basic dummy" - } else { - header["Authorization"] = rc.Authorization +func getAuthenticatedMeta(ctx *context.Context, rc *requestContext, p lfs_module.Pointer, requireWrite bool) *models.LFSMetaObject { + if !p.IsValid() { + log.Info("Attempt to access invalid LFS OID[%s] in %s/%s", p.Oid, rc.User, rc.Repo) + writeStatusMessage(ctx, http.StatusUnprocessableEntity, "Oid or size are invalid") + return nil } - if download { - rep.Actions["download"] = &lfs_module.Link{Href: rc.ObjectLink(pointer.Oid), Header: header} + repository := getAuthenticatedRepository(ctx, rc, requireWrite) + if repository == nil { + return nil } - if upload { - rep.Actions["upload"] = &lfs_module.Link{Href: rc.ObjectLink(pointer.Oid), Header: header} + meta, err := repository.GetLFSMetaObjectByOid(p.Oid) + if err != nil { + log.Error("Unable to get LFS OID[%s] Error: %v", p.Oid, err) + writeStatus(ctx, http.StatusNotFound) + return nil } - if upload && !download { - // Force client side verify action while gitea lacks proper server side verification - verifyHeader := make(map[string]string) - for k, v := range header { - verifyHeader[k] = v - } + return meta +} - // This is only needed to workaround https://github.com/git-lfs/git-lfs/issues/3662 - verifyHeader["Accept"] = lfs_module.MediaType +func getAuthenticatedRepository(ctx *context.Context, rc *requestContext, requireWrite bool) *models.Repository { + repository, err := models.GetRepositoryByOwnerAndName(rc.User, rc.Repo) + if err != nil { + log.Error("Unable to get repository: %s/%s Error: %v", rc.User, rc.Repo, err) + writeStatus(ctx, http.StatusNotFound) + return nil + } - rep.Actions["verify"] = &lfs_module.Link{Href: rc.VerifyLink(), Header: verifyHeader} + if !authenticate(ctx, repository, rc.Authorization, false, requireWrite) { + requireAuth(ctx) + return nil } - return rep + return repository } -// MetaMatcher provides a mux.MatcherFunc that only allows requests that contain -// an Accept header with the lfs_module.MediaType -func MetaMatcher(r *http.Request) bool { - mediaParts := strings.Split(r.Header.Get("Accept"), ";") - mt := mediaParts[0] - return mt == lfs_module.MediaType -} +func buildObjectResponse(rc *requestContext, pointer lfs_module.Pointer, download, upload bool, err *lfs_module.ObjectError) *lfs_module.ObjectResponse { + rep := &lfs_module.ObjectResponse{Pointer: pointer} + if err != nil { + rep.Error = err + } else { + rep.Actions = make(map[string]*lfs_module.Link) -func unpack(ctx *context.Context) (*requestContext, lfs_module.Pointer) { - r := ctx.Req - rc := &requestContext{ - User: ctx.Params("username"), - Repo: strings.TrimSuffix(ctx.Params("reponame"), ".git"), - Authorization: r.Header.Get("Authorization"), - } - p := lfs_module.Pointer{Oid: ctx.Params("oid")} + header := make(map[string]string) - if r.Method == "POST" { // Maybe also check if +json - var p2 lfs_module.Pointer - bodyReader := r.Body - defer bodyReader.Close() - json := jsoniter.ConfigCompatibleWithStandardLibrary - dec := json.NewDecoder(bodyReader) - err := dec.Decode(&p2) - if err != nil { - // The error is logged as a WARN here because this may represent misbehaviour rather than a true error - log.Warn("Unable to decode POST request vars for LFS OID[%s] in %s/%s: Error: %v", p.Oid, rc.User, rc.Repo, err) - return rc, p + if len(rc.Authorization) > 0 { + header["Authorization"] = rc.Authorization } - p.Oid = p2.Oid - p.Size = p2.Size - } - - return rc, p -} + if download { + rep.Actions["download"] = &lfs_module.Link{Href: rc.DownloadLink(pointer), Header: header} + } + if upload { + rep.Actions["upload"] = &lfs_module.Link{Href: rc.UploadLink(pointer), Header: header} -// TODO cheap hack, unify with unpack -func unpackbatch(ctx *context.Context) *lfs_module.BatchRequest { + verifyHeader := make(map[string]string) + for key, value := range header { + verifyHeader[key] = value + } - r := ctx.Req - var bv lfs_module.BatchRequest + // This is only needed to workaround https://github.com/git-lfs/git-lfs/issues/3662 + verifyHeader["Accept"] = lfs_module.MediaType - bodyReader := r.Body - defer bodyReader.Close() - json := jsoniter.ConfigCompatibleWithStandardLibrary - dec := json.NewDecoder(bodyReader) - err := dec.Decode(&bv) - if err != nil { - // The error is logged as a WARN here because this may represent misbehaviour rather than a true error - log.Warn("Unable to decode BATCH request vars in %s/%s: Error: %v", ctx.Params("username"), strings.TrimSuffix(ctx.Params("reponame"), ".git"), err) - return &bv + rep.Actions["verify"] = &lfs_module.Link{Href: rc.VerifyLink(pointer), Header: verifyHeader} + } } - - return &bv + return rep } func writeStatus(ctx *context.Context, status int) { - message := http.StatusText(status) - - mediaParts := strings.Split(ctx.Req.Header.Get("Accept"), ";") - mt := mediaParts[0] - if strings.HasSuffix(mt, "+json") { - message = `{"message":"` + message + `"}` - } + writeStatusMessage(ctx, status, http.StatusText(status)) +} +func writeStatusMessage(ctx *context.Context, status int, message string) { + ctx.Resp.Header().Set("Content-Type", lfs_module.MediaType) ctx.Resp.WriteHeader(status) - fmt.Fprint(ctx.Resp, message) - logRequest(ctx.Req, status) -} -func logRequest(r *http.Request, status int) { - log.Debug("LFS request - Method: %s, URL: %s, Status %d", r.Method, r.URL, status) + er := lfs_module.ErrorResponse{Message: message} + + enc := jsoniter.NewEncoder(ctx.Resp) + if err := enc.Encode(er); err != nil { + log.Error("Failed to encode error response as json. Error: %v", err) + } } // authenticate uses the authorization string to determine whether @@ -645,5 +521,5 @@ func parseToken(authorization string, target *models.Repository, mode models.Acc func requireAuth(ctx *context.Context) { ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs") - writeStatus(ctx, 401) + writeStatus(ctx, http.StatusUnauthorized) } diff --git a/services/mirror/mirror.go b/services/mirror/mirror.go index 9e2dde85fca2d..1e30c919e6d4c 100644 --- a/services/mirror/mirror.go +++ b/services/mirror/mirror.go @@ -7,593 +7,97 @@ package mirror import ( "context" "fmt" - "net/url" "strconv" "strings" - "time" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/cache" - "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/graceful" - "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" - "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/sync" - "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" ) // mirrorQueue holds an UniqueQueue object of the mirror var mirrorQueue = sync.NewUniqueQueue(setting.Repository.MirrorQueueLength) -func readAddress(m *models.Mirror) { - if len(m.Address) > 0 { - return - } - var err error - m.Address, err = remoteAddress(m.Repo.RepoPath()) - if err != nil { - log.Error("remoteAddress: %v", err) - } -} - -func remoteAddress(repoPath string) (string, error) { - var cmd *git.Command - err := git.LoadGitVersion() - if err != nil { - return "", err - } - if git.CheckGitVersionAtLeast("2.7") == nil { - cmd = git.NewCommand("remote", "get-url", "origin") - } else { - cmd = git.NewCommand("config", "--get", "remote.origin.url") - } - - result, err := cmd.RunInDir(repoPath) - if err != nil { - if strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { - return "", nil - } - return "", err - } - if len(result) > 0 { - return result[:len(result)-1], nil - } - return "", nil -} - -// sanitizeOutput sanitizes output of a command, replacing occurrences of the -// repository's remote address with a sanitized version. -func sanitizeOutput(output, repoPath string) (string, error) { - remoteAddr, err := remoteAddress(repoPath) - if err != nil { - // if we're unable to load the remote address, then we're unable to - // sanitize. - return "", err - } - return util.SanitizeMessage(output, remoteAddr), nil -} - -// AddressNoCredentials returns mirror address from Git repository config without credentials. -func AddressNoCredentials(m *models.Mirror) string { - readAddress(m) - u, err := url.Parse(m.Address) - if err != nil { - // this shouldn't happen but just return it unsanitised - return m.Address - } - u.User = nil - return u.String() -} - -// UpdateAddress writes new address to Git repository and database -func UpdateAddress(m *models.Mirror, addr string) error { - repoPath := m.Repo.RepoPath() - // Remove old origin - _, err := git.NewCommand("remote", "rm", "origin").RunInDir(repoPath) - if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { - return err - } - - _, err = git.NewCommand("remote", "add", "origin", "--mirror=fetch", addr).RunInDir(repoPath) - if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { - return err - } - - if m.Repo.HasWiki() { - wikiPath := m.Repo.WikiPath() - wikiRemotePath := repo_module.WikiRemoteURL(addr) - // Remove old origin of wiki - _, err := git.NewCommand("remote", "rm", "origin").RunInDir(wikiPath) - if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { - return err - } - - _, err = git.NewCommand("remote", "add", "origin", "--mirror=fetch", wikiRemotePath).RunInDir(wikiPath) - if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { - return err - } - } - - m.Repo.OriginalURL = addr - return models.UpdateRepositoryCols(m.Repo, "original_url") -} - -// gitShortEmptySha Git short empty SHA -const gitShortEmptySha = "0000000" - -// mirrorSyncResult contains information of a updated reference. -// If the oldCommitID is "0000000", it means a new reference, the value of newCommitID is empty. -// If the newCommitID is "0000000", it means the reference is deleted, the value of oldCommitID is empty. -type mirrorSyncResult struct { - refName string - oldCommitID string - newCommitID string -} - -// parseRemoteUpdateOutput detects create, update and delete operations of references from upstream. -func parseRemoteUpdateOutput(output string) []*mirrorSyncResult { - results := make([]*mirrorSyncResult, 0, 3) - lines := strings.Split(output, "\n") - for i := range lines { - // Make sure reference name is presented before continue - idx := strings.Index(lines[i], "-> ") - if idx == -1 { - continue - } - - refName := lines[i][idx+3:] - - switch { - case strings.HasPrefix(lines[i], " * "): // New reference - if strings.HasPrefix(lines[i], " * [new tag]") { - refName = git.TagPrefix + refName - } else if strings.HasPrefix(lines[i], " * [new branch]") { - refName = git.BranchPrefix + refName - } - results = append(results, &mirrorSyncResult{ - refName: refName, - oldCommitID: gitShortEmptySha, - }) - case strings.HasPrefix(lines[i], " - "): // Delete reference - results = append(results, &mirrorSyncResult{ - refName: refName, - newCommitID: gitShortEmptySha, - }) - case strings.HasPrefix(lines[i], " + "): // Force update - if idx := strings.Index(refName, " "); idx > -1 { - refName = refName[:idx] - } - delimIdx := strings.Index(lines[i][3:], " ") - if delimIdx == -1 { - log.Error("SHA delimiter not found: %q", lines[i]) - continue - } - shas := strings.Split(lines[i][3:delimIdx+3], "...") - if len(shas) != 2 { - log.Error("Expect two SHAs but not what found: %q", lines[i]) - continue - } - results = append(results, &mirrorSyncResult{ - refName: refName, - oldCommitID: shas[0], - newCommitID: shas[1], - }) - case strings.HasPrefix(lines[i], " "): // New commits of a reference - delimIdx := strings.Index(lines[i][3:], " ") - if delimIdx == -1 { - log.Error("SHA delimiter not found: %q", lines[i]) - continue - } - shas := strings.Split(lines[i][3:delimIdx+3], "..") - if len(shas) != 2 { - log.Error("Expect two SHAs but not what found: %q", lines[i]) - continue - } - results = append(results, &mirrorSyncResult{ - refName: refName, - oldCommitID: shas[0], - newCommitID: shas[1], - }) - - default: - log.Warn("parseRemoteUpdateOutput: unexpected update line %q", lines[i]) - } - } - return results -} - -// runSync returns true if sync finished without error. -func runSync(ctx context.Context, m *models.Mirror) ([]*mirrorSyncResult, bool) { - repoPath := m.Repo.RepoPath() - wikiPath := m.Repo.WikiPath() - timeout := time.Duration(setting.Git.Timeout.Mirror) * time.Second - - log.Trace("SyncMirrors [repo: %-v]: running git remote update...", m.Repo) - gitArgs := []string{"remote", "update"} - if m.EnablePrune { - gitArgs = append(gitArgs, "--prune") - } - - stdoutBuilder := strings.Builder{} - stderrBuilder := strings.Builder{} - if err := git.NewCommand(gitArgs...). - SetDescription(fmt.Sprintf("Mirror.runSync: %s", m.Repo.FullName())). - RunInDirTimeoutPipeline(timeout, repoPath, &stdoutBuilder, &stderrBuilder); err != nil { - stdout := stdoutBuilder.String() - stderr := stderrBuilder.String() - // sanitize the output, since it may contain the remote address, which may - // contain a password - stderrMessage, sanitizeErr := sanitizeOutput(stderr, repoPath) - if sanitizeErr != nil { - log.Error("sanitizeOutput failed on stderr: %v", sanitizeErr) - log.Error("Failed to update mirror repository %v:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdout, stderr, err) - return nil, false - } - stdoutMessage, err := sanitizeOutput(stdout, repoPath) - if err != nil { - log.Error("sanitizeOutput failed: %v", sanitizeErr) - log.Error("Failed to update mirror repository %v:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdout, stderrMessage, err) - return nil, false - } - - log.Error("Failed to update mirror repository %v:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err) - desc := fmt.Sprintf("Failed to update mirror repository '%s': %s", repoPath, stderrMessage) - if err = models.CreateRepositoryNotice(desc); err != nil { - log.Error("CreateRepositoryNotice: %v", err) - } - return nil, false - } - output := stderrBuilder.String() - - gitRepo, err := git.OpenRepository(repoPath) - if err != nil { - log.Error("OpenRepository: %v", err) - return nil, false - } - defer gitRepo.Close() - - log.Trace("SyncMirrors [repo: %-v]: syncing releases with tags...", m.Repo) - if err = repo_module.SyncReleasesWithTags(m.Repo, gitRepo); err != nil { - log.Error("Failed to synchronize tags to releases for repository: %v", err) - } - - if m.LFS && setting.LFS.StartServer { - log.Trace("SyncMirrors [repo: %-v]: syncing LFS objects...", m.Repo) - readAddress(m) - ep := lfs.DetermineEndpoint(m.Address, m.LFSEndpoint) - if err = repo_module.StoreMissingLfsObjectsInRepository(ctx, m.Repo, gitRepo, ep); err != nil { - log.Error("Failed to synchronize LFS objects for repository: %v", err) - } - } - - log.Trace("SyncMirrors [repo: %-v]: updating size of repository", m.Repo) - if err := m.Repo.UpdateSize(models.DefaultDBContext()); err != nil { - log.Error("Failed to update size for mirror repository: %v", err) - } - - if m.Repo.HasWiki() { - log.Trace("SyncMirrors [repo: %-v Wiki]: running git remote update...", m.Repo) - stderrBuilder.Reset() - stdoutBuilder.Reset() - if err := git.NewCommand("remote", "update", "--prune"). - SetDescription(fmt.Sprintf("Mirror.runSync Wiki: %s ", m.Repo.FullName())). - RunInDirTimeoutPipeline(timeout, wikiPath, &stdoutBuilder, &stderrBuilder); err != nil { - stdout := stdoutBuilder.String() - stderr := stderrBuilder.String() - // sanitize the output, since it may contain the remote address, which may - // contain a password - stderrMessage, sanitizeErr := sanitizeOutput(stderr, repoPath) - if sanitizeErr != nil { - log.Error("sanitizeOutput failed on stderr: %v", sanitizeErr) - log.Error("Failed to update mirror repository wiki %v:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdout, stderr, err) - return nil, false - } - stdoutMessage, err := sanitizeOutput(stdout, repoPath) - if err != nil { - log.Error("sanitizeOutput failed: %v", sanitizeErr) - log.Error("Failed to update mirror repository wiki %v:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdout, stderrMessage, err) - return nil, false - } - - log.Error("Failed to update mirror repository wiki %v:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err) - desc := fmt.Sprintf("Failed to update mirror repository wiki '%s': %s", wikiPath, stderrMessage) - if err = models.CreateRepositoryNotice(desc); err != nil { - log.Error("CreateRepositoryNotice: %v", err) - } - return nil, false - } - log.Trace("SyncMirrors [repo: %-v Wiki]: git remote update complete", m.Repo) - } - - log.Trace("SyncMirrors [repo: %-v]: invalidating mirror branch caches...", m.Repo) - branches, _, err := repo_module.GetBranches(m.Repo, 0, 0) - if err != nil { - log.Error("GetBranches: %v", err) - return nil, false - } - - for _, branch := range branches { - cache.Remove(m.Repo.GetCommitsCountCacheKey(branch.Name, true)) - } - - m.UpdatedUnix = timeutil.TimeStampNow() - return parseRemoteUpdateOutput(output), true -} - -// Address returns mirror address from Git repository config without credentials. -func Address(m *models.Mirror) string { - readAddress(m) - return util.SanitizeURLCredentials(m.Address, false) -} - -// Username returns the mirror address username -func Username(m *models.Mirror) string { - readAddress(m) - u, err := url.Parse(m.Address) - if err != nil { - // this shouldn't happen but if it does return "" - return "" - } - return u.User.Username() -} - -// Password returns the mirror address password -func Password(m *models.Mirror) string { - readAddress(m) - u, err := url.Parse(m.Address) - if err != nil { - // this shouldn't happen but if it does return "" - return "" - } - password, _ := u.User.Password() - return password -} - // Update checks and updates mirror repositories. func Update(ctx context.Context) error { log.Trace("Doing: Update") - if err := models.MirrorsIterate(func(idx int, bean interface{}) error { - m := bean.(*models.Mirror) - if m.Repo == nil { - log.Error("Disconnected mirror repository found: %d", m.ID) + + handler := func(idx int, bean interface{}) error { + var item string + if m, ok := bean.(*models.Mirror); ok { + if m.Repo == nil { + log.Error("Disconnected mirror found: %d", m.ID) + return nil + } + item = fmt.Sprintf("pull %d", m.RepoID) + } else if m, ok := bean.(*models.PushMirror); ok { + if m.Repo == nil { + log.Error("Disconnected push-mirror found: %d", m.ID) + return nil + } + item = fmt.Sprintf("push %d", m.ID) + } else { + log.Error("Unknown bean: %v", bean) return nil } + select { case <-ctx.Done(): return fmt.Errorf("Aborted") default: - mirrorQueue.Add(m.RepoID) + mirrorQueue.Add(item) return nil } - }); err != nil { - log.Trace("Update: %v", err) + } + + if err := models.MirrorsIterate(handler); err != nil { + log.Error("MirrorsIterate: %v", err) + return err + } + if err := models.PushMirrorsIterate(handler); err != nil { + log.Error("PushMirrorsIterate: %v", err) return err } log.Trace("Finished: Update") return nil } -// SyncMirrors checks and syncs mirrors. +// syncMirrors checks and syncs mirrors. // FIXME: graceful: this should be a persistable queue -func SyncMirrors(ctx context.Context) { +func syncMirrors(ctx context.Context) { // Start listening on new sync requests. for { select { case <-ctx.Done(): mirrorQueue.Close() return - case repoID := <-mirrorQueue.Queue(): - syncMirror(ctx, repoID) - } - } -} - -func syncMirror(ctx context.Context, repoID string) { - log.Trace("SyncMirrors [repo_id: %v]", repoID) - defer func() { - err := recover() - if err == nil { - return - } - // There was a panic whilst syncMirrors... - log.Error("PANIC whilst syncMirrors[%s] Panic: %v\nStacktrace: %s", repoID, err, log.Stack(2)) - }() - mirrorQueue.Remove(repoID) - - id, _ := strconv.ParseInt(repoID, 10, 64) - m, err := models.GetMirrorByRepoID(id) - if err != nil { - log.Error("GetMirrorByRepoID [%s]: %v", repoID, err) - return - } - - log.Trace("SyncMirrors [repo: %-v]: Running Sync", m.Repo) - results, ok := runSync(ctx, m) - if !ok { - return - } - - log.Trace("SyncMirrors [repo: %-v]: Scheduling next update", m.Repo) - m.ScheduleNextUpdate() - if err = models.UpdateMirror(m); err != nil { - log.Error("UpdateMirror [%s]: %v", repoID, err) - return - } - - var gitRepo *git.Repository - if len(results) == 0 { - log.Trace("SyncMirrors [repo: %-v]: no branches updated", m.Repo) - } else { - log.Trace("SyncMirrors [repo: %-v]: %d branches updated", m.Repo, len(results)) - gitRepo, err = git.OpenRepository(m.Repo.RepoPath()) - if err != nil { - log.Error("OpenRepository [%d]: %v", m.RepoID, err) - return - } - defer gitRepo.Close() - - if ok := checkAndUpdateEmptyRepository(m, gitRepo, results); !ok { - return - } - } - - for _, result := range results { - // Discard GitHub pull requests, i.e. refs/pull/* - if strings.HasPrefix(result.refName, "refs/pull/") { - continue - } - - tp, _ := git.SplitRefName(result.refName) - - // Create reference - if result.oldCommitID == gitShortEmptySha { - if tp == git.TagPrefix { - tp = "tag" - } else if tp == git.BranchPrefix { - tp = "branch" + case item := <-mirrorQueue.Queue(): + id, _ := strconv.ParseInt(item[5:], 10, 64) + if strings.HasPrefix(item, "pull") { + _ = SyncPullMirror(ctx, id) + } else if strings.HasPrefix(item, "push") { + _ = SyncPushMirror(ctx, id) + } else { + log.Error("Unknown item in queue: %v", item) } - commitID, err := gitRepo.GetRefCommitID(result.refName) - if err != nil { - log.Error("gitRepo.GetRefCommitID [repo_id: %s, ref_name: %s]: %v", m.RepoID, result.refName, err) - continue - } - notification.NotifySyncPushCommits(m.Repo.MustOwner(), m.Repo, &repo_module.PushUpdateOptions{ - RefFullName: result.refName, - OldCommitID: git.EmptySHA, - NewCommitID: commitID, - }, repo_module.NewPushCommits()) - notification.NotifySyncCreateRef(m.Repo.MustOwner(), m.Repo, tp, result.refName) - continue - } - - // Delete reference - if result.newCommitID == gitShortEmptySha { - notification.NotifySyncDeleteRef(m.Repo.MustOwner(), m.Repo, tp, result.refName) - continue - } - - // Push commits - oldCommitID, err := git.GetFullCommitID(gitRepo.Path, result.oldCommitID) - if err != nil { - log.Error("GetFullCommitID [%d]: %v", m.RepoID, err) - continue - } - newCommitID, err := git.GetFullCommitID(gitRepo.Path, result.newCommitID) - if err != nil { - log.Error("GetFullCommitID [%d]: %v", m.RepoID, err) - continue - } - commits, err := gitRepo.CommitsBetweenIDs(newCommitID, oldCommitID) - if err != nil { - log.Error("CommitsBetweenIDs [repo_id: %d, new_commit_id: %s, old_commit_id: %s]: %v", m.RepoID, newCommitID, oldCommitID, err) - continue + mirrorQueue.Remove(item) } - - theCommits := repo_module.ListToPushCommits(commits) - if len(theCommits.Commits) > setting.UI.FeedMaxCommitNum { - theCommits.Commits = theCommits.Commits[:setting.UI.FeedMaxCommitNum] - } - - theCommits.CompareURL = m.Repo.ComposeCompareURL(oldCommitID, newCommitID) - - notification.NotifySyncPushCommits(m.Repo.MustOwner(), m.Repo, &repo_module.PushUpdateOptions{ - RefFullName: result.refName, - OldCommitID: oldCommitID, - NewCommitID: newCommitID, - }, theCommits) - } - log.Trace("SyncMirrors [repo: %-v]: done notifying updated branches/tags - now updating last commit time", m.Repo) - - // Get latest commit date and update to current repository updated time - commitDate, err := git.GetLatestCommitTime(m.Repo.RepoPath()) - if err != nil { - log.Error("GetLatestCommitDate [%d]: %v", m.RepoID, err) - return - } - - if err = models.UpdateRepositoryUpdatedTime(m.RepoID, commitDate); err != nil { - log.Error("Update repository 'updated_unix' [%d]: %v", m.RepoID, err) - return } - - log.Trace("SyncMirrors [repo: %-v]: Successfully updated", m.Repo) -} - -func checkAndUpdateEmptyRepository(m *models.Mirror, gitRepo *git.Repository, results []*mirrorSyncResult) bool { - if !m.Repo.IsEmpty { - return true - } - - hasDefault := false - hasMaster := false - hasMain := false - defaultBranchName := m.Repo.DefaultBranch - if len(defaultBranchName) == 0 { - defaultBranchName = setting.Repository.DefaultBranch - } - firstName := "" - for _, result := range results { - if strings.HasPrefix(result.refName, "refs/pull/") { - continue - } - tp, name := git.SplitRefName(result.refName) - if len(tp) > 0 && tp != git.BranchPrefix { - continue - } - if len(firstName) == 0 { - firstName = name - } - - hasDefault = hasDefault || name == defaultBranchName - hasMaster = hasMaster || name == "master" - hasMain = hasMain || name == "main" - } - - if len(firstName) > 0 { - if hasDefault { - m.Repo.DefaultBranch = defaultBranchName - } else if hasMaster { - m.Repo.DefaultBranch = "master" - } else if hasMain { - m.Repo.DefaultBranch = "main" - } else { - m.Repo.DefaultBranch = firstName - } - // Update the git repository default branch - if err := gitRepo.SetDefaultBranch(m.Repo.DefaultBranch); err != nil { - if !git.IsErrUnsupportedVersion(err) { - log.Error("Failed to update default branch of underlying git repository %-v. Error: %v", m.Repo, err) - desc := fmt.Sprintf("Failed to uupdate default branch of underlying git repository '%s': %v", m.Repo.RepoPath(), err) - if err = models.CreateRepositoryNotice(desc); err != nil { - log.Error("CreateRepositoryNotice: %v", err) - } - return false - } - } - m.Repo.IsEmpty = false - // Update the is empty and default_branch columns - if err := models.UpdateRepositoryCols(m.Repo, "default_branch", "is_empty"); err != nil { - log.Error("Failed to update default branch of repository %-v. Error: %v", m.Repo, err) - desc := fmt.Sprintf("Failed to uupdate default branch of repository '%s': %v", m.Repo.RepoPath(), err) - if err = models.CreateRepositoryNotice(desc); err != nil { - log.Error("CreateRepositoryNotice: %v", err) - } - return false - } - } - return true } // InitSyncMirrors initializes a go routine to sync the mirrors func InitSyncMirrors() { - go graceful.GetManager().RunWithShutdownContext(SyncMirrors) + go graceful.GetManager().RunWithShutdownContext(syncMirrors) } // StartToMirror adds repoID to mirror queue func StartToMirror(repoID int64) { - go mirrorQueue.Add(repoID) + go mirrorQueue.Add(fmt.Sprintf("pull %d", repoID)) +} + +// AddPushMirrorToQueue adds the push mirror to the queue +func AddPushMirrorToQueue(mirrorID int64) { + go mirrorQueue.Add(fmt.Sprintf("push %d", mirrorID)) } diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go new file mode 100644 index 0000000000000..a16724b36fefa --- /dev/null +++ b/services/mirror/mirror_pull.go @@ -0,0 +1,452 @@ +// 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 mirror + +import ( + "context" + "fmt" + "strings" + "time" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/cache" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/lfs" + "code.gitea.io/gitea/modules/log" + "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/timeutil" + "code.gitea.io/gitea/modules/util" +) + +// gitShortEmptySha Git short empty SHA +const gitShortEmptySha = "0000000" + +// UpdateAddress writes new address to Git repository and database +func UpdateAddress(m *models.Mirror, addr string) error { + remoteName := m.GetRemoteName() + repoPath := m.Repo.RepoPath() + // Remove old remote + _, err := git.NewCommand("remote", "rm", remoteName).RunInDir(repoPath) + if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { + return err + } + + _, err = git.NewCommand("remote", "add", remoteName, "--mirror=fetch", addr).RunInDir(repoPath) + if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { + return err + } + + if m.Repo.HasWiki() { + wikiPath := m.Repo.WikiPath() + wikiRemotePath := repo_module.WikiRemoteURL(addr) + // Remove old remote of wiki + _, err := git.NewCommand("remote", "rm", remoteName).RunInDir(wikiPath) + if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { + return err + } + + _, err = git.NewCommand("remote", "add", remoteName, "--mirror=fetch", wikiRemotePath).RunInDir(wikiPath) + if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { + return err + } + } + + m.Repo.OriginalURL = addr + return models.UpdateRepositoryCols(m.Repo, "original_url") +} + +// mirrorSyncResult contains information of a updated reference. +// If the oldCommitID is "0000000", it means a new reference, the value of newCommitID is empty. +// If the newCommitID is "0000000", it means the reference is deleted, the value of oldCommitID is empty. +type mirrorSyncResult struct { + refName string + oldCommitID string + newCommitID string +} + +// parseRemoteUpdateOutput detects create, update and delete operations of references from upstream. +func parseRemoteUpdateOutput(output string) []*mirrorSyncResult { + results := make([]*mirrorSyncResult, 0, 3) + lines := strings.Split(output, "\n") + for i := range lines { + // Make sure reference name is presented before continue + idx := strings.Index(lines[i], "-> ") + if idx == -1 { + continue + } + + refName := lines[i][idx+3:] + + switch { + case strings.HasPrefix(lines[i], " * "): // New reference + if strings.HasPrefix(lines[i], " * [new tag]") { + refName = git.TagPrefix + refName + } else if strings.HasPrefix(lines[i], " * [new branch]") { + refName = git.BranchPrefix + refName + } + results = append(results, &mirrorSyncResult{ + refName: refName, + oldCommitID: gitShortEmptySha, + }) + case strings.HasPrefix(lines[i], " - "): // Delete reference + results = append(results, &mirrorSyncResult{ + refName: refName, + newCommitID: gitShortEmptySha, + }) + case strings.HasPrefix(lines[i], " + "): // Force update + if idx := strings.Index(refName, " "); idx > -1 { + refName = refName[:idx] + } + delimIdx := strings.Index(lines[i][3:], " ") + if delimIdx == -1 { + log.Error("SHA delimiter not found: %q", lines[i]) + continue + } + shas := strings.Split(lines[i][3:delimIdx+3], "...") + if len(shas) != 2 { + log.Error("Expect two SHAs but not what found: %q", lines[i]) + continue + } + results = append(results, &mirrorSyncResult{ + refName: refName, + oldCommitID: shas[0], + newCommitID: shas[1], + }) + case strings.HasPrefix(lines[i], " "): // New commits of a reference + delimIdx := strings.Index(lines[i][3:], " ") + if delimIdx == -1 { + log.Error("SHA delimiter not found: %q", lines[i]) + continue + } + shas := strings.Split(lines[i][3:delimIdx+3], "..") + if len(shas) != 2 { + log.Error("Expect two SHAs but not what found: %q", lines[i]) + continue + } + results = append(results, &mirrorSyncResult{ + refName: refName, + oldCommitID: shas[0], + newCommitID: shas[1], + }) + + default: + log.Warn("parseRemoteUpdateOutput: unexpected update line %q", lines[i]) + } + } + return results +} + +// runSync returns true if sync finished without error. +func runSync(ctx context.Context, m *models.Mirror) ([]*mirrorSyncResult, bool) { + repoPath := m.Repo.RepoPath() + wikiPath := m.Repo.WikiPath() + timeout := time.Duration(setting.Git.Timeout.Mirror) * time.Second + + log.Trace("SyncMirrors [repo: %-v]: running git remote update...", m.Repo) + gitArgs := []string{"remote", "update"} + if m.EnablePrune { + gitArgs = append(gitArgs, "--prune") + } + gitArgs = append(gitArgs, m.GetRemoteName()) + + remoteAddr, remoteErr := git.GetRemoteAddress(repoPath, m.GetRemoteName()) + if remoteErr != nil { + log.Error("GetRemoteAddress Error %v", remoteErr) + } + + stdoutBuilder := strings.Builder{} + stderrBuilder := strings.Builder{} + if err := git.NewCommand(gitArgs...). + SetDescription(fmt.Sprintf("Mirror.runSync: %s", m.Repo.FullName())). + RunInDirTimeoutPipeline(timeout, repoPath, &stdoutBuilder, &stderrBuilder); err != nil { + stdout := stdoutBuilder.String() + stderr := stderrBuilder.String() + + // sanitize the output, since it may contain the remote address, which may + // contain a password + + sanitizer := util.NewURLSanitizer(remoteAddr, true) + stderrMessage := sanitizer.Replace(stderr) + stdoutMessage := sanitizer.Replace(stdout) + + log.Error("Failed to update mirror repository %v:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err) + desc := fmt.Sprintf("Failed to update mirror repository '%s': %s", repoPath, stderrMessage) + if err = models.CreateRepositoryNotice(desc); err != nil { + log.Error("CreateRepositoryNotice: %v", err) + } + return nil, false + } + output := stderrBuilder.String() + + gitRepo, err := git.OpenRepository(repoPath) + if err != nil { + log.Error("OpenRepository: %v", err) + return nil, false + } + + log.Trace("SyncMirrors [repo: %-v]: syncing releases with tags...", m.Repo) + if err = repo_module.SyncReleasesWithTags(m.Repo, gitRepo); err != nil { + log.Error("Failed to synchronize tags to releases for repository: %v", err) + } + + if m.LFS && setting.LFS.StartServer { + log.Trace("SyncMirrors [repo: %-v]: syncing LFS objects...", m.Repo) + ep := lfs.DetermineEndpoint(remoteAddr.String(), m.LFSEndpoint) + if err = repo_module.StoreMissingLfsObjectsInRepository(ctx, m.Repo, gitRepo, ep); err != nil { + log.Error("Failed to synchronize LFS objects for repository: %v", err) + } + } + gitRepo.Close() + + log.Trace("SyncMirrors [repo: %-v]: updating size of repository", m.Repo) + if err := m.Repo.UpdateSize(models.DefaultDBContext()); err != nil { + log.Error("Failed to update size for mirror repository: %v", err) + } + + if m.Repo.HasWiki() { + log.Trace("SyncMirrors [repo: %-v Wiki]: running git remote update...", m.Repo) + stderrBuilder.Reset() + stdoutBuilder.Reset() + if err := git.NewCommand("remote", "update", "--prune", m.GetRemoteName()). + SetDescription(fmt.Sprintf("Mirror.runSync Wiki: %s ", m.Repo.FullName())). + RunInDirTimeoutPipeline(timeout, wikiPath, &stdoutBuilder, &stderrBuilder); err != nil { + stdout := stdoutBuilder.String() + stderr := stderrBuilder.String() + + // sanitize the output, since it may contain the remote address, which may + // contain a password + + remoteAddr, remoteErr := git.GetRemoteAddress(wikiPath, m.GetRemoteName()) + if remoteErr != nil { + log.Error("GetRemoteAddress Error %v", remoteErr) + } + + sanitizer := util.NewURLSanitizer(remoteAddr, true) + stderrMessage := sanitizer.Replace(stderr) + stdoutMessage := sanitizer.Replace(stdout) + + log.Error("Failed to update mirror repository wiki %v:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err) + desc := fmt.Sprintf("Failed to update mirror repository wiki '%s': %s", wikiPath, stderrMessage) + if err = models.CreateRepositoryNotice(desc); err != nil { + log.Error("CreateRepositoryNotice: %v", err) + } + return nil, false + } + log.Trace("SyncMirrors [repo: %-v Wiki]: git remote update complete", m.Repo) + } + + log.Trace("SyncMirrors [repo: %-v]: invalidating mirror branch caches...", m.Repo) + branches, _, err := repo_module.GetBranches(m.Repo, 0, 0) + if err != nil { + log.Error("GetBranches: %v", err) + return nil, false + } + + for _, branch := range branches { + cache.Remove(m.Repo.GetCommitsCountCacheKey(branch.Name, true)) + } + + m.UpdatedUnix = timeutil.TimeStampNow() + return parseRemoteUpdateOutput(output), true +} + +// SyncPullMirror starts the sync of the pull mirror and schedules the next run. +func SyncPullMirror(ctx context.Context, repoID int64) bool { + log.Trace("SyncMirrors [repo_id: %v]", repoID) + defer func() { + err := recover() + if err == nil { + return + } + // There was a panic whilst syncMirrors... + log.Error("PANIC whilst syncMirrors[%d] Panic: %v\nStacktrace: %s", repoID, err, log.Stack(2)) + }() + + m, err := models.GetMirrorByRepoID(repoID) + if err != nil { + log.Error("GetMirrorByRepoID [%d]: %v", repoID, err) + return false + } + + log.Trace("SyncMirrors [repo: %-v]: Running Sync", m.Repo) + results, ok := runSync(ctx, m) + if !ok { + return false + } + + log.Trace("SyncMirrors [repo: %-v]: Scheduling next update", m.Repo) + m.ScheduleNextUpdate() + if err = models.UpdateMirror(m); err != nil { + log.Error("UpdateMirror [%d]: %v", m.RepoID, err) + return false + } + + var gitRepo *git.Repository + if len(results) == 0 { + log.Trace("SyncMirrors [repo: %-v]: no branches updated", m.Repo) + } else { + log.Trace("SyncMirrors [repo: %-v]: %d branches updated", m.Repo, len(results)) + gitRepo, err = git.OpenRepository(m.Repo.RepoPath()) + if err != nil { + log.Error("OpenRepository [%d]: %v", m.RepoID, err) + return false + } + defer gitRepo.Close() + + if ok := checkAndUpdateEmptyRepository(m, gitRepo, results); !ok { + return false + } + } + + for _, result := range results { + // Discard GitHub pull requests, i.e. refs/pull/* + if strings.HasPrefix(result.refName, "refs/pull/") { + continue + } + + tp, _ := git.SplitRefName(result.refName) + + // Create reference + if result.oldCommitID == gitShortEmptySha { + if tp == git.TagPrefix { + tp = "tag" + } else if tp == git.BranchPrefix { + tp = "branch" + } + commitID, err := gitRepo.GetRefCommitID(result.refName) + if err != nil { + log.Error("gitRepo.GetRefCommitID [repo_id: %d, ref_name: %s]: %v", m.RepoID, result.refName, err) + continue + } + notification.NotifySyncPushCommits(m.Repo.MustOwner(), m.Repo, &repo_module.PushUpdateOptions{ + RefFullName: result.refName, + OldCommitID: git.EmptySHA, + NewCommitID: commitID, + }, repo_module.NewPushCommits()) + notification.NotifySyncCreateRef(m.Repo.MustOwner(), m.Repo, tp, result.refName) + continue + } + + // Delete reference + if result.newCommitID == gitShortEmptySha { + notification.NotifySyncDeleteRef(m.Repo.MustOwner(), m.Repo, tp, result.refName) + continue + } + + // Push commits + oldCommitID, err := git.GetFullCommitID(gitRepo.Path, result.oldCommitID) + if err != nil { + log.Error("GetFullCommitID [%d]: %v", m.RepoID, err) + continue + } + newCommitID, err := git.GetFullCommitID(gitRepo.Path, result.newCommitID) + if err != nil { + log.Error("GetFullCommitID [%d]: %v", m.RepoID, err) + continue + } + commits, err := gitRepo.CommitsBetweenIDs(newCommitID, oldCommitID) + if err != nil { + log.Error("CommitsBetweenIDs [repo_id: %d, new_commit_id: %s, old_commit_id: %s]: %v", m.RepoID, newCommitID, oldCommitID, err) + continue + } + + theCommits := repo_module.ListToPushCommits(commits) + if len(theCommits.Commits) > setting.UI.FeedMaxCommitNum { + theCommits.Commits = theCommits.Commits[:setting.UI.FeedMaxCommitNum] + } + + theCommits.CompareURL = m.Repo.ComposeCompareURL(oldCommitID, newCommitID) + + notification.NotifySyncPushCommits(m.Repo.MustOwner(), m.Repo, &repo_module.PushUpdateOptions{ + RefFullName: result.refName, + OldCommitID: oldCommitID, + NewCommitID: newCommitID, + }, theCommits) + } + log.Trace("SyncMirrors [repo: %-v]: done notifying updated branches/tags - now updating last commit time", m.Repo) + + // Get latest commit date and update to current repository updated time + commitDate, err := git.GetLatestCommitTime(m.Repo.RepoPath()) + if err != nil { + log.Error("GetLatestCommitDate [%d]: %v", m.RepoID, err) + return false + } + + if err = models.UpdateRepositoryUpdatedTime(m.RepoID, commitDate); err != nil { + log.Error("Update repository 'updated_unix' [%d]: %v", m.RepoID, err) + return false + } + + log.Trace("SyncMirrors [repo: %-v]: Successfully updated", m.Repo) + + return true +} + +func checkAndUpdateEmptyRepository(m *models.Mirror, gitRepo *git.Repository, results []*mirrorSyncResult) bool { + if !m.Repo.IsEmpty { + return true + } + + hasDefault := false + hasMaster := false + hasMain := false + defaultBranchName := m.Repo.DefaultBranch + if len(defaultBranchName) == 0 { + defaultBranchName = setting.Repository.DefaultBranch + } + firstName := "" + for _, result := range results { + if strings.HasPrefix(result.refName, "refs/pull/") { + continue + } + tp, name := git.SplitRefName(result.refName) + if len(tp) > 0 && tp != git.BranchPrefix { + continue + } + if len(firstName) == 0 { + firstName = name + } + + hasDefault = hasDefault || name == defaultBranchName + hasMaster = hasMaster || name == "master" + hasMain = hasMain || name == "main" + } + + if len(firstName) > 0 { + if hasDefault { + m.Repo.DefaultBranch = defaultBranchName + } else if hasMaster { + m.Repo.DefaultBranch = "master" + } else if hasMain { + m.Repo.DefaultBranch = "main" + } else { + m.Repo.DefaultBranch = firstName + } + // Update the git repository default branch + if err := gitRepo.SetDefaultBranch(m.Repo.DefaultBranch); err != nil { + if !git.IsErrUnsupportedVersion(err) { + log.Error("Failed to update default branch of underlying git repository %-v. Error: %v", m.Repo, err) + desc := fmt.Sprintf("Failed to uupdate default branch of underlying git repository '%s': %v", m.Repo.RepoPath(), err) + if err = models.CreateRepositoryNotice(desc); err != nil { + log.Error("CreateRepositoryNotice: %v", err) + } + return false + } + } + m.Repo.IsEmpty = false + // Update the is empty and default_branch columns + if err := models.UpdateRepositoryCols(m.Repo, "default_branch", "is_empty"); err != nil { + log.Error("Failed to update default branch of repository %-v. Error: %v", m.Repo, err) + desc := fmt.Sprintf("Failed to uupdate default branch of repository '%s': %v", m.Repo.RepoPath(), err) + if err = models.CreateRepositoryNotice(desc); err != nil { + log.Error("CreateRepositoryNotice: %v", err) + } + return false + } + } + return true +} diff --git a/services/mirror/mirror_push.go b/services/mirror/mirror_push.go new file mode 100644 index 0000000000000..de813036894ba --- /dev/null +++ b/services/mirror/mirror_push.go @@ -0,0 +1,242 @@ +// 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 mirror + +import ( + "context" + "errors" + "io" + "net/url" + "regexp" + "time" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/lfs" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/repository" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/util" +) + +var stripExitStatus = regexp.MustCompile(`exit status \d+ - `) + +// AddPushMirrorRemote registers the push mirror remote. +func AddPushMirrorRemote(m *models.PushMirror, addr string) error { + addRemoteAndConfig := func(addr, path string) error { + if _, err := git.NewCommand("remote", "add", "--mirror=push", m.RemoteName, addr).RunInDir(path); err != nil { + return err + } + if _, err := git.NewCommand("config", "--add", "remote."+m.RemoteName+".push", "+refs/heads/*:refs/heads/*").RunInDir(path); err != nil { + return err + } + if _, err := git.NewCommand("config", "--add", "remote."+m.RemoteName+".push", "+refs/tags/*:refs/tags/*").RunInDir(path); err != nil { + return err + } + return nil + } + + if err := addRemoteAndConfig(addr, m.Repo.RepoPath()); err != nil { + return err + } + + if m.Repo.HasWiki() { + wikiRemoteURL := repository.WikiRemoteURL(addr) + if len(wikiRemoteURL) > 0 { + if err := addRemoteAndConfig(wikiRemoteURL, m.Repo.WikiPath()); err != nil { + return err + } + } + } + + return nil +} + +// RemovePushMirrorRemote removes the push mirror remote. +func RemovePushMirrorRemote(m *models.PushMirror) error { + cmd := git.NewCommand("remote", "rm", m.RemoteName) + + if _, err := cmd.RunInDir(m.Repo.RepoPath()); err != nil { + return err + } + + if m.Repo.HasWiki() { + if _, err := cmd.RunInDir(m.Repo.WikiPath()); err != nil { + // The wiki remote may not exist + log.Warn("Wiki Remote[%d] could not be removed: %v", m.ID, err) + } + } + + return nil +} + +// SyncPushMirror starts the sync of the push mirror and schedules the next run. +func SyncPushMirror(ctx context.Context, mirrorID int64) bool { + log.Trace("SyncPushMirror [mirror: %d]", mirrorID) + defer func() { + err := recover() + if err == nil { + return + } + // There was a panic whilst syncPushMirror... + log.Error("PANIC whilst syncPushMirror[%d] Panic: %v\nStacktrace: %s", mirrorID, err, log.Stack(2)) + }() + + m, err := models.GetPushMirrorByID(mirrorID) + if err != nil { + log.Error("GetPushMirrorByID [%d]: %v", mirrorID, err) + return false + } + + m.LastError = "" + + log.Trace("SyncPushMirror [mirror: %d][repo: %-v]: Running Sync", m.ID, m.Repo) + err = runPushSync(ctx, m) + if err != nil { + log.Error("SyncPushMirror [mirror: %d][repo: %-v]: %v", m.ID, m.Repo, err) + m.LastError = stripExitStatus.ReplaceAllLiteralString(err.Error(), "") + } + + m.LastUpdateUnix = timeutil.TimeStampNow() + + if err := models.UpdatePushMirror(m); err != nil { + log.Error("UpdatePushMirror [%d]: %v", m.ID, err) + + return false + } + + log.Trace("SyncPushMirror [mirror: %d][repo: %-v]: Finished", m.ID, m.Repo) + + return err == nil +} + +func runPushSync(ctx context.Context, m *models.PushMirror) error { + timeout := time.Duration(setting.Git.Timeout.Mirror) * time.Second + + performPush := func(path string) error { + remoteAddr, err := git.GetRemoteAddress(path, m.RemoteName) + if err != nil { + log.Error("GetRemoteAddress(%s) Error %v", path, err) + return errors.New("Unexpected error") + } + + if setting.LFS.StartServer { + log.Trace("SyncMirrors [repo: %-v]: syncing LFS objects...", m.Repo) + + gitRepo, err := git.OpenRepository(path) + if err != nil { + log.Error("OpenRepository: %v", err) + return errors.New("Unexpected error") + } + defer gitRepo.Close() + + ep := lfs.DetermineEndpoint(remoteAddr.String(), "") + if err := pushAllLFSObjects(ctx, gitRepo, ep); err != nil { + return util.NewURLSanitizedError(err, remoteAddr, true) + } + } + + log.Trace("Pushing %s mirror[%d] remote %s", path, m.ID, m.RemoteName) + + if err := git.Push(path, git.PushOptions{ + Remote: m.RemoteName, + Force: true, + Mirror: true, + Timeout: timeout, + }); err != nil { + log.Error("Error pushing %s mirror[%d] remote %s: %v", path, m.ID, m.RemoteName, err) + + return util.NewURLSanitizedError(err, remoteAddr, true) + } + + return nil + } + + err := performPush(m.Repo.RepoPath()) + if err != nil { + return err + } + + if m.Repo.HasWiki() { + wikiPath := m.Repo.WikiPath() + _, err := git.GetRemoteAddress(wikiPath, m.RemoteName) + if err == nil { + err := performPush(wikiPath) + if err != nil { + return err + } + } else { + log.Trace("Skipping wiki: No remote configured") + } + } + + return nil +} + +func pushAllLFSObjects(ctx context.Context, gitRepo *git.Repository, endpoint *url.URL) error { + client := lfs.NewClient(endpoint) + contentStore := lfs.NewContentStore() + + pointerChan := make(chan lfs.PointerBlob) + errChan := make(chan error, 1) + go lfs.SearchPointerBlobs(ctx, gitRepo, pointerChan, errChan) + + uploadObjects := func(pointers []lfs.Pointer) error { + err := client.Upload(ctx, pointers, func(p lfs.Pointer, objectError error) (io.ReadCloser, error) { + if objectError != nil { + return nil, objectError + } + + content, err := contentStore.Get(p) + if err != nil { + log.Error("Error reading LFS object %v: %v", p, err) + } + return content, err + }) + if err != nil { + select { + case <-ctx.Done(): + return nil + default: + } + } + return err + } + + var batch []lfs.Pointer + for pointerBlob := range pointerChan { + exists, err := contentStore.Exists(pointerBlob.Pointer) + if err != nil { + log.Error("Error checking if LFS object %v exists: %v", pointerBlob.Pointer, err) + return err + } + if !exists { + log.Trace("Skipping missing LFS object %v", pointerBlob.Pointer) + continue + } + + batch = append(batch, pointerBlob.Pointer) + if len(batch) >= client.BatchSize() { + if err := uploadObjects(batch); err != nil { + return err + } + batch = nil + } + } + if len(batch) > 0 { + if err := uploadObjects(batch); err != nil { + return err + } + } + + err, has := <-errChan + if has { + log.Error("Error enumerating LFS objects for repository: %v", err) + return err + } + + return nil +} diff --git a/services/pull/check.go b/services/pull/check.go index 3ec76de5e8748..9db1654cfbc05 100644 --- a/services/pull/check.go +++ b/services/pull/check.go @@ -28,21 +28,19 @@ var prQueue queue.UniqueQueue // AddToTaskQueue adds itself to pull request test task queue. func AddToTaskQueue(pr *models.PullRequest) { - go func() { - err := prQueue.PushFunc(strconv.FormatInt(pr.ID, 10), func() error { - pr.Status = models.PullRequestStatusChecking - err := pr.UpdateColsIfNotMerged("status") - if err != nil { - log.Error("AddToTaskQueue.UpdateCols[%d].(add to queue): %v", pr.ID, err) - } else { - log.Trace("Adding PR ID: %d to the test pull requests queue", pr.ID) - } - return err - }) - if err != nil && err != queue.ErrAlreadyInQueue { - log.Error("Error adding prID %d to the test pull requests queue: %v", pr.ID, err) + err := prQueue.PushFunc(strconv.FormatInt(pr.ID, 10), func() error { + pr.Status = models.PullRequestStatusChecking + err := pr.UpdateColsIfNotMerged("status") + if err != nil { + log.Error("AddToTaskQueue.UpdateCols[%d].(add to queue): %v", pr.ID, err) + } else { + log.Trace("Adding PR ID: %d to the test pull requests queue", pr.ID) } - }() + return err + }) + if err != nil && err != queue.ErrAlreadyInQueue { + log.Error("Error adding prID %d to the test pull requests queue: %v", pr.ID, err) + } } // checkAndUpdateStatus checks if pull request is possible to leaving checking status, diff --git a/services/pull/pull.go b/services/pull/pull.go index cc560fb199d2a..02c0a7fe7c878 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -570,16 +570,44 @@ func GetSquashMergeCommitMessages(pr *models.PullRequest) string { authors := make([]string, 0, list.Len()) stringBuilder := strings.Builder{} - stringBuilder.WriteString(pr.Issue.Content) - if stringBuilder.Len() > 0 { - stringBuilder.WriteRune('\n') - stringBuilder.WriteRune('\n') + if !setting.Repository.PullRequest.PopulateSquashCommentWithCommitMessages { + stringBuilder.WriteString(pr.Issue.Content) + if stringBuilder.Len() > 0 { + stringBuilder.WriteRune('\n') + stringBuilder.WriteRune('\n') + } } // commits list is in reverse chronological order element := list.Back() for element != nil { commit := element.Value.(*git.Commit) + + if setting.Repository.PullRequest.PopulateSquashCommentWithCommitMessages { + maxSize := setting.Repository.PullRequest.DefaultMergeMessageSize + if maxSize < 0 || stringBuilder.Len() < maxSize { + var toWrite []byte + if element == list.Back() { + toWrite = []byte(strings.TrimPrefix(commit.CommitMessage, pr.Issue.Title)) + } else { + toWrite = []byte(commit.CommitMessage) + } + + if len(toWrite) > maxSize-stringBuilder.Len() && maxSize > -1 { + toWrite = append(toWrite[:maxSize-stringBuilder.Len()], "..."...) + } + if _, err := stringBuilder.Write(toWrite); err != nil { + log.Error("Unable to write commit message Error: %v", err) + return "" + } + + if _, err := stringBuilder.WriteRune('\n'); err != nil { + log.Error("Unable to write commit message Error: %v", err) + return "" + } + } + } + authorString := commit.Author.String() if !authorsMap[authorString] && authorString != posterSig { authors = append(authors, authorString) diff --git a/services/pull/review.go b/services/pull/review.go index 4b647722fcb2b..b07e21fad9774 100644 --- a/services/pull/review.go +++ b/services/pull/review.go @@ -100,7 +100,7 @@ func CreateCodeComment(doer *models.User, gitRepo *git.Repository, issue *models if !isReview && !existsReview { // Submit the review we've just created so the comment shows up in the issue view - if _, _, err = SubmitReview(doer, gitRepo, issue, models.ReviewTypeComment, "", latestCommitID); err != nil { + if _, _, err = SubmitReview(doer, gitRepo, issue, models.ReviewTypeComment, "", latestCommitID, nil); err != nil { return nil, err } } @@ -215,7 +215,7 @@ func createCodeComment(doer *models.User, repo *models.Repository, issue *models } // SubmitReview creates a review out of the existing pending review or creates a new one if no pending review exist -func SubmitReview(doer *models.User, gitRepo *git.Repository, issue *models.Issue, reviewType models.ReviewType, content, commitID string) (*models.Review, *models.Comment, error) { +func SubmitReview(doer *models.User, gitRepo *git.Repository, issue *models.Issue, reviewType models.ReviewType, content, commitID string, attachmentUUIDs []string) (*models.Review, *models.Comment, error) { pr, err := issue.GetPullRequest() if err != nil { return nil, nil, err @@ -240,7 +240,7 @@ func SubmitReview(doer *models.User, gitRepo *git.Repository, issue *models.Issu } } - review, comm, err := models.SubmitReview(doer, issue, reviewType, content, commitID, stale) + review, comm, err := models.SubmitReview(doer, issue, reviewType, content, commitID, stale, attachmentUUIDs) if err != nil { return nil, nil, err } diff --git a/services/release/release_test.go b/services/release/release_test.go index 102e3d7e0c0e1..085be55cb4b1f 100644 --- a/services/release/release_test.go +++ b/services/release/release_test.go @@ -221,7 +221,7 @@ func TestRelease_Update(t *testing.T) { assert.NoError(t, UpdateRelease(user, gitRepo, release, []string{attach.UUID}, nil, nil)) assert.NoError(t, models.GetReleaseAttachments(release)) - assert.EqualValues(t, 1, len(release.Attachments)) + assert.Len(t, release.Attachments, 1) assert.EqualValues(t, attach.UUID, release.Attachments[0].UUID) assert.EqualValues(t, release.ID, release.Attachments[0].ReleaseID) assert.EqualValues(t, attach.Name, release.Attachments[0].Name) @@ -232,7 +232,7 @@ func TestRelease_Update(t *testing.T) { })) release.Attachments = nil assert.NoError(t, models.GetReleaseAttachments(release)) - assert.EqualValues(t, 1, len(release.Attachments)) + assert.Len(t, release.Attachments, 1) assert.EqualValues(t, attach.UUID, release.Attachments[0].UUID) assert.EqualValues(t, release.ID, release.Attachments[0].ReleaseID) assert.EqualValues(t, "test2.txt", release.Attachments[0].Name) @@ -241,7 +241,7 @@ func TestRelease_Update(t *testing.T) { assert.NoError(t, UpdateRelease(user, gitRepo, release, nil, []string{attach.UUID}, nil)) release.Attachments = nil assert.NoError(t, models.GetReleaseAttachments(release)) - assert.EqualValues(t, 0, len(release.Attachments)) + assert.Empty(t, release.Attachments) } func TestRelease_createTag(t *testing.T) { diff --git a/services/repository/branch.go b/services/repository/branch.go new file mode 100644 index 0000000000000..df07030be3a03 --- /dev/null +++ b/services/repository/branch.go @@ -0,0 +1,72 @@ +// 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 repository + +import ( + "errors" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" + repo_module "code.gitea.io/gitea/modules/repository" + pull_service "code.gitea.io/gitea/services/pull" +) + +// enmuerates all branch related errors +var ( + ErrBranchIsDefault = errors.New("branch is default") + ErrBranchIsProtected = errors.New("branch is protected") +) + +// DeleteBranch delete branch +func DeleteBranch(doer *models.User, repo *models.Repository, gitRepo *git.Repository, branchName string) error { + if branchName == repo.DefaultBranch { + return ErrBranchIsDefault + } + + isProtected, err := repo.IsProtectedBranch(branchName, doer) + if err != nil { + return err + } + + if isProtected { + return ErrBranchIsProtected + } + + commit, err := gitRepo.GetBranchCommit(branchName) + if err != nil { + return err + } + + if err := gitRepo.DeleteBranch(branchName, git.DeleteBranchOptions{ + Force: true, + }); err != nil { + return err + } + + if err := pull_service.CloseBranchPulls(doer, repo.ID, branchName); err != nil { + return err + } + + // Don't return error below this + if err := PushUpdate( + &repo_module.PushUpdateOptions{ + RefFullName: git.BranchPrefix + branchName, + OldCommitID: commit.ID.String(), + NewCommitID: git.EmptySHA, + PusherID: doer.ID, + PusherName: doer.Name, + RepoUserName: repo.OwnerName, + RepoName: repo.Name, + }); err != nil { + log.Error("Update: %v", err) + } + + if err := repo.AddDeletedBranch(branchName, commit.ID.String(), doer.ID); err != nil { + log.Warn("AddDeletedBranch: %v", err) + } + + return nil +} diff --git a/services/repository/push.go b/services/repository/push.go index bed5c575fe81d..f031073b2e0e0 100644 --- a/services/repository/push.go +++ b/services/repository/push.go @@ -208,7 +208,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { } // Cache for big repository - if err := repo_module.CacheRef(repo, gitRepo, opts.RefFullName); err != nil { + if err := repo_module.CacheRef(graceful.GetManager().HammerContext(), repo, gitRepo, opts.RefFullName); err != nil { log.Error("repo_module.CacheRef %s/%s failed: %v", repo.ID, branch, err) } } else { diff --git a/services/repository/transfer.go b/services/repository/transfer.go index ec769190bdb49..bb323c1c0a23b 100644 --- a/services/repository/transfer.go +++ b/services/repository/transfer.go @@ -94,6 +94,20 @@ func StartRepositoryTransfer(doer, newOwner *models.User, repo *models.Repositor } } + // In case the new owner would not have sufficient access to the repo, give access rights for read + hasAccess, err := models.HasAccess(newOwner.ID, repo) + if err != nil { + return err + } + if !hasAccess { + if err := repo.AddCollaborator(newOwner); err != nil { + return err + } + if err := repo.ChangeCollaborationAccessMode(newOwner.ID, models.AccessModeRead); err != nil { + return err + } + } + // Make repo as pending for transfer repo.Status = models.RepositoryPendingTransfer if err := models.CreatePendingRepositoryTransfer(doer, newOwner, repo.ID, teams); err != nil { diff --git a/services/repository/transfer_test.go b/services/repository/transfer_test.go index 052b8c9954285..c92844674c6d4 100644 --- a/services/repository/transfer_test.go +++ b/services/repository/transfer_test.go @@ -52,3 +52,24 @@ func TestTransferOwnership(t *testing.T) { models.CheckConsistencyFor(t, &models.Repository{}, &models.User{}, &models.Team{}) } + +func TestStartRepositoryTransferSetPermission(t *testing.T) { + assert.NoError(t, models.PrepareTestDatabase()) + + doer := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) + recipient := models.AssertExistsAndLoadBean(t, &models.User{ID: 5}).(*models.User) + repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository) + repo.Owner = models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User) + + hasAccess, err := models.HasAccess(recipient.ID, repo) + assert.NoError(t, err) + assert.False(t, hasAccess) + + assert.NoError(t, StartRepositoryTransfer(doer, recipient, repo, nil)) + + hasAccess, err = models.HasAccess(recipient.ID, repo) + assert.NoError(t, err) + assert.True(t, hasAccess) + + models.CheckConsistencyFor(t, &models.Repository{}, &models.User{}, &models.Team{}) +} diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 6f19fe5ed6bd3..cd989dfbbf0a2 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -25,14 +25,14 @@ environment: apps: gitea: command: gitea - plugs: [network, network-bind] + plugs: [network, network-bind, removable-media] web: command: gitea web daemon: simple - plugs: [network, network-bind] + plugs: [network, network-bind, removable-media] dump: command: gitea dump - plugs: [home] + plugs: [home, removable-media] version: command: gitea --version sqlite: diff --git a/templates/base/head_navbar.tmpl b/templates/base/head_navbar.tmpl index fcc5fb25e78b5..a9ca42fde60fc 100644 --- a/templates/base/head_navbar.tmpl +++ b/templates/base/head_navbar.tmpl @@ -1,7 +1,7 @@ + {{if .Attachments}} + {{template "repo/issue/view_content/attachments" Dict "ctx" $ "Attachments" .Attachments "Content" .RenderedContent}} + {{end}} diff --git a/templates/repo/migrate/migrating.tmpl b/templates/repo/migrate/migrating.tmpl index e4a3ec81f713a..c1f189553f6fc 100644 --- a/templates/repo/migrate/migrating.tmpl +++ b/templates/repo/migrate/migrating.tmpl @@ -22,6 +22,7 @@

{{.i18n.Tr "repo.migrate.migrating" .CloneAddr | Safe}}

+

- {{if .Repository.IsMirror}} + {{if or .Repository.IsMirror (not .DisabledMirrors)}}

{{.i18n.Tr "repo.settings.mirror_settings"}}

-
- {{.CsrfTokenHtml}} - -
- -
- - -
-
-
- - -
-
- - -

{{.i18n.Tr "repo.mirror_address_desc"}}

-
-
- - {{.i18n.Tr "repo.need_auth"}} - -
-
- - -
- -
- - -
-

{{.i18n.Tr "repo.mirror_password_help"}}

-
-
- - {{if .LFSStartServer}} -
- -
- - -
-
-
- - -

{{.i18n.Tr "repo.mirror_lfs_endpoint_desc" "https://github.com/git-lfs/git-lfs/blob/main/docs/api/server-discovery.md#server-discovery" | Str2html}}

-
+ {{$.i18n.Tr "repo.settings.mirror_settings.docs" | Safe}} + + {{if or .Repository.IsMirror .Repository.PushMirrors}} + + + + + + + + {{end}} - -
- -
- - -
- - - {{.CsrfTokenHtml}} - -
- - {{.Mirror.UpdatedUnix.AsTime}} -
-
- -
- + {{if .Repository.IsMirror}} + + + + + + + + + + + + + {{end}} + + {{range .Repository.PushMirrors}} + + {{$address := MirrorRemoteAddress .}} + + + + + + {{else}} + + + + {{end}} + + + + +
{{$.i18n.Tr "repo.settings.mirror_settings.mirrored_repository"}}{{$.i18n.Tr "repo.settings.mirror_settings.direction"}}{{$.i18n.Tr "repo.settings.mirror_settings.last_update"}}
{{(MirrorRemoteAddress .Mirror).Address}}{{$.i18n.Tr "repo.settings.mirror_settings.direction.pull"}}{{.Mirror.UpdatedUnix.AsTime}} +
+ {{.CsrfTokenHtml}} + + +
+
+
+ {{.CsrfTokenHtml}} + +
+ +
+ + +
+
+
+ + +
+ {{$address := MirrorRemoteAddress .Mirror}} +
+ + +

{{.i18n.Tr "repo.mirror_address_desc"}}

+
+
+ + {{.i18n.Tr "repo.need_auth"}} + +
+
+ + +
+ +
+ + +
+

{{.i18n.Tr "repo.mirror_password_help"}}

+
+
+ + {{if .LFSStartServer}} +
+ +
+ + +
+
+
+ + +

{{.i18n.Tr "repo.mirror_lfs_endpoint_desc" "https://github.com/git-lfs/git-lfs/blob/main/docs/api/server-discovery.md#server-discovery" | Str2html}}

+
+ {{end}} +
+ +
+
+
{{$address.Address}}{{$.i18n.Tr "repo.settings.mirror_settings.direction.push"}}{{if .LastUpdateUnix}}{{.LastUpdateUnix.AsTime}}{{else}}{{$.i18n.Tr "never"}}{{end}} {{if .LastError}}
{{$.i18n.Tr "error"}}
{{end}}
+
+ {{$.CsrfTokenHtml}} + + + +
+
+ {{$.CsrfTokenHtml}} + + + +
+
{{$.i18n.Tr "repo.settings.mirror_settings.push_mirror.none"}}
+
+ {{.CsrfTokenHtml}} + +
+ + +

{{.i18n.Tr "repo.mirror_address_desc"}}

+
+
+ + {{.i18n.Tr "repo.need_auth"}} + +
+
+ + +
+ +
+ + +
+
+
+
+ + +
+
+ +
+
+
{{end}} @@ -649,7 +733,8 @@
{{.i18n.Tr "repo.settings.transfer_notices_1"}}
- {{.i18n.Tr "repo.settings.transfer_notices_2"}} + {{.i18n.Tr "repo.settings.transfer_notices_2"}}
+ {{.i18n.Tr "repo.settings.transfer_notices_3"}}
{{.CsrfTokenHtml}} diff --git a/templates/repo/upload.tmpl b/templates/repo/upload.tmpl index 9215da2b19670..3dd40d1b27bcb 100644 --- a/templates/repo/upload.tmpl +++ b/templates/repo/upload.tmpl @@ -1,6 +1,5 @@
+> +
+
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index e3ac4a4c8a68c..017dc824d7d62 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -630,7 +630,7 @@ "operationId": "notifyGetList", "parameters": [ { - "type": "string", + "type": "boolean", "description": "If true, show notifications marked as read. Default value is false", "name": "all", "in": "query" @@ -645,6 +645,22 @@ "name": "status-types", "in": "query" }, + { + "type": "array", + "items": { + "enum": [ + "issue", + "pull", + "commit", + "repository" + ], + "type": "string" + }, + "collectionFormat": "multi", + "description": "filter notifications by subject type", + "name": "subject-type", + "in": "query" + }, { "type": "string", "format": "date-time", @@ -1860,6 +1876,12 @@ "name": "labels", "in": "query" }, + { + "type": "string", + "description": "comma separated list of milestone names. Fetch only issues that have any of this milestones. Non existent are discarded", + "name": "milestones", + "in": "query" + }, { "type": "string", "description": "search string", @@ -2255,6 +2277,39 @@ } } }, + "/repos/{owner}/{repo}/assignees": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "Return all users that have write access and can be assigned to issues", + "operationId": "repoGetAssignees", + "parameters": [ + { + "type": "string", + "description": "owner of the repo", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repo", + "name": "repo", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/UserList" + } + } + } + }, "/repos/{owner}/{repo}/branch_protections": { "get": { "produces": [ @@ -4218,6 +4273,38 @@ "name": "milestones", "in": "query" }, + { + "type": "string", + "format": "date-time", + "description": "Only show notifications updated after the given time. This is a timestamp in RFC 3339 format", + "name": "since", + "in": "query" + }, + { + "type": "string", + "format": "date-time", + "description": "Only show notifications updated before the given time. This is a timestamp in RFC 3339 format", + "name": "before", + "in": "query" + }, + { + "type": "string", + "description": "filter (issues / pulls) created to", + "name": "created_by", + "in": "query" + }, + { + "type": "string", + "description": "filter (issues / pulls) assigned to", + "name": "assigned_by", + "in": "query" + }, + { + "type": "string", + "description": "filter (issues / pulls) mentioning to", + "name": "mentioned_by", + "in": "query" + }, { "type": "integer", "description": "page number of results to return (1-based)", @@ -6790,7 +6877,7 @@ "required": true }, { - "type": "string", + "type": "boolean", "description": "If true, show notifications marked as read. Default value is false", "name": "all", "in": "query" @@ -6805,6 +6892,22 @@ "name": "status-types", "in": "query" }, + { + "type": "array", + "items": { + "enum": [ + "issue", + "pull", + "commit", + "repository" + ], + "type": "string" + }, + "collectionFormat": "multi", + "description": "filter notifications by subject type", + "name": "subject-type", + "in": "query" + }, { "type": "string", "format": "date-time", @@ -8006,6 +8109,18 @@ "in": "path", "required": true }, + { + "type": "boolean", + "description": "filter (exclude / include) drafts, if you dont have repo write access none will show", + "name": "draft", + "in": "query" + }, + { + "type": "boolean", + "description": "filter (exclude / include) pre-releases", + "name": "pre-release", + "in": "query" + }, { "type": "integer", "description": "page size of results, deprecated - use limit", @@ -8547,6 +8662,39 @@ } } }, + "/repos/{owner}/{repo}/reviewers": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "Return all users that can be requested to review in this repo", + "operationId": "repoGetReviewers", + "parameters": [ + { + "type": "string", + "description": "owner of the repo", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repo", + "name": "repo", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/UserList" + } + } + } + }, "/repos/{owner}/{repo}/signing-key.gpg": { "get": { "produces": [ @@ -8934,6 +9082,50 @@ "$ref": "#/responses/TagList" } } + }, + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "Create a new git tag in a repository", + "operationId": "repoCreateTag", + "parameters": [ + { + "type": "string", + "description": "owner of the repo", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repo", + "name": "repo", + "in": "path", + "required": true + }, + { + "name": "body", + "in": "body", + "schema": { + "$ref": "#/definitions/CreateTagOption" + } + } + ], + "responses": { + "200": { + "$ref": "#/responses/AnnotatedTag" + }, + "404": { + "$ref": "#/responses/notFound" + }, + "409": { + "$ref": "#/responses/conflict" + } + } } }, "/repos/{owner}/{repo}/tags/{tag}": { @@ -12944,6 +13136,28 @@ }, "x-go-package": "code.gitea.io/gitea/modules/structs" }, + "CreateTagOption": { + "description": "CreateTagOption options when creating a tag", + "type": "object", + "required": [ + "tag_name" + ], + "properties": { + "message": { + "type": "string", + "x-go-name": "Message" + }, + "tag_name": { + "type": "string", + "x-go-name": "TagName" + }, + "target": { + "type": "string", + "x-go-name": "Target" + } + }, + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, "CreateTeamOption": { "description": "CreateTeamOption options for creating a team", "type": "object", @@ -16001,6 +16215,10 @@ "type": "string", "x-go-name": "ID" }, + "message": { + "type": "string", + "x-go-name": "Message" + }, "name": { "type": "string", "x-go-name": "Name" @@ -16275,6 +16493,17 @@ "format": "email", "x-go-name": "Email" }, + "followers_count": { + "description": "user counts", + "type": "integer", + "format": "int64", + "x-go-name": "Followers" + }, + "following_count": { + "type": "integer", + "format": "int64", + "x-go-name": "Following" + }, "full_name": { "description": "the user's full name", "type": "string", @@ -16321,6 +16550,11 @@ "type": "boolean", "x-go-name": "Restricted" }, + "starred_repos_count": { + "type": "integer", + "format": "int64", + "x-go-name": "StarredRepos" + }, "website": { "description": "the user's website", "type": "string", @@ -17101,7 +17335,7 @@ "parameterBodies": { "description": "parameterBodies", "schema": { - "$ref": "#/definitions/PullReviewRequestOptions" + "$ref": "#/definitions/CreateTagOption" } }, "redirect": { diff --git a/templates/user/auth/oidc_wellknown.tmpl b/templates/user/auth/oidc_wellknown.tmpl index fcde060a8d19f..93a048b513da2 100644 --- a/templates/user/auth/oidc_wellknown.tmpl +++ b/templates/user/auth/oidc_wellknown.tmpl @@ -2,9 +2,45 @@ "issuer": "{{AppUrl | JSEscape | Safe}}", "authorization_endpoint": "{{AppUrl | JSEscape | Safe}}login/oauth/authorize", "token_endpoint": "{{AppUrl | JSEscape | Safe}}login/oauth/access_token", + "jwks_uri": "{{AppUrl | JSEscape | Safe}}login/oauth/keys", "userinfo_endpoint": "{{AppUrl | JSEscape | Safe}}login/oauth/userinfo", "response_types_supported": [ "code", "id_token" + ], + "id_token_signing_alg_values_supported": [ + "{{.SigningKey.SigningMethod.Alg | JSEscape | Safe}}" + ], + "subject_types_supported": [ + "public" + ], + "scopes_supported": [ + "openid", + "profile", + "email" + ], + "claims_supported": [ + "aud", + "exp", + "iat", + "iss", + "sub", + "name", + "preferred_username", + "profile", + "picture", + "website", + "locale", + "updated_at", + "email", + "email_verified" + ], + "code_challenge_methods_supported": [ + "plain", + "S256" + ], + "grant_types_supported": [ + "authorization_code", + "refresh_token" ] } diff --git a/templates/user/dashboard/repolist.tmpl b/templates/user/dashboard/repolist.tmpl index 8ac07e1df63ea..f39d3711d473f 100644 --- a/templates/user/dashboard/repolist.tmpl +++ b/templates/user/dashboard/repolist.tmpl @@ -9,7 +9,7 @@ :more-repos-link="'{{.ContextUser.HomeLink}}'" {{if not .ContextUser.IsOrganization}} :organizations="[ - {{range .ContextUser.Orgs}} + {{range .Orgs}} {name: '{{.Name}}', num_repos: '{{.NumRepos}}'}, {{end}} ]" diff --git a/vendor/github.com/Microsoft/go-winio/README.md b/vendor/github.com/Microsoft/go-winio/README.md index 5680010575b7f..60c93fe50684c 100644 --- a/vendor/github.com/Microsoft/go-winio/README.md +++ b/vendor/github.com/Microsoft/go-winio/README.md @@ -1,4 +1,4 @@ -# go-winio +# go-winio [![Build Status](https://github.com/microsoft/go-winio/actions/workflows/ci.yml/badge.svg)](https://github.com/microsoft/go-winio/actions/workflows/ci.yml) This repository contains utilities for efficiently performing Win32 IO operations in Go. Currently, this is focused on accessing named pipes and other file handles, and diff --git a/vendor/github.com/ProtonMail/go-crypto/AUTHORS b/vendor/github.com/ProtonMail/go-crypto/AUTHORS new file mode 100644 index 0000000000000..2b00ddba0dfee --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/AUTHORS @@ -0,0 +1,3 @@ +# This source code refers to The Go Authors for copyright purposes. +# The master list of authors is in the main Go distribution, +# visible at https://tip.golang.org/AUTHORS. diff --git a/vendor/github.com/ProtonMail/go-crypto/CONTRIBUTORS b/vendor/github.com/ProtonMail/go-crypto/CONTRIBUTORS new file mode 100644 index 0000000000000..1fbd3e976faf5 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/CONTRIBUTORS @@ -0,0 +1,3 @@ +# This source code was written by the Go contributors. +# The master list of contributors is in the main Go distribution, +# visible at https://tip.golang.org/CONTRIBUTORS. diff --git a/vendor/github.com/ProtonMail/go-crypto/LICENSE b/vendor/github.com/ProtonMail/go-crypto/LICENSE new file mode 100644 index 0000000000000..6a66aea5eafe0 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/ProtonMail/go-crypto/PATENTS b/vendor/github.com/ProtonMail/go-crypto/PATENTS new file mode 100644 index 0000000000000..733099041f84f --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google 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, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/vendor/github.com/ProtonMail/go-crypto/bitcurves/bitcurve.go b/vendor/github.com/ProtonMail/go-crypto/bitcurves/bitcurve.go new file mode 100644 index 0000000000000..3ed3f43573794 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/bitcurves/bitcurve.go @@ -0,0 +1,381 @@ +package bitcurves + +// Copyright 2010 The Go Authors. All rights reserved. +// Copyright 2011 ThePiachu. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package bitelliptic implements several Koblitz elliptic curves over prime +// fields. + +// This package operates, internally, on Jacobian coordinates. For a given +// (x, y) position on the curve, the Jacobian coordinates are (x1, y1, z1) +// where x = x1/z1² and y = y1/z1³. The greatest speedups come when the whole +// calculation can be performed within the transform (as in ScalarMult and +// ScalarBaseMult). But even for Add and Double, it's faster to apply and +// reverse the transform than to operate in affine coordinates. + +import ( + "crypto/elliptic" + "io" + "math/big" + "sync" +) + +// A BitCurve represents a Koblitz Curve with a=0. +// See http://www.hyperelliptic.org/EFD/g1p/auto-shortw.html +type BitCurve struct { + Name string + P *big.Int // the order of the underlying field + N *big.Int // the order of the base point + B *big.Int // the constant of the BitCurve equation + Gx, Gy *big.Int // (x,y) of the base point + BitSize int // the size of the underlying field +} + +// Params returns the parameters of the given BitCurve (see BitCurve struct) +func (bitCurve *BitCurve) Params() (cp *elliptic.CurveParams) { + cp = new(elliptic.CurveParams) + cp.Name = bitCurve.Name + cp.P = bitCurve.P + cp.N = bitCurve.N + cp.Gx = bitCurve.Gx + cp.Gy = bitCurve.Gy + cp.BitSize = bitCurve.BitSize + return cp +} + +// IsOnCurve returns true if the given (x,y) lies on the BitCurve. +func (bitCurve *BitCurve) IsOnCurve(x, y *big.Int) bool { + // y² = x³ + b + y2 := new(big.Int).Mul(y, y) //y² + y2.Mod(y2, bitCurve.P) //y²%P + + x3 := new(big.Int).Mul(x, x) //x² + x3.Mul(x3, x) //x³ + + x3.Add(x3, bitCurve.B) //x³+B + x3.Mod(x3, bitCurve.P) //(x³+B)%P + + return x3.Cmp(y2) == 0 +} + +// affineFromJacobian reverses the Jacobian transform. See the comment at the +// top of the file. +func (bitCurve *BitCurve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) { + if z.Cmp(big.NewInt(0)) == 0 { + panic("bitcurve: Can't convert to affine with Jacobian Z = 0") + } + // x = YZ^2 mod P + zinv := new(big.Int).ModInverse(z, bitCurve.P) + zinvsq := new(big.Int).Mul(zinv, zinv) + + xOut = new(big.Int).Mul(x, zinvsq) + xOut.Mod(xOut, bitCurve.P) + // y = YZ^3 mod P + zinvsq.Mul(zinvsq, zinv) + yOut = new(big.Int).Mul(y, zinvsq) + yOut.Mod(yOut, bitCurve.P) + return xOut, yOut +} + +// Add returns the sum of (x1,y1) and (x2,y2) +func (bitCurve *BitCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) { + z := new(big.Int).SetInt64(1) + x, y, z := bitCurve.addJacobian(x1, y1, z, x2, y2, z) + return bitCurve.affineFromJacobian(x, y, z) +} + +// addJacobian takes two points in Jacobian coordinates, (x1, y1, z1) and +// (x2, y2, z2) and returns their sum, also in Jacobian form. +func (bitCurve *BitCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) { + // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + z1z1 := new(big.Int).Mul(z1, z1) + z1z1.Mod(z1z1, bitCurve.P) + z2z2 := new(big.Int).Mul(z2, z2) + z2z2.Mod(z2z2, bitCurve.P) + + u1 := new(big.Int).Mul(x1, z2z2) + u1.Mod(u1, bitCurve.P) + u2 := new(big.Int).Mul(x2, z1z1) + u2.Mod(u2, bitCurve.P) + h := new(big.Int).Sub(u2, u1) + if h.Sign() == -1 { + h.Add(h, bitCurve.P) + } + i := new(big.Int).Lsh(h, 1) + i.Mul(i, i) + j := new(big.Int).Mul(h, i) + + s1 := new(big.Int).Mul(y1, z2) + s1.Mul(s1, z2z2) + s1.Mod(s1, bitCurve.P) + s2 := new(big.Int).Mul(y2, z1) + s2.Mul(s2, z1z1) + s2.Mod(s2, bitCurve.P) + r := new(big.Int).Sub(s2, s1) + if r.Sign() == -1 { + r.Add(r, bitCurve.P) + } + r.Lsh(r, 1) + v := new(big.Int).Mul(u1, i) + + x3 := new(big.Int).Set(r) + x3.Mul(x3, x3) + x3.Sub(x3, j) + x3.Sub(x3, v) + x3.Sub(x3, v) + x3.Mod(x3, bitCurve.P) + + y3 := new(big.Int).Set(r) + v.Sub(v, x3) + y3.Mul(y3, v) + s1.Mul(s1, j) + s1.Lsh(s1, 1) + y3.Sub(y3, s1) + y3.Mod(y3, bitCurve.P) + + z3 := new(big.Int).Add(z1, z2) + z3.Mul(z3, z3) + z3.Sub(z3, z1z1) + if z3.Sign() == -1 { + z3.Add(z3, bitCurve.P) + } + z3.Sub(z3, z2z2) + if z3.Sign() == -1 { + z3.Add(z3, bitCurve.P) + } + z3.Mul(z3, h) + z3.Mod(z3, bitCurve.P) + + return x3, y3, z3 +} + +// Double returns 2*(x,y) +func (bitCurve *BitCurve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) { + z1 := new(big.Int).SetInt64(1) + return bitCurve.affineFromJacobian(bitCurve.doubleJacobian(x1, y1, z1)) +} + +// doubleJacobian takes a point in Jacobian coordinates, (x, y, z), and +// returns its double, also in Jacobian form. +func (bitCurve *BitCurve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.Int) { + // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l + + a := new(big.Int).Mul(x, x) //X1² + b := new(big.Int).Mul(y, y) //Y1² + c := new(big.Int).Mul(b, b) //B² + + d := new(big.Int).Add(x, b) //X1+B + d.Mul(d, d) //(X1+B)² + d.Sub(d, a) //(X1+B)²-A + d.Sub(d, c) //(X1+B)²-A-C + d.Mul(d, big.NewInt(2)) //2*((X1+B)²-A-C) + + e := new(big.Int).Mul(big.NewInt(3), a) //3*A + f := new(big.Int).Mul(e, e) //E² + + x3 := new(big.Int).Mul(big.NewInt(2), d) //2*D + x3.Sub(f, x3) //F-2*D + x3.Mod(x3, bitCurve.P) + + y3 := new(big.Int).Sub(d, x3) //D-X3 + y3.Mul(e, y3) //E*(D-X3) + y3.Sub(y3, new(big.Int).Mul(big.NewInt(8), c)) //E*(D-X3)-8*C + y3.Mod(y3, bitCurve.P) + + z3 := new(big.Int).Mul(y, z) //Y1*Z1 + z3.Mul(big.NewInt(2), z3) //3*Y1*Z1 + z3.Mod(z3, bitCurve.P) + + return x3, y3, z3 +} + +//TODO: double check if it is okay +// ScalarMult returns k*(Bx,By) where k is a number in big-endian form. +func (bitCurve *BitCurve) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) { + // We have a slight problem in that the identity of the group (the + // point at infinity) cannot be represented in (x, y) form on a finite + // machine. Thus the standard add/double algorithm has to be tweaked + // slightly: our initial state is not the identity, but x, and we + // ignore the first true bit in |k|. If we don't find any true bits in + // |k|, then we return nil, nil, because we cannot return the identity + // element. + + Bz := new(big.Int).SetInt64(1) + x := Bx + y := By + z := Bz + + seenFirstTrue := false + for _, byte := range k { + for bitNum := 0; bitNum < 8; bitNum++ { + if seenFirstTrue { + x, y, z = bitCurve.doubleJacobian(x, y, z) + } + if byte&0x80 == 0x80 { + if !seenFirstTrue { + seenFirstTrue = true + } else { + x, y, z = bitCurve.addJacobian(Bx, By, Bz, x, y, z) + } + } + byte <<= 1 + } + } + + if !seenFirstTrue { + return nil, nil + } + + return bitCurve.affineFromJacobian(x, y, z) +} + +// ScalarBaseMult returns k*G, where G is the base point of the group and k is +// an integer in big-endian form. +func (bitCurve *BitCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) { + return bitCurve.ScalarMult(bitCurve.Gx, bitCurve.Gy, k) +} + +var mask = []byte{0xff, 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f} + +//TODO: double check if it is okay +// GenerateKey returns a public/private key pair. The private key is generated +// using the given reader, which must return random data. +func (bitCurve *BitCurve) GenerateKey(rand io.Reader) (priv []byte, x, y *big.Int, err error) { + byteLen := (bitCurve.BitSize + 7) >> 3 + priv = make([]byte, byteLen) + + for x == nil { + _, err = io.ReadFull(rand, priv) + if err != nil { + return + } + // We have to mask off any excess bits in the case that the size of the + // underlying field is not a whole number of bytes. + priv[0] &= mask[bitCurve.BitSize%8] + // This is because, in tests, rand will return all zeros and we don't + // want to get the point at infinity and loop forever. + priv[1] ^= 0x42 + x, y = bitCurve.ScalarBaseMult(priv) + } + return +} + +// Marshal converts a point into the form specified in section 4.3.6 of ANSI +// X9.62. +func (bitCurve *BitCurve) Marshal(x, y *big.Int) []byte { + byteLen := (bitCurve.BitSize + 7) >> 3 + + ret := make([]byte, 1+2*byteLen) + ret[0] = 4 // uncompressed point + + xBytes := x.Bytes() + copy(ret[1+byteLen-len(xBytes):], xBytes) + yBytes := y.Bytes() + copy(ret[1+2*byteLen-len(yBytes):], yBytes) + return ret +} + +// Unmarshal converts a point, serialised by Marshal, into an x, y pair. On +// error, x = nil. +func (bitCurve *BitCurve) Unmarshal(data []byte) (x, y *big.Int) { + byteLen := (bitCurve.BitSize + 7) >> 3 + if len(data) != 1+2*byteLen { + return + } + if data[0] != 4 { // uncompressed form + return + } + x = new(big.Int).SetBytes(data[1 : 1+byteLen]) + y = new(big.Int).SetBytes(data[1+byteLen:]) + return +} + +//curve parameters taken from: +//http://www.secg.org/collateral/sec2_final.pdf + +var initonce sync.Once +var secp160k1 *BitCurve +var secp192k1 *BitCurve +var secp224k1 *BitCurve +var secp256k1 *BitCurve + +func initAll() { + initS160() + initS192() + initS224() + initS256() +} + +func initS160() { + // See SEC 2 section 2.4.1 + secp160k1 = new(BitCurve) + secp160k1.Name = "secp160k1" + secp160k1.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73", 16) + secp160k1.N, _ = new(big.Int).SetString("0100000000000000000001B8FA16DFAB9ACA16B6B3", 16) + secp160k1.B, _ = new(big.Int).SetString("0000000000000000000000000000000000000007", 16) + secp160k1.Gx, _ = new(big.Int).SetString("3B4C382CE37AA192A4019E763036F4F5DD4D7EBB", 16) + secp160k1.Gy, _ = new(big.Int).SetString("938CF935318FDCED6BC28286531733C3F03C4FEE", 16) + secp160k1.BitSize = 160 +} + +func initS192() { + // See SEC 2 section 2.5.1 + secp192k1 = new(BitCurve) + secp192k1.Name = "secp192k1" + secp192k1.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37", 16) + secp192k1.N, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D", 16) + secp192k1.B, _ = new(big.Int).SetString("000000000000000000000000000000000000000000000003", 16) + secp192k1.Gx, _ = new(big.Int).SetString("DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D", 16) + secp192k1.Gy, _ = new(big.Int).SetString("9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D", 16) + secp192k1.BitSize = 192 +} + +func initS224() { + // See SEC 2 section 2.6.1 + secp224k1 = new(BitCurve) + secp224k1.Name = "secp224k1" + secp224k1.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D", 16) + secp224k1.N, _ = new(big.Int).SetString("010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7", 16) + secp224k1.B, _ = new(big.Int).SetString("00000000000000000000000000000000000000000000000000000005", 16) + secp224k1.Gx, _ = new(big.Int).SetString("A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C", 16) + secp224k1.Gy, _ = new(big.Int).SetString("7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5", 16) + secp224k1.BitSize = 224 +} + +func initS256() { + // See SEC 2 section 2.7.1 + secp256k1 = new(BitCurve) + secp256k1.Name = "secp256k1" + secp256k1.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16) + secp256k1.N, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16) + secp256k1.B, _ = new(big.Int).SetString("0000000000000000000000000000000000000000000000000000000000000007", 16) + secp256k1.Gx, _ = new(big.Int).SetString("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16) + secp256k1.Gy, _ = new(big.Int).SetString("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 16) + secp256k1.BitSize = 256 +} + +// S160 returns a BitCurve which implements secp160k1 (see SEC 2 section 2.4.1) +func S160() *BitCurve { + initonce.Do(initAll) + return secp160k1 +} + +// S192 returns a BitCurve which implements secp192k1 (see SEC 2 section 2.5.1) +func S192() *BitCurve { + initonce.Do(initAll) + return secp192k1 +} + +// S224 returns a BitCurve which implements secp224k1 (see SEC 2 section 2.6.1) +func S224() *BitCurve { + initonce.Do(initAll) + return secp224k1 +} + +// S256 returns a BitCurve which implements bitcurves (see SEC 2 section 2.7.1) +func S256() *BitCurve { + initonce.Do(initAll) + return secp256k1 +} diff --git a/vendor/github.com/ProtonMail/go-crypto/brainpool/brainpool.go b/vendor/github.com/ProtonMail/go-crypto/brainpool/brainpool.go new file mode 100644 index 0000000000000..77fb8b9a0461b --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/brainpool/brainpool.go @@ -0,0 +1,134 @@ +// Package brainpool implements Brainpool elliptic curves. +// Implementation of rcurves is from github.com/ebfe/brainpool +// Note that these curves are implemented with naive, non-constant time operations +// and are likely not suitable for enviroments where timing attacks are a concern. +package brainpool + +import ( + "crypto/elliptic" + "math/big" + "sync" +) + +var ( + once sync.Once + p256t1, p384t1, p512t1 *elliptic.CurveParams + p256r1, p384r1, p512r1 *rcurve +) + +func initAll() { + initP256t1() + initP384t1() + initP512t1() + initP256r1() + initP384r1() + initP512r1() +} + +func initP256t1() { + p256t1 = &elliptic.CurveParams{Name: "brainpoolP256t1"} + p256t1.P, _ = new(big.Int).SetString("A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377", 16) + p256t1.N, _ = new(big.Int).SetString("A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7", 16) + p256t1.B, _ = new(big.Int).SetString("662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04", 16) + p256t1.Gx, _ = new(big.Int).SetString("A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F4", 16) + p256t1.Gy, _ = new(big.Int).SetString("2D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE", 16) + p256t1.BitSize = 256 +} + +func initP256r1() { + twisted := p256t1 + params := &elliptic.CurveParams{ + Name: "brainpoolP256r1", + P: twisted.P, + N: twisted.N, + BitSize: twisted.BitSize, + } + params.Gx, _ = new(big.Int).SetString("8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262", 16) + params.Gy, _ = new(big.Int).SetString("547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997", 16) + z, _ := new(big.Int).SetString("3E2D4BD9597B58639AE7AA669CAB9837CF5CF20A2C852D10F655668DFC150EF0", 16) + p256r1 = newrcurve(twisted, params, z) +} + +func initP384t1() { + p384t1 = &elliptic.CurveParams{Name: "brainpoolP384t1"} + p384t1.P, _ = new(big.Int).SetString("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53", 16) + p384t1.N, _ = new(big.Int).SetString("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC3103B883202E9046565", 16) + p384t1.B, _ = new(big.Int).SetString("7F519EADA7BDA81BD826DBA647910F8C4B9346ED8CCDC64E4B1ABD11756DCE1D2074AA263B88805CED70355A33B471EE", 16) + p384t1.Gx, _ = new(big.Int).SetString("18DE98B02DB9A306F2AFCD7235F72A819B80AB12EBD653172476FECD462AABFFC4FF191B946A5F54D8D0AA2F418808CC", 16) + p384t1.Gy, _ = new(big.Int).SetString("25AB056962D30651A114AFD2755AD336747F93475B7A1FCA3B88F2B6A208CCFE469408584DC2B2912675BF5B9E582928", 16) + p384t1.BitSize = 384 +} + +func initP384r1() { + twisted := p384t1 + params := &elliptic.CurveParams{ + Name: "brainpoolP384r1", + P: twisted.P, + N: twisted.N, + BitSize: twisted.BitSize, + } + params.Gx, _ = new(big.Int).SetString("1D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D646AAEF87B2E247D4AF1E", 16) + params.Gy, _ = new(big.Int).SetString("8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E4646217791811142820341263C5315", 16) + z, _ := new(big.Int).SetString("41DFE8DD399331F7166A66076734A89CD0D2BCDB7D068E44E1F378F41ECBAE97D2D63DBC87BCCDDCCC5DA39E8589291C", 16) + p384r1 = newrcurve(twisted, params, z) +} + +func initP512t1() { + p512t1 = &elliptic.CurveParams{Name: "brainpoolP512t1"} + p512t1.P, _ = new(big.Int).SetString("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3", 16) + p512t1.N, _ = new(big.Int).SetString("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069", 16) + p512t1.B, _ = new(big.Int).SetString("7CBBBCF9441CFAB76E1890E46884EAE321F70C0BCB4981527897504BEC3E36A62BCDFA2304976540F6450085F2DAE145C22553B465763689180EA2571867423E", 16) + p512t1.Gx, _ = new(big.Int).SetString("640ECE5C12788717B9C1BA06CBC2A6FEBA85842458C56DDE9DB1758D39C0313D82BA51735CDB3EA499AA77A7D6943A64F7A3F25FE26F06B51BAA2696FA9035DA", 16) + p512t1.Gy, _ = new(big.Int).SetString("5B534BD595F5AF0FA2C892376C84ACE1BB4E3019B71634C01131159CAE03CEE9D9932184BEEF216BD71DF2DADF86A627306ECFF96DBB8BACE198B61E00F8B332", 16) + p512t1.BitSize = 512 +} + +func initP512r1() { + twisted := p512t1 + params := &elliptic.CurveParams{ + Name: "brainpoolP512r1", + P: twisted.P, + N: twisted.N, + BitSize: twisted.BitSize, + } + params.Gx, _ = new(big.Int).SetString("81AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F822", 16) + params.Gy, _ = new(big.Int).SetString("7DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892", 16) + z, _ := new(big.Int).SetString("12EE58E6764838B69782136F0F2D3BA06E27695716054092E60A80BEDB212B64E585D90BCE13761F85C3F1D2A64E3BE8FEA2220F01EBA5EEB0F35DBD29D922AB", 16) + p512r1 = newrcurve(twisted, params, z) +} + +// P256t1 returns a Curve which implements Brainpool P256t1 (see RFC 5639, section 3.4) +func P256t1() elliptic.Curve { + once.Do(initAll) + return p256t1 +} + +// P256r1 returns a Curve which implements Brainpool P256r1 (see RFC 5639, section 3.4) +func P256r1() elliptic.Curve { + once.Do(initAll) + return p256r1 +} + +// P384t1 returns a Curve which implements Brainpool P384t1 (see RFC 5639, section 3.6) +func P384t1() elliptic.Curve { + once.Do(initAll) + return p384t1 +} + +// P384r1 returns a Curve which implements Brainpool P384r1 (see RFC 5639, section 3.6) +func P384r1() elliptic.Curve { + once.Do(initAll) + return p384r1 +} + +// P512t1 returns a Curve which implements Brainpool P512t1 (see RFC 5639, section 3.7) +func P512t1() elliptic.Curve { + once.Do(initAll) + return p512t1 +} + +// P512r1 returns a Curve which implements Brainpool P512r1 (see RFC 5639, section 3.7) +func P512r1() elliptic.Curve { + once.Do(initAll) + return p512r1 +} diff --git a/vendor/github.com/ProtonMail/go-crypto/brainpool/rcurve.go b/vendor/github.com/ProtonMail/go-crypto/brainpool/rcurve.go new file mode 100644 index 0000000000000..2d5355085f2e7 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/brainpool/rcurve.go @@ -0,0 +1,83 @@ +package brainpool + +import ( + "crypto/elliptic" + "math/big" +) + +var _ elliptic.Curve = (*rcurve)(nil) + +type rcurve struct { + twisted elliptic.Curve + params *elliptic.CurveParams + z *big.Int + zinv *big.Int + z2 *big.Int + z3 *big.Int + zinv2 *big.Int + zinv3 *big.Int +} + +var ( + two = big.NewInt(2) + three = big.NewInt(3) +) + +func newrcurve(twisted elliptic.Curve, params *elliptic.CurveParams, z *big.Int) *rcurve { + zinv := new(big.Int).ModInverse(z, params.P) + return &rcurve{ + twisted: twisted, + params: params, + z: z, + zinv: zinv, + z2: new(big.Int).Exp(z, two, params.P), + z3: new(big.Int).Exp(z, three, params.P), + zinv2: new(big.Int).Exp(zinv, two, params.P), + zinv3: new(big.Int).Exp(zinv, three, params.P), + } +} + +func (curve *rcurve) toTwisted(x, y *big.Int) (*big.Int, *big.Int) { + var tx, ty big.Int + tx.Mul(x, curve.z2) + tx.Mod(&tx, curve.params.P) + ty.Mul(y, curve.z3) + ty.Mod(&ty, curve.params.P) + return &tx, &ty +} + +func (curve *rcurve) fromTwisted(tx, ty *big.Int) (*big.Int, *big.Int) { + var x, y big.Int + x.Mul(tx, curve.zinv2) + x.Mod(&x, curve.params.P) + y.Mul(ty, curve.zinv3) + y.Mod(&y, curve.params.P) + return &x, &y +} + +func (curve *rcurve) Params() *elliptic.CurveParams { + return curve.params +} + +func (curve *rcurve) IsOnCurve(x, y *big.Int) bool { + return curve.twisted.IsOnCurve(curve.toTwisted(x, y)) +} + +func (curve *rcurve) Add(x1, y1, x2, y2 *big.Int) (x, y *big.Int) { + tx1, ty1 := curve.toTwisted(x1, y1) + tx2, ty2 := curve.toTwisted(x2, y2) + return curve.fromTwisted(curve.twisted.Add(tx1, ty1, tx2, ty2)) +} + +func (curve *rcurve) Double(x1, y1 *big.Int) (x, y *big.Int) { + return curve.fromTwisted(curve.twisted.Double(curve.toTwisted(x1, y1))) +} + +func (curve *rcurve) ScalarMult(x1, y1 *big.Int, scalar []byte) (x, y *big.Int) { + tx1, ty1 := curve.toTwisted(x1, y1) + return curve.fromTwisted(curve.twisted.ScalarMult(tx1, ty1, scalar)) +} + +func (curve *rcurve) ScalarBaseMult(scalar []byte) (x, y *big.Int) { + return curve.fromTwisted(curve.twisted.ScalarBaseMult(scalar)) +} \ No newline at end of file diff --git a/vendor/github.com/ProtonMail/go-crypto/eax/eax.go b/vendor/github.com/ProtonMail/go-crypto/eax/eax.go new file mode 100644 index 0000000000000..6b6bc7aed0444 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/eax/eax.go @@ -0,0 +1,162 @@ +// Copyright (C) 2019 ProtonTech AG + +// Package eax provides an implementation of the EAX +// (encrypt-authenticate-translate) mode of operation, as described in +// Bellare, Rogaway, and Wagner "THE EAX MODE OF OPERATION: A TWO-PASS +// AUTHENTICATED-ENCRYPTION SCHEME OPTIMIZED FOR SIMPLICITY AND EFFICIENCY." +// In FSE'04, volume 3017 of LNCS, 2004 +package eax + +import ( + "crypto/cipher" + "crypto/subtle" + "errors" + "github.com/ProtonMail/go-crypto/internal/byteutil" +) + +const ( + defaultTagSize = 16 + defaultNonceSize = 16 +) + +type eax struct { + block cipher.Block // Only AES-{128, 192, 256} supported + tagSize int // At least 12 bytes recommended + nonceSize int +} + +func (e *eax) NonceSize() int { + return e.nonceSize +} + +func (e *eax) Overhead() int { + return e.tagSize +} + +// NewEAX returns an EAX instance with AES-{KEYLENGTH} and default nonce and +// tag lengths. Supports {128, 192, 256}- bit key length. +func NewEAX(block cipher.Block) (cipher.AEAD, error) { + return NewEAXWithNonceAndTagSize(block, defaultNonceSize, defaultTagSize) +} + +// NewEAXWithNonceAndTagSize returns an EAX instance with AES-{keyLength} and +// given nonce and tag lengths in bytes. Panics on zero nonceSize and +// exceedingly long tags. +// +// It is recommended to use at least 12 bytes as tag length (see, for instance, +// NIST SP 800-38D). +// +// Only to be used for compatibility with existing cryptosystems with +// non-standard parameters. For all other cases, prefer NewEAX. +func NewEAXWithNonceAndTagSize( + block cipher.Block, nonceSize, tagSize int) (cipher.AEAD, error) { + if nonceSize < 1 { + return nil, eaxError("Cannot initialize EAX with nonceSize = 0") + } + if tagSize > block.BlockSize() { + return nil, eaxError("Custom tag length exceeds blocksize") + } + return &eax{ + block: block, + tagSize: tagSize, + nonceSize: nonceSize, + }, nil +} + +func (e *eax) Seal(dst, nonce, plaintext, adata []byte) []byte { + if len(nonce) > e.nonceSize { + panic("crypto/eax: Nonce too long for this instance") + } + ret, out := byteutil.SliceForAppend(dst, len(plaintext) + e.tagSize) + omacNonce := e.omacT(0, nonce) + omacAdata := e.omacT(1, adata) + + // Encrypt message using CTR mode and omacNonce as IV + ctr := cipher.NewCTR(e.block, omacNonce) + ciphertextData := out[:len(plaintext)] + ctr.XORKeyStream(ciphertextData, plaintext) + + omacCiphertext := e.omacT(2, ciphertextData) + + tag := out[len(plaintext):] + for i := 0; i < e.tagSize; i++ { + tag[i] = omacCiphertext[i] ^ omacNonce[i] ^ omacAdata[i] + } + return ret +} + +func (e* eax) Open(dst, nonce, ciphertext, adata []byte) ([]byte, error) { + if len(nonce) > e.nonceSize { + panic("crypto/eax: Nonce too long for this instance") + } + if len(ciphertext) < e.tagSize { + return nil, eaxError("Ciphertext shorter than tag length") + } + sep := len(ciphertext) - e.tagSize + + // Compute tag + omacNonce := e.omacT(0, nonce) + omacAdata := e.omacT(1, adata) + omacCiphertext := e.omacT(2, ciphertext[:sep]) + + tag := make([]byte, e.tagSize) + for i := 0; i < e.tagSize; i++ { + tag[i] = omacCiphertext[i] ^ omacNonce[i] ^ omacAdata[i] + } + + // Compare tags + if subtle.ConstantTimeCompare(ciphertext[sep:], tag) != 1 { + return nil, eaxError("Tag authentication failed") + } + + // Decrypt ciphertext + ret, out := byteutil.SliceForAppend(dst, len(ciphertext)) + ctr := cipher.NewCTR(e.block, omacNonce) + ctr.XORKeyStream(out, ciphertext[:sep]) + + return ret[:sep], nil +} + +// Tweakable OMAC - Calls OMAC_K([t]_n || plaintext) +func (e *eax) omacT(t byte, plaintext []byte) []byte { + blockSize := e.block.BlockSize() + byteT := make([]byte, blockSize) + byteT[blockSize-1] = t + concat := append(byteT, plaintext...) + return e.omac(concat) +} + +func (e *eax) omac(plaintext []byte) []byte { + blockSize := e.block.BlockSize() + // L ← E_K(0^n); B ← 2L; P ← 4L + L := make([]byte, blockSize) + e.block.Encrypt(L, L) + B := byteutil.GfnDouble(L) + P := byteutil.GfnDouble(B) + + // CBC with IV = 0 + cbc := cipher.NewCBCEncrypter(e.block, make([]byte, blockSize)) + padded := e.pad(plaintext, B, P) + cbcCiphertext := make([]byte, len(padded)) + cbc.CryptBlocks(cbcCiphertext, padded) + + return cbcCiphertext[len(cbcCiphertext)-blockSize:] +} + +func (e *eax) pad(plaintext, B, P []byte) []byte { + // if |M| in {n, 2n, 3n, ...} + blockSize := e.block.BlockSize() + if len(plaintext) != 0 && len(plaintext)%blockSize == 0 { + return byteutil.RightXor(plaintext, B) + } + + // else return (M || 1 || 0^(n−1−(|M| % n))) xor→ P + ending := make([]byte, blockSize-len(plaintext)%blockSize) + ending[0] = 0x80 + padded := append(plaintext, ending...) + return byteutil.RightXor(padded, P) +} + +func eaxError(err string) error { + return errors.New("crypto/eax: " + err) +} diff --git a/vendor/github.com/ProtonMail/go-crypto/eax/eax_test_vectors.go b/vendor/github.com/ProtonMail/go-crypto/eax/eax_test_vectors.go new file mode 100644 index 0000000000000..ddb53d07905cf --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/eax/eax_test_vectors.go @@ -0,0 +1,58 @@ +package eax + +// Test vectors from +// https://web.cs.ucdavis.edu/~rogaway/papers/eax.pdf +var testVectors = []struct { + msg, key, nonce, header, ciphertext string +}{ + {"", + "233952DEE4D5ED5F9B9C6D6FF80FF478", + "62EC67F9C3A4A407FCB2A8C49031A8B3", + "6BFB914FD07EAE6B", + "E037830E8389F27B025A2D6527E79D01"}, + {"F7FB", + "91945D3F4DCBEE0BF45EF52255F095A4", + "BECAF043B0A23D843194BA972C66DEBD", + "FA3BFD4806EB53FA", + "19DD5C4C9331049D0BDAB0277408F67967E5"}, + {"1A47CB4933", + "01F74AD64077F2E704C0F60ADA3DD523", + "70C3DB4F0D26368400A10ED05D2BFF5E", + "234A3463C1264AC6", + "D851D5BAE03A59F238A23E39199DC9266626C40F80"}, + {"481C9E39B1", + "D07CF6CBB7F313BDDE66B727AFD3C5E8", + "8408DFFF3C1A2B1292DC199E46B7D617", + "33CCE2EABFF5A79D", + "632A9D131AD4C168A4225D8E1FF755939974A7BEDE"}, + {"40D0C07DA5E4", + "35B6D0580005BBC12B0587124557D2C2", + "FDB6B06676EEDC5C61D74276E1F8E816", + "AEB96EAEBE2970E9", + "071DFE16C675CB0677E536F73AFE6A14B74EE49844DD"}, + {"4DE3B35C3FC039245BD1FB7D", + "BD8E6E11475E60B268784C38C62FEB22", + "6EAC5C93072D8E8513F750935E46DA1B", + "D4482D1CA78DCE0F", + "835BB4F15D743E350E728414ABB8644FD6CCB86947C5E10590210A4F"}, + {"8B0A79306C9CE7ED99DAE4F87F8DD61636", + "7C77D6E813BED5AC98BAA417477A2E7D", + "1A8C98DCD73D38393B2BF1569DEEFC19", + "65D2017990D62528", + "02083E3979DA014812F59F11D52630DA30137327D10649B0AA6E1C181DB617D7F2"}, + {"1BDA122BCE8A8DBAF1877D962B8592DD2D56", + "5FFF20CAFAB119CA2FC73549E20F5B0D", + "DDE59B97D722156D4D9AFF2BC7559826", + "54B9F04E6A09189A", + "2EC47B2C4954A489AFC7BA4897EDCDAE8CC33B60450599BD02C96382902AEF7F832A"}, + {"6CF36720872B8513F6EAB1A8A44438D5EF11", + "A4A4782BCFFD3EC5E7EF6D8C34A56123", + "B781FCF2F75FA5A8DE97A9CA48E522EC", + "899A175897561D7E", + "0DE18FD0FDD91E7AF19F1D8EE8733938B1E8E7F6D2231618102FDB7FE55FF1991700"}, + {"CA40D7446E545FFAED3BD12A740A659FFBBB3CEAB7", + "8395FCF1E95BEBD697BD010BC766AAC3", + "22E7ADD93CFC6393C57EC0B3C17D6B44", + "126735FCC320D25A", + "CB8920F87A6C75CFF39627B56E3ED197C552D295A7CFC46AFC253B4652B1AF3795B124AB6E"}, +} diff --git a/vendor/github.com/ProtonMail/go-crypto/eax/random_vectors.go b/vendor/github.com/ProtonMail/go-crypto/eax/random_vectors.go new file mode 100644 index 0000000000000..4eb19f28d9c44 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/eax/random_vectors.go @@ -0,0 +1,131 @@ +// These vectors include key length in {128, 192, 256}, tag size 128, and +// random nonce, header, and plaintext lengths. + +// This file was automatically generated. + +package eax + +var randomVectors = []struct { + key, nonce, header, plaintext, ciphertext string +}{ + {"DFDE093F36B0356E5A81F609786982E3", + "1D8AC604419001816905BA72B14CED7E", + "152A1517A998D7A24163FCDD146DE81AC347C8B97088F502093C1ABB8F6E33D9A219C34D7603A18B1F5ABE02E56661B7D7F67E81EC08C1302EF38D80A859486D450E94A4F26AD9E68EEBBC0C857A0FC5CF9E641D63D565A7E361BC8908F5A8DC8FD6", + "1C8EAAB71077FE18B39730A3156ADE29C5EE824C7EE86ED2A253B775603FB237116E654F6FEC588DD27F523A0E01246FE73FE348491F2A8E9ABC6CA58D663F71CDBCF4AD798BE46C42AE6EE8B599DB44A1A48D7BBBBA0F7D2750181E1C5E66967F7D57CBD30AFBDA5727", + "79E7E150934BBEBF7013F61C60462A14D8B15AF7A248AFB8A344EF021C1500E16666891D6E973D8BB56B71A371F12CA34660C4410C016982B20F547E3762A58B7BF4F20236CADCF559E2BE7D783B13723B2741FC7CDC8997D839E39A3DDD2BADB96743DD7049F1BDB0516A262869915B3F70498AFB7B191BF960"}, + {"F10619EF02E5D94D7550EB84ED364A21", + "8DC0D4F2F745BBAE835CC5574B942D20", + "FE561358F2E8DF7E1024FF1AE9A8D36EBD01352214505CB99D644777A8A1F6027FA2BDBFC529A9B91136D5F2416CFC5F0F4EC3A1AFD32BDDA23CA504C5A5CB451785FABF4DFE4CD50D817491991A60615B30286361C100A95D1712F2A45F8E374461F4CA2B", + "D7B5A971FC219631D30EFC3664AE3127D9CF3097DAD9C24AC7905D15E8D9B25B026B31D68CAE00975CDB81EB1FD96FD5E1A12E2BB83FA25F1B1D91363457657FC03875C27F2946C5", + "2F336ED42D3CC38FC61660C4CD60BA4BD438B05F5965D8B7B399D2E7167F5D34F792D318F94DB15D67463AC449E13D568CC09BFCE32A35EE3EE96A041927680AE329811811E27F2D1E8E657707AF99BA96D13A478D695D59"}, + {"429F514EFC64D98A698A9247274CFF45", + "976AA5EB072F912D126ACEBC954FEC38", + "A71D89DC5B6CEDBB7451A27C3C2CAE09126DB4C421", + "5632FE62AB1DC549D54D3BC3FC868ACCEDEFD9ECF5E9F8", + "848AE4306CA8C7F416F8707625B7F55881C0AB430353A5C967CDA2DA787F581A70E34DBEBB2385"}, + {"398138F309085F47F8457CDF53895A63", + "F8A8A7F2D28E5FFF7BBC2F24353F7A36", + "5D633C21BA7764B8855CAB586F3746E236AD486039C83C6B56EFA9C651D38A41D6B20DAEE3418BFEA44B8BD6", + "A3BBAA91920AF5E10659818B1B3B300AC79BFC129C8329E75251F73A66D3AE0128EB91D5031E0A65C329DB7D1E9C0493E268", + "D078097267606E5FB07CFB7E2B4B718172A82C6A4CEE65D549A4DFB9838003BD2FBF64A7A66988AC1A632FD88F9E9FBB57C5A78AD2E086EACBA3DB68511D81C2970A"}, + {"7A4151EBD3901B42CBA45DAFB2E931BA", + "0FC88ACEE74DD538040321C330974EB8", + "250464FB04733BAB934C59E6AD2D6AE8D662CBCFEFBE61E5A308D4211E58C4C25935B72C69107722E946BFCBF416796600542D76AEB73F2B25BF53BAF97BDEB36ED3A7A51C31E7F170EB897457E7C17571D1BA0A908954E9", + "88C41F3EBEC23FAB8A362D969CAC810FAD4F7CA6A7F7D0D44F060F92E37E1183768DD4A8C733F71C96058D362A39876D183B86C103DE", + "74A25B2182C51096D48A870D80F18E1CE15867778E34FCBA6BD7BFB3739FDCD42AD0F2D9F4EBA29085285C6048C15BCE5E5166F1F962D3337AA88E6062F05523029D0A7F0BF9"}, + {"BFB147E1CD5459424F8C0271FC0E0DC5", + "EABCC126442BF373969EA3015988CC45", + "4C0880E1D71AA2C7", + "BE1B5EC78FBF73E7A6682B21BA7E0E5D2D1C7ABE", + "5660D7C1380E2F306895B1402CB2D6C37876504276B414D120F4CF92FDDDBB293A238EA0"}, + {"595DD6F52D18BC2CA8EB4EDAA18D9FA3", + "0F84B5D36CF4BC3B863313AF3B4D2E97", + "30AE6CC5F99580F12A779D98BD379A60948020C0B6FBD5746B30BA3A15C6CD33DAF376C70A9F15B6C0EB410A93161F7958AE23", + "8EF3687A1642B070970B0B91462229D1D76ABC154D18211F7152AA9FF368", + "317C1DDB11417E5A9CC4DDE7FDFF6659A5AC4B31DE025212580A05CDAC6024D3E4AE7C2966E52B9129E9ECDBED86"}, + {"44E6F2DC8FDC778AD007137D11410F50", + "270A237AD977F7187AA6C158A0BAB24F", + "509B0F0EB12E2AA5C5BA2DE553C07FAF4CE0C9E926531AA709A3D6224FCB783ACCF1559E10B1123EBB7D52E8AB54E6B5352A9ED0D04124BF0E9D9BACFD7E32B817B2E625F5EE94A64EDE9E470DE7FE6886C19B294F9F828209FE257A78", + "8B3D7815DF25618A5D0C55A601711881483878F113A12EC36CF64900549A3199555528559DC118F789788A55FAFD944E6E99A9CA3F72F238CD3F4D88223F7A745992B3FAED1848", + "1CC00D79F7AD82FDA71B58D286E5F34D0CC4CEF30704E771CC1E50746BDF83E182B078DB27149A42BAE619DF0F85B0B1090AD55D3B4471B0D6F6ECCD09C8F876B30081F0E7537A9624F8AAF29DA85E324122EFB4D68A56"}, + {"BB7BC352A03044B4428D8DBB4B0701FDEC4649FD17B81452", + "8B4BBE26CCD9859DCD84884159D6B0A4", + "2212BEB0E78E0F044A86944CF33C8D5C80D9DBE1034BF3BCF73611835C7D3A52F5BD2D81B68FD681B68540A496EE5DA16FD8AC8824E60E1EC2042BE28FB0BFAD4E4B03596446BDD8C37D936D9B3D5295BE19F19CF5ACE1D33A46C952CE4DE5C12F92C1DD051E04AEED", + "9037234CC44FFF828FABED3A7084AF40FA7ABFF8E0C0EFB57A1CC361E18FC4FAC1AB54F3ABFE9FF77263ACE16C3A", + "A9391B805CCD956081E0B63D282BEA46E7025126F1C1631239C33E92AA6F92CD56E5A4C56F00FF9658E93D48AF4EF0EF81628E34AD4DB0CDAEDCD2A17EE7"}, + {"99C0AD703196D2F60A74E6B378B838B31F82EA861F06FC4E", + "92745C018AA708ECFEB1667E9F3F1B01", + "828C69F376C0C0EC651C67749C69577D589EE39E51404D80EBF70C8660A8F5FD375473F4A7C611D59CB546A605D67446CE2AA844135FCD78BB5FBC90222A00D42920BB1D7EEDFB0C4672554F583EF23184F89063CDECBE482367B5F9AF3ACBC3AF61392BD94CBCD9B64677", + "A879214658FD0A5B0E09836639BF82E05EC7A5EF71D4701934BDA228435C68AC3D5CEB54997878B06A655EEACEFB1345C15867E7FE6C6423660C8B88DF128EBD6BCD85118DBAE16E9252FFB204324E5C8F38CA97759BDBF3CB0083", + "51FE87996F194A2585E438B023B345439EA60D1AEBED4650CDAF48A4D4EEC4FC77DC71CC4B09D3BEEF8B7B7AF716CE2B4EFFB3AC9E6323C18AC35E0AA6E2BBBC8889490EB6226C896B0D105EAB42BFE7053CCF00ED66BA94C1BA09A792AA873F0C3B26C5C5F9A936E57B25"}, + {"7086816D00D648FB8304AA8C9E552E1B69A9955FB59B25D1", + "0F45CF7F0BF31CCEB85D9DA10F4D749F", + "93F27C60A417D9F0669E86ACC784FC8917B502DAF30A6338F11B30B94D74FEFE2F8BE1BBE2EAD10FAB7EED3C6F72B7C3ECEE1937C32ED4970A6404E139209C05", + "877F046601F3CBE4FB1491943FA29487E738F94B99AF206262A1D6FF856C9AA0B8D4D08A54370C98F8E88FA3DCC2B14C1F76D71B2A4C7963AEE8AF960464C5BEC8357AD00DC8", + "FE96906B895CE6A8E72BC72344E2C8BB3C63113D70EAFA26C299BAFE77A8A6568172EB447FB3E86648A0AF3512DEB1AAC0819F3EC553903BF28A9FB0F43411237A774BF9EE03E445D280FBB9CD12B9BAAB6EF5E52691"}, + {"062F65A896D5BF1401BADFF70E91B458E1F9BD4888CB2E4D", + "5B11EA1D6008EBB41CF892FCA5B943D1", + "BAF4FF5C8242", + "A8870E091238355984EB2F7D61A865B9170F440BFF999A5993DD41A10F4440D21FF948DDA2BF663B2E03AC3324492DC5E40262ECC6A65C07672353BE23E7FB3A9D79FF6AA38D97960905A38DECC312CB6A59E5467ECF06C311CD43ADC0B543EDF34FE8BE611F176460D5627CA51F8F8D9FED71F55C", + "B10E127A632172CF8AA7539B140D2C9C2590E6F28C3CB892FC498FCE56A34F732FBFF32E79C7B9747D9094E8635A0C084D6F0247F9768FB5FF83493799A9BEC6C39572120C40E9292C8C947AE8573462A9108C36D9D7112E6995AE5867E6C8BB387D1C5D4BEF524F391B9FD9F0A3B4BFA079E915BCD920185CFD38D114C558928BD7D47877"}, + {"38A8E45D6D705A11AF58AED5A1344896998EACF359F2E26A", + "FD82B5B31804FF47D44199B533D0CF84", + "DE454D4E62FE879F2050EE3E25853623D3E9AC52EEC1A1779A48CFAF5ECA0BFDE44749391866D1", + "B804", + "164BB965C05EBE0931A1A63293EDF9C38C27"}, + {"34C33C97C6D7A0850DA94D78A58DC61EC717CD7574833068", + "343BE00DA9483F05C14F2E9EB8EA6AE8", + "78312A43EFDE3CAE34A65796FF059A3FE15304EEA5CF1D9306949FE5BF3349D4977D4EBE76C040FE894C5949E4E4D6681153DA87FB9AC5062063CA2EA183566343362370944CE0362D25FC195E124FD60E8682E665D13F2229DDA3E4B2CB1DCA", + "CC11BB284B1153578E4A5ED9D937B869DAF00F5B1960C23455CA9CC43F486A3BE0B66254F1041F04FDF459C8640465B6E1D2CF899A381451E8E7FCB50CF87823BE77E24B132BBEEDC72E53369B275E1D8F49ECE59F4F215230AC4FE133FC80E4F634EE80BA4682B62C86", + "E7F703DC31A95E3A4919FF957836CB76C063D81702AEA4703E1C2BF30831E58C4609D626EC6810E12EAA5B930F049FF9EFC22C3E3F1EBD4A1FB285CB02A1AC5AD46B425199FC0A85670A5C4E3DAA9636C8F64C199F42F18AAC8EA7457FD377F322DD7752D7D01B946C8F0A97E6113F0D50106F319AFD291AAACE"}, + {"C6ECF7F053573E403E61B83052A343D93CBCC179D1E835BE", + "E280E13D7367042E3AA09A80111B6184", + "21486C9D7A9647", + "5F2639AFA6F17931853791CD8C92382BBB677FD72D0AB1A080D0E49BFAA21810E963E4FACD422E92F65CBFAD5884A60CD94740DF31AF02F95AA57DA0C4401B0ED906", + "5C51DB20755302070C45F52E50128A67C8B2E4ED0EACB7E29998CCE2E8C289DD5655913EC1A51CC3AABE5CDC2402B2BE7D6D4BF6945F266FBD70BA9F37109067157AE7530678B45F64475D4EBFCB5FFF46A5"}, + {"5EC6CF7401BC57B18EF154E8C38ACCA8959E57D2F3975FF5", + "656B41CB3F9CF8C08BAD7EBFC80BD225", + "6B817C2906E2AF425861A7EF59BA5801F143EE2A139EE72697CDE168B4", + "2C0E1DDC9B1E5389BA63845B18B1F8A1DB062037151BCC56EF7C21C0BB4DAE366636BBA975685D7CC5A94AFBE89C769016388C56FB7B57CE750A12B718A8BDCF70E80E8659A8330EFC8F86640F21735E8C80E23FE43ABF23507CE3F964AE4EC99D", + "ED780CF911E6D1AA8C979B889B0B9DC1ABE261832980BDBFB576901D9EF5AB8048998E31A15BE54B3E5845A4D136AD24D0BDA1C3006168DF2F8AC06729CB0818867398150020131D8F04EDF1923758C9EABB5F735DE5EA1758D4BC0ACFCA98AFD202E9839B8720253693B874C65586C6F0"}, + {"C92F678EB2208662F5BCF3403EC05F5961E957908A3E79421E1D25FC19054153", + "DA0F3A40983D92F2D4C01FED33C7A192", + "2B6E9D26DB406A0FAB47608657AA10EFC2B4AA5F459B29FF85AC9A40BFFE7AEB04F77E9A11FAAA116D7F6D4DA417671A9AB02C588E0EF59CB1BFB4B1CC931B63A3B3A159FCEC97A04D1E6F0C7E6A9CEF6B0ABB04758A69F1FE754DF4C2610E8C46B6CF413BDB31351D55BEDCB7B4A13A1C98E10984475E0F2F957853", + "F37326A80E08", + "83519E53E321D334F7C10B568183775C0E9AAE55F806"}, + {"6847E0491BE57E72995D186D50094B0B3593957A5146798FCE68B287B2FB37B5", + "3EE1182AEBB19A02B128F28E1D5F7F99", + "D9F35ABB16D776CE", + "DB7566ED8EA95BDF837F23DB277BAFBC5E70D1105ADFD0D9EF15475051B1EF94709C67DCA9F8D5", + "2CDCED0C9EBD6E2A508822A685F7DCD1CDD99E7A5FCA786C234E7F7F1D27EC49751AD5DCFA30C5EDA87C43CAE3B919B6BBCFE34C8EDA59"}, + {"82B019673642C08388D3E42075A4D5D587558C229E4AB8F660E37650C4C41A0A", + "336F5D681E0410FAE7B607246092C6DC", + "D430CBD8FE435B64214E9E9CDC5DE99D31CFCFB8C10AA0587A49DF276611", + "998404153AD77003E1737EDE93ED79859EE6DCCA93CB40C4363AA817ABF2DBBD46E42A14A7183B6CC01E12A577888141363D0AE011EB6E8D28C0B235", + "9BEF69EEB60BD3D6065707B7557F25292A8872857CFBD24F2F3C088E4450995333088DA50FD9121221C504DF1D0CD5EFE6A12666C5D5BB12282CF4C19906E9CFAB97E9BDF7F49DC17CFC384B"}, + {"747B2E269B1859F0622C15C8BAD6A725028B1F94B8DB7326948D1E6ED663A8BC", + "AB91F7245DDCE3F1C747872D47BE0A8A", + "3B03F786EF1DDD76E1D42646DA4CD2A5165DC5383CE86D1A0B5F13F910DC278A4E451EE0192CBA178E13B3BA27FDC7840DF73D2E104B", + "6B803F4701114F3E5FE21718845F8416F70F626303F545BE197189E0A2BA396F37CE06D389EB2658BC7D56D67868708F6D0D32", + "1570DDB0BCE75AA25D1957A287A2C36B1A5F2270186DA81BA6112B7F43B0F3D1D0ED072591DCF1F1C99BBB25621FC39B896FF9BD9413A2845363A9DCD310C32CF98E57"}, + {"02E59853FB29AEDA0FE1C5F19180AD99A12FF2F144670BB2B8BADF09AD812E0A", + "C691294EF67CD04D1B9242AF83DD1421", + "879334DAE3", + "1E17F46A98FEF5CBB40759D95354", + "FED8C3FF27DDF6313AED444A2985B36CBA268AAD6AAC563C0BA28F6DB5DB"}, + {"F6C1FB9B4188F2288FF03BD716023198C3582CF2A037FC2F29760916C2B7FCDB", + "4228DA0678CA3534588859E77DFF014C", + "D8153CAF35539A61DD8D05B3C9B44F01E564FB9348BCD09A1C23B84195171308861058F0A3CD2A55B912A3AAEE06FF4D356C77275828F2157C2FC7C115DA39E443210CCC56BEDB0CC99BBFB227ABD5CC454F4E7F547C7378A659EEB6A7E809101A84F866503CB18D4484E1FA09B3EC7FC75EB2E35270800AA7", + "23B660A779AD285704B12EC1C580387A47BEC7B00D452C6570", + "5AA642BBABA8E49849002A2FAF31DB8FC7773EFDD656E469CEC19B3206D4174C9A263D0A05484261F6"}, + {"8FF6086F1FADB9A3FBE245EAC52640C43B39D43F89526BB5A6EBA47710931446", + "943188480C99437495958B0AE4831AA9", + "AD5CD0BDA426F6EBA23C8EB23DC73FF9FEC173355EDBD6C9344C4C4383F211888F7CE6B29899A6801DF6B38651A7C77150941A", + "80CD5EA8D7F81DDF5070B934937912E8F541A5301877528EB41AB60C020968D459960ED8FB73083329841A", + "ABAE8EB7F36FCA2362551E72DAC890BA1BB6794797E0FC3B67426EC9372726ED4725D379EA0AC9147E48DCD0005C502863C2C5358A38817C8264B5"}, + {"A083B54E6B1FE01B65D42FCD248F97BB477A41462BBFE6FD591006C022C8FD84", + "B0490F5BD68A52459556B3749ACDF40E", + "8892E047DA5CFBBDF7F3CFCBD1BD21C6D4C80774B1826999234394BD3E513CC7C222BB40E1E3140A152F19B3802F0D036C24A590512AD0E8", + "D7B15752789DC94ED0F36778A5C7BBB207BEC32BAC66E702B39966F06E381E090C6757653C3D26A81EC6AD6C364D66867A334C91BB0B8A8A4B6EACDF0783D09010AEBA2DD2062308FE99CC1F", + "C071280A732ADC93DF272BF1E613B2BB7D46FC6665EF2DC1671F3E211D6BDE1D6ADDD28DF3AA2E47053FC8BB8AE9271EC8BC8B2CFFA320D225B451685B6D23ACEFDD241FE284F8ADC8DB07F456985B14330BBB66E0FB212213E05B3E"}, +} diff --git a/vendor/github.com/ProtonMail/go-crypto/internal/byteutil/byteutil.go b/vendor/github.com/ProtonMail/go-crypto/internal/byteutil/byteutil.go new file mode 100644 index 0000000000000..a6bdf51232e9a --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/internal/byteutil/byteutil.go @@ -0,0 +1,92 @@ +// Copyright (C) 2019 ProtonTech AG +// This file contains necessary tools for the aex and ocb packages. +// +// These functions SHOULD NOT be used elsewhere, since they are optimized for +// specific input nature in the EAX and OCB modes of operation. + +package byteutil + +// GfnDouble computes 2 * input in the field of 2^n elements. +// The irreducible polynomial in the finite field for n=128 is +// x^128 + x^7 + x^2 + x + 1 (equals 0x87) +// Constant-time execution in order to avoid side-channel attacks +func GfnDouble(input []byte) []byte { + if len(input) != 16 { + panic("Doubling in GFn only implemented for n = 128") + } + // If the first bit is zero, return 2L = L << 1 + // Else return (L << 1) xor 0^120 10000111 + shifted := ShiftBytesLeft(input) + shifted[15] ^= ((input[0] >> 7) * 0x87) + return shifted +} + +// ShiftBytesLeft outputs the byte array corresponding to x << 1 in binary. +func ShiftBytesLeft(x []byte) []byte { + l := len(x) + dst := make([]byte, l) + for i := 0; i < l-1; i++ { + dst[i] = (x[i] << 1) | (x[i+1] >> 7) + } + dst[l-1] = x[l-1] << 1 + return dst +} + +// ShiftNBytesLeft puts in dst the byte array corresponding to x << n in binary. +func ShiftNBytesLeft(dst, x []byte, n int) { + // Erase first n / 8 bytes + copy(dst, x[n/8:]) + + // Shift the remaining n % 8 bits + bits := uint(n % 8) + l := len(dst) + for i := 0; i < l-1; i++ { + dst[i] = (dst[i] << bits) | (dst[i+1] >> uint(8 - bits)) + } + dst[l-1] = dst[l-1] << bits + + // Append trailing zeroes + dst = append(dst, make([]byte, n/8)...) +} + +// XorBytesMut assumes equal input length, replaces X with X XOR Y +func XorBytesMut(X, Y []byte) { + for i := 0; i < len(X); i++ { + X[i] ^= Y[i] + } +} + + +// XorBytes assumes equal input length, puts X XOR Y into Z +func XorBytes(Z, X, Y []byte) { + for i := 0; i < len(X); i++ { + Z[i] = X[i] ^ Y[i] + } +} + +// RightXor XORs smaller input (assumed Y) at the right of the larger input (assumed X) +func RightXor(X, Y []byte) []byte { + offset := len(X) - len(Y) + xored := make([]byte, len(X)); + copy(xored, X) + for i := 0; i < len(Y); i++ { + xored[offset + i] ^= Y[i] + } + return xored +} + +// SliceForAppend takes a slice and a requested number of bytes. It returns a +// slice with the contents of the given slice followed by that many bytes and a +// second slice that aliases into it and contains only the extra bytes. If the +// original slice has sufficient capacity then no allocation is performed. +func SliceForAppend(in []byte, n int) (head, tail []byte) { + if total := len(in) + n; cap(in) >= total { + head = in[:total] + } else { + head = make([]byte, total) + copy(head, in) + } + tail = head[len(in):] + return +} + diff --git a/vendor/github.com/ProtonMail/go-crypto/ocb/ocb.go b/vendor/github.com/ProtonMail/go-crypto/ocb/ocb.go new file mode 100644 index 0000000000000..7f78cfa759b40 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/ocb/ocb.go @@ -0,0 +1,317 @@ +// Copyright (C) 2019 ProtonTech AG + +// Package ocb provides an implementation of the OCB (offset codebook) mode of +// operation, as described in RFC-7253 of the IRTF and in Rogaway, Bellare, +// Black and Krovetz - OCB: A BLOCK-CIPHER MODE OF OPERATION FOR EFFICIENT +// AUTHENTICATED ENCRYPTION (2003). +// Security considerations (from RFC-7253): A private key MUST NOT be used to +// encrypt more than 2^48 blocks. Tag length should be at least 12 bytes (a +// brute-force forging adversary succeeds after 2^{tag length} attempts). A +// single key SHOULD NOT be used to decrypt ciphertext with different tag +// lengths. Nonces need not be secret, but MUST NOT be reused. +// This package only supports underlying block ciphers with 128-bit blocks, +// such as AES-{128, 192, 256}, but may be extended to other sizes. +package ocb + +import ( + "bytes" + "crypto/cipher" + "crypto/subtle" + "errors" + "github.com/ProtonMail/go-crypto/internal/byteutil" + "math/bits" +) + +type ocb struct { + block cipher.Block + tagSize int + nonceSize int + mask mask + // Optimized en/decrypt: For each nonce N used to en/decrypt, the 'Ktop' + // internal variable can be reused for en/decrypting with nonces sharing + // all but the last 6 bits with N. The prefix of the first nonce used to + // compute the new Ktop, and the Ktop value itself, are stored in + // reusableKtop. If using incremental nonces, this saves one block cipher + // call every 63 out of 64 OCB encryptions, and stores one nonce and one + // output of the block cipher in memory only. + reusableKtop reusableKtop +} + +type mask struct { + // L_*, L_$, (L_i)_{i ∈ N} + lAst []byte + lDol []byte + L [][]byte +} + +type reusableKtop struct { + noncePrefix []byte + Ktop []byte +} + +const ( + defaultTagSize = 16 + defaultNonceSize = 15 +) + +const ( + enc = iota + dec +) + +func (o *ocb) NonceSize() int { + return o.nonceSize +} + +func (o *ocb) Overhead() int { + return o.tagSize +} + +// NewOCB returns an OCB instance with the given block cipher and default +// tag and nonce sizes. +func NewOCB(block cipher.Block) (cipher.AEAD, error) { + return NewOCBWithNonceAndTagSize(block, defaultNonceSize, defaultTagSize) +} + +// NewOCBWithNonceAndTagSize returns an OCB instance with the given block +// cipher, nonce length, and tag length. Panics on zero nonceSize and +// exceedingly long tag size. +// +// It is recommended to use at least 12 bytes as tag length. +func NewOCBWithNonceAndTagSize( + block cipher.Block, nonceSize, tagSize int) (cipher.AEAD, error) { + if block.BlockSize() != 16 { + return nil, ocbError("Block cipher must have 128-bit blocks") + } + if nonceSize < 1 { + return nil, ocbError("Incorrect nonce length") + } + if nonceSize >= block.BlockSize() { + return nil, ocbError("Nonce length exceeds blocksize - 1") + } + if tagSize > block.BlockSize() { + return nil, ocbError("Custom tag length exceeds blocksize") + } + return &ocb{ + block: block, + tagSize: tagSize, + nonceSize: nonceSize, + mask: initializeMaskTable(block), + reusableKtop: reusableKtop{ + noncePrefix: nil, + Ktop: nil, + }, + }, nil +} + +func (o *ocb) Seal(dst, nonce, plaintext, adata []byte) []byte { + if len(nonce) > o.nonceSize { + panic("crypto/ocb: Incorrect nonce length given to OCB") + } + ret, out := byteutil.SliceForAppend(dst, len(plaintext)+o.tagSize) + o.crypt(enc, out, nonce, adata, plaintext) + return ret +} + +func (o *ocb) Open(dst, nonce, ciphertext, adata []byte) ([]byte, error) { + if len(nonce) > o.nonceSize { + panic("Nonce too long for this instance") + } + if len(ciphertext) < o.tagSize { + return nil, ocbError("Ciphertext shorter than tag length") + } + sep := len(ciphertext) - o.tagSize + ret, out := byteutil.SliceForAppend(dst, len(ciphertext)) + ciphertextData := ciphertext[:sep] + tag := ciphertext[sep:] + o.crypt(dec, out, nonce, adata, ciphertextData) + if subtle.ConstantTimeCompare(ret[sep:], tag) == 1 { + ret = ret[:sep] + return ret, nil + } + for i := range out { + out[i] = 0 + } + return nil, ocbError("Tag authentication failed") +} + +// On instruction enc (resp. dec), crypt is the encrypt (resp. decrypt) +// function. It returns the resulting plain/ciphertext with the tag appended. +func (o *ocb) crypt(instruction int, Y, nonce, adata, X []byte) []byte { + // + // Consider X as a sequence of 128-bit blocks + // + // Note: For encryption (resp. decryption), X is the plaintext (resp., the + // ciphertext without the tag). + blockSize := o.block.BlockSize() + + // + // Nonce-dependent and per-encryption variables + // + // Zero out the last 6 bits of the nonce into truncatedNonce to see if Ktop + // is already computed. + truncatedNonce := make([]byte, len(nonce)) + copy(truncatedNonce, nonce) + truncatedNonce[len(truncatedNonce)-1] &= 192 + Ktop := make([]byte, blockSize) + if bytes.Equal(truncatedNonce, o.reusableKtop.noncePrefix) { + Ktop = o.reusableKtop.Ktop + } else { + // Nonce = num2str(TAGLEN mod 128, 7) || zeros(120 - bitlen(N)) || 1 || N + paddedNonce := append(make([]byte, blockSize-1-len(nonce)), 1) + paddedNonce = append(paddedNonce, truncatedNonce...) + paddedNonce[0] |= byte(((8 * o.tagSize) % (8 * blockSize)) << 1) + // Last 6 bits of paddedNonce are already zero. Encrypt into Ktop + paddedNonce[blockSize-1] &= 192 + Ktop = paddedNonce + o.block.Encrypt(Ktop, Ktop) + o.reusableKtop.noncePrefix = truncatedNonce + o.reusableKtop.Ktop = Ktop + } + + // Stretch = Ktop || ((lower half of Ktop) XOR (lower half of Ktop << 8)) + xorHalves := make([]byte, blockSize/2) + byteutil.XorBytes(xorHalves, Ktop[:blockSize/2], Ktop[1:1+blockSize/2]) + stretch := append(Ktop, xorHalves...) + bottom := int(nonce[len(nonce)-1] & 63) + offset := make([]byte, len(stretch)) + byteutil.ShiftNBytesLeft(offset, stretch, bottom) + offset = offset[:blockSize] + + // + // Process any whole blocks + // + // Note: For encryption Y is ciphertext || tag, for decryption Y is + // plaintext || tag. + checksum := make([]byte, blockSize) + m := len(X) / blockSize + for i := 0; i < m; i++ { + index := bits.TrailingZeros(uint(i + 1)) + if len(o.mask.L)-1 < index { + o.mask.extendTable(index) + } + byteutil.XorBytesMut(offset, o.mask.L[bits.TrailingZeros(uint(i+1))]) + blockX := X[i*blockSize : (i+1)*blockSize] + blockY := Y[i*blockSize : (i+1)*blockSize] + byteutil.XorBytes(blockY, blockX, offset) + switch instruction { + case enc: + o.block.Encrypt(blockY, blockY) + byteutil.XorBytesMut(blockY, offset) + byteutil.XorBytesMut(checksum, blockX) + case dec: + o.block.Decrypt(blockY, blockY) + byteutil.XorBytesMut(blockY, offset) + byteutil.XorBytesMut(checksum, blockY) + } + } + // + // Process any final partial block and compute raw tag + // + tag := make([]byte, blockSize) + if len(X)%blockSize != 0 { + byteutil.XorBytesMut(offset, o.mask.lAst) + pad := make([]byte, blockSize) + o.block.Encrypt(pad, offset) + chunkX := X[blockSize*m:] + chunkY := Y[blockSize*m : len(X)] + byteutil.XorBytes(chunkY, chunkX, pad[:len(chunkX)]) + // P_* || bit(1) || zeroes(127) - len(P_*) + switch instruction { + case enc: + paddedY := append(chunkX, byte(128)) + paddedY = append(paddedY, make([]byte, blockSize-len(chunkX)-1)...) + byteutil.XorBytesMut(checksum, paddedY) + case dec: + paddedX := append(chunkY, byte(128)) + paddedX = append(paddedX, make([]byte, blockSize-len(chunkY)-1)...) + byteutil.XorBytesMut(checksum, paddedX) + } + byteutil.XorBytes(tag, checksum, offset) + byteutil.XorBytesMut(tag, o.mask.lDol) + o.block.Encrypt(tag, tag) + byteutil.XorBytesMut(tag, o.hash(adata)) + copy(Y[blockSize*m+len(chunkY):], tag[:o.tagSize]) + } else { + byteutil.XorBytes(tag, checksum, offset) + byteutil.XorBytesMut(tag, o.mask.lDol) + o.block.Encrypt(tag, tag) + byteutil.XorBytesMut(tag, o.hash(adata)) + copy(Y[blockSize*m:], tag[:o.tagSize]) + } + return Y +} + +// This hash function is used to compute the tag. Per design, on empty input it +// returns a slice of zeros, of the same length as the underlying block cipher +// block size. +func (o *ocb) hash(adata []byte) []byte { + // + // Consider A as a sequence of 128-bit blocks + // + A := make([]byte, len(adata)) + copy(A, adata) + blockSize := o.block.BlockSize() + + // + // Process any whole blocks + // + sum := make([]byte, blockSize) + offset := make([]byte, blockSize) + m := len(A) / blockSize + for i := 0; i < m; i++ { + chunk := A[blockSize*i : blockSize*(i+1)] + index := bits.TrailingZeros(uint(i + 1)) + // If the mask table is too short + if len(o.mask.L)-1 < index { + o.mask.extendTable(index) + } + byteutil.XorBytesMut(offset, o.mask.L[index]) + byteutil.XorBytesMut(chunk, offset) + o.block.Encrypt(chunk, chunk) + byteutil.XorBytesMut(sum, chunk) + } + + // + // Process any final partial block; compute final hash value + // + if len(A)%blockSize != 0 { + byteutil.XorBytesMut(offset, o.mask.lAst) + // Pad block with 1 || 0 ^ 127 - bitlength(a) + ending := make([]byte, blockSize-len(A)%blockSize) + ending[0] = 0x80 + encrypted := append(A[blockSize*m:], ending...) + byteutil.XorBytesMut(encrypted, offset) + o.block.Encrypt(encrypted, encrypted) + byteutil.XorBytesMut(sum, encrypted) + } + return sum +} + +func initializeMaskTable(block cipher.Block) mask { + // + // Key-dependent variables + // + lAst := make([]byte, block.BlockSize()) + block.Encrypt(lAst, lAst) + lDol := byteutil.GfnDouble(lAst) + L := make([][]byte, 1) + L[0] = byteutil.GfnDouble(lDol) + + return mask{ + lAst: lAst, + lDol: lDol, + L: L, + } +} + +// Extends the L array of mask m up to L[limit], with L[i] = GfnDouble(L[i-1]) +func (m *mask) extendTable(limit int) { + for i := len(m.L); i <= limit; i++ { + m.L = append(m.L, byteutil.GfnDouble(m.L[i-1])) + } +} + +func ocbError(err string) error { + return errors.New("crypto/ocb: " + err) +} diff --git a/vendor/github.com/ProtonMail/go-crypto/ocb/random_vectors.go b/vendor/github.com/ProtonMail/go-crypto/ocb/random_vectors.go new file mode 100644 index 0000000000000..0efaf344fd592 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/ocb/random_vectors.go @@ -0,0 +1,136 @@ +// In the test vectors provided by RFC 7253, the "bottom" +// internal variable, which defines "offset" for the first time, does not +// exceed 15. However, it can attain values up to 63. + +// These vectors include key length in {128, 192, 256}, tag size 128, and +// random nonce, header, and plaintext lengths. + +// This file was automatically generated. + +package ocb + +var randomVectors = []struct { + key, nonce, header, plaintext, ciphertext string +}{ + + {"9438C5D599308EAF13F800D2D31EA7F0", + "C38EE4801BEBFFA1CD8635BE", + "0E507B7DADD8A98CDFE272D3CB6B3E8332B56AE583FB049C0874D4200BED16BD1A044182434E9DA0E841F182DFD5B3016B34641CED0784F1745F63AB3D0DA22D3351C9EF9A658B8081E24498EBF61FCE40DA6D8E184536", + "962D227786FB8913A8BAD5DC3250", + "EEDEF5FFA5986D1E3BF86DDD33EF9ADC79DCA06E215FA772CCBA814F63AD"}, + {"BA7DE631C7D6712167C6724F5B9A2B1D", + "35263EBDA05765DC0E71F1F5", + "0103257B4224507C0242FEFE821EA7FA42E0A82863E5F8B68F7D881B4B44FA428A2B6B21D2F591260802D8AB6D83", + "9D6D1FC93AE8A64E7889B7B2E3521EFA9B920A8DDB692E6F833DDC4A38AFA535E5E2A3ED82CB7E26404AB86C54D01C4668F28398C2DF33D5D561CBA1C8DCFA7A912F5048E545B59483C0E3221F54B14DAA2E4EB657B3BEF9554F34CAD69B2724AE962D3D8A", + "E93852D1985C5E775655E937FA79CE5BF28A585F2AF53A5018853B9634BE3C84499AC0081918FDCE0624494D60E25F76ACD6853AC7576E3C350F332249BFCABD4E73CEABC36BE4EDDA40914E598AE74174A0D7442149B26990899491BDDFE8FC54D6C18E83AE9E9A6FFBF5D376565633862EEAD88D"}, + {"2E74B25289F6FD3E578C24866E9C72A5", + "FD912F15025AF8414642BA1D1D", + "FB5FB8C26F365EEDAB5FE260C6E3CCD27806729C8335F146063A7F9EA93290E56CF84576EB446350D22AD730547C267B1F0BBB97EB34E1E2C41A", + "6C092EBF78F76EE8C1C6E592277D9545BA16EDB67BC7D8480B9827702DC2F8A129E2B08A2CE710CA7E1DA45CE162BB6CD4B512E632116E2211D3C90871EFB06B8D4B902681C7FB", + "6AC0A77F26531BF4F354A1737F99E49BE32ECD909A7A71AD69352906F54B08A9CE9B8CA5D724CBFFC5673437F23F630697F3B84117A1431D6FA8CC13A974FB4AD360300522E09511B99E71065D5AC4BBCB1D791E864EF4"}, + {"E7EC507C802528F790AFF5303A017B17", + "4B97A7A568940A9E3CE7A99E93031E", + "28349BDC5A09390C480F9B8AA3EDEA3DDB8B9D64BCA322C570B8225DF0E31190DAB25A4014BA39519E02ABFB12B89AA28BBFD29E486E7FB28734258C817B63CED9912DBAFEBB93E2798AB2890DE3B0ACFCFF906AB15563EF7823CE83D27CDB251195E22BD1337BCBDE65E7C2C427321C463C2777BFE5AEAA", + "9455B3EA706B74", + "7F33BA3EA848D48A96B9530E26888F43EBD4463C9399B6"}, + {"6C928AA3224736F28EE7378DE0090191", + "8936138E2E4C6A13280017A1622D", + "6202717F2631565BDCDC57C6584543E72A7C8BD444D0D108ED35069819633C", + "DA0691439E5F035F3E455269D14FE5C201C8C9B0A3FE2D3F86BCC59387C868FE65733D388360B31E3CE28B4BF6A8BE636706B536D5720DB66B47CF1C7A5AFD6F61E0EF90F1726D6B0E169F9A768B2B7AE4EE00A17F630AC905FCAAA1B707FFF25B3A1AAE83B504837C64A5639B2A34002B300EC035C9B43654DA55", + "B8804D182AB0F0EEB464FA7BD1329AD6154F982013F3765FEDFE09E26DAC078C9C1439BFC1159D6C02A25E3FF83EF852570117B315852AD5EE20E0FA3AA0A626B0E43BC0CEA38B44579DD36803455FB46989B90E6D229F513FD727AF8372517E9488384C515D6067704119C931299A0982EDDFB9C2E86A90C450C077EB222511EC9CCABC9FCFDB19F70088"}, + {"ECEA315CA4B3F425B0C9957A17805EA4", + "664CDAE18403F4F9BA13015A44FC", + "642AFB090D6C6DB46783F08B01A3EF2A8FEB5736B531EAC226E7888FCC8505F396818F83105065FACB3267485B9E5E4A0261F621041C08FCCB2A809A49AB5252A91D0971BCC620B9D614BD77E57A0EED2FA5", + "6852C31F8083E20E364CEA21BB7854D67CEE812FE1C9ED2425C0932A90D3780728D1BB", + "2ECEF962A9695A463ADABB275BDA9FF8B2BA57AEC2F52EFFB700CD9271A74D2A011C24AEA946051BD6291776429B7E681BA33E"}, + {"4EE616C4A58AAA380878F71A373461F6", + "91B8C9C176D9C385E9C47E52", + "CDA440B7F9762C572A718AC754EDEECC119E5EE0CCB9FEA4FFB22EEE75087C032EBF3DA9CDD8A28CC010B99ED45143B41A4BA50EA2A005473F89639237838867A57F23B0F0ED3BF22490E4501DAC9C658A9B9F", + "D6E645FA9AE410D15B8123FD757FA356A8DBE9258DDB5BE88832E615910993F497EC", + "B70ED7BF959FB2AAED4F36174A2A99BFB16992C8CDF369C782C4DB9C73DE78C5DB8E0615F647243B97ACDB24503BC9CADC48"}, + {"DCD475773136C830D5E3D0C5FE05B7FF", + "BB8E1FBB483BE7616A922C4A", + "36FEF2E1CB29E76A6EA663FC3AF66ECD7404F466382F7B040AABED62293302B56E8783EF7EBC21B4A16C3E78A7483A0A403F253A2CDC5BBF79DC3DAE6C73F39A961D8FBBE8D41B", + "441E886EA38322B2437ECA7DEB5282518865A66780A454E510878E61BFEC3106A3CD93D2A02052E6F9E1832F9791053E3B76BF4C07EFDD6D4106E3027FABB752E60C1AA425416A87D53938163817A1051EBA1D1DEEB4B9B25C7E97368B52E5911A31810B0EC5AF547559B6142D9F4C4A6EF24A4CF75271BF9D48F62B", + "1BE4DD2F4E25A6512C2CC71D24BBB07368589A94C2714962CD0ACE5605688F06342587521E75F0ACAFFD86212FB5C34327D238DB36CF2B787794B9A4412E7CD1410EA5DDD2450C265F29CF96013CD213FD2880657694D718558964BC189B4A84AFCF47EB012935483052399DBA5B088B0A0477F20DFE0E85DCB735E21F22A439FB837DD365A93116D063E607"}, + {"3FBA2B3D30177FFE15C1C59ED2148BB2C091F5615FBA7C07", + "FACF804A4BEBF998505FF9DE", + "8213B9263B2971A5BDA18DBD02208EE1", + "15B323926993B326EA19F892D704439FC478828322AF72118748284A1FD8A6D814E641F70512FD706980337379F31DC63355974738D7FEA87AD2858C0C2EBBFBE74371C21450072373C7B651B334D7C4D43260B9D7CCD3AF9EDB", + "6D35DC1469B26E6AAB26272A41B46916397C24C485B61162E640A062D9275BC33DDCFD3D9E1A53B6C8F51AC89B66A41D59B3574197A40D9B6DCF8A4E2A001409C8112F16B9C389E0096179DB914E05D6D11ED0005AD17E1CE105A2F0BAB8F6B1540DEB968B7A5428FF44"}, + {"53B52B8D4D748BCDF1DDE68857832FA46227FA6E2F32EFA1", + "0B0EF53D4606B28D1398355F", + "F23882436349094AF98BCACA8218E81581A043B19009E28EFBF2DE37883E04864148CC01D240552CA8844EC1456F42034653067DA67E80F87105FD06E14FF771246C9612867BE4D215F6D761", + "F15030679BD4088D42CAC9BF2E9606EAD4798782FA3ED8C57EBE7F84A53236F51B25967C6489D0CD20C9EEA752F9BC", + "67B96E2D67C3729C96DAEAEDF821D61C17E648643A2134C5621FEC621186915AD80864BFD1EB5B238BF526A679385E012A457F583AFA78134242E9D9C1B4E4"}, + {"0272DD80F23399F49BFC320381A5CD8225867245A49A7D41", + "5C83F4896D0738E1366B1836", + "69B0337289B19F73A12BAEEA857CCAF396C11113715D9500CCCF48BA08CFF12BC8B4BADB3084E63B85719DB5058FA7C2C11DEB096D7943CFA7CAF5", + "C01AD10FC8B562CD17C7BC2FAB3E26CBDFF8D7F4DEA816794BBCC12336991712972F52816AABAB244EB43B0137E2BAC1DD413CE79531E78BEF782E6B439612BB3AEF154DE3502784F287958EBC159419F9EBA27916A28D6307324129F506B1DE80C1755A929F87", + "FEFE52DD7159C8DD6E8EC2D3D3C0F37AB6CB471A75A071D17EC4ACDD8F3AA4D7D4F7BB559F3C09099E3D9003E5E8AA1F556B79CECDE66F85B08FA5955E6976BF2695EA076388A62D2AD5BAB7CBF1A7F3F4C8D5CDF37CDE99BD3E30B685D9E5EEE48C7C89118EF4878EB89747F28271FA2CC45F8E9E7601"}, + {"3EEAED04A455D6E5E5AB53CFD5AFD2F2BC625C7BF4BE49A5", + "36B88F63ADBB5668588181D774", + "D367E3CB3703E762D23C6533188EF7028EFF9D935A3977150361997EC9DEAF1E4794BDE26AA8B53C124980B1362EC86FCDDFC7A90073171C1BAEE351A53234B86C66E8AB92FAE99EC6967A6D3428892D80", + "573454C719A9A55E04437BF7CBAAF27563CCCD92ADD5E515CD63305DFF0687E5EEF790C5DCA5C0033E9AB129505E2775438D92B38F08F3B0356BA142C6F694", + "E9F79A5B432D9E682C9AAA5661CFC2E49A0FCB81A431E54B42EB73DD3BED3F377FEC556ABA81624BA64A5D739AD41467460088F8D4F442180A9382CA635745473794C382FCDDC49BA4EB6D8A44AE3C"}, + {"B695C691538F8CBD60F039D0E28894E3693CC7C36D92D79D", + "BC099AEB637361BAC536B57618", + "BFFF1A65AE38D1DC142C71637319F5F6508E2CB33C9DCB94202B359ED5A5ED8042E7F4F09231D32A7242976677E6F4C549BF65FADC99E5AF43F7A46FD95E16C2", + "081DF3FD85B415D803F0BE5AC58CFF0023FDDED99788296C3731D8", + "E50C64E3614D94FE69C47092E46ACC9957C6FEA2CCBF96BC62FBABE7424753C75F9C147C42AE26FE171531"}, + {"C9ACBD2718F0689A1BE9802A551B6B8D9CF5614DAF5E65ED", + "B1B0AAF373B8B026EB80422051D8", + "6648C0E61AC733C76119D23FB24548D637751387AA2EAE9D80E912B7BD486CAAD9EAF4D7A5FE2B54AAD481E8EC94BB4D558000896E2010462B70C9FED1E7273080D1", + "189F591F6CB6D59AFEDD14C341741A8F1037DC0DF00FC57CE65C30F49E860255CEA5DC6019380CC0FE8880BC1A9E685F41C239C38F36E3F2A1388865C5C311059C0A", + "922A5E949B61D03BE34AB5F4E58607D4504EA14017BB363DAE3C873059EA7A1C77A746FB78981671D26C2CF6D9F24952D510044CE02A10177E9DB42D0145211DFE6E84369C5E3BC2669EAB4147B2822895F9"}, + {"7A832BD2CF5BF4919F353CE2A8C86A5E406DA2D52BE16A72", + "2F2F17CECF7E5A756D10785A3CB9DB", + "61DA05E3788CC2D8405DBA70C7A28E5AF699863C9F72E6C6770126929F5D6FA267F005EBCF49495CB46400958A3AE80D1289D1C671", + "44E91121195A41AF14E8CFDBD39A4B517BE0DF1A72977ED8A3EEF8EEDA1166B2EB6DB2C4AE2E74FA0F0C74537F659BFBD141E5DDEC67E64EDA85AABD3F52C85A785B9FB3CECD70E7DF", + "BEDF596EA21288D2B84901E188F6EE1468B14D5161D3802DBFE00D60203A24E2AB62714BF272A45551489838C3A7FEAADC177B591836E73684867CCF4E12901DCF2064058726BBA554E84ADC5136F507E961188D4AF06943D3"}, + {"1508E8AE9079AA15F1CEC4F776B4D11BCCB061B58AA56C18", + "BCA625674F41D1E3AB47672DC0C3", + "8B12CF84F16360F0EAD2A41BC021530FFCEC7F3579CAE658E10E2D3D81870F65AFCED0C77C6C4C6E6BA424FF23088C796BA6195ABA35094BF1829E089662E7A95FC90750AE16D0C8AFA55DAC789D7735B970B58D4BE7CEC7341DA82A0179A01929C27A59C5063215B859EA43", + "E525422519ECE070E82C", + "B47BC07C3ED1C0A43BA52C43CBACBCDBB29CAF1001E09FDF7107"}, + {"7550C2761644E911FE9ADD119BAC07376BEA442845FEAD876D7E7AC1B713E464", + "36D2EC25ADD33CDEDF495205BBC923", + "7FCFE81A3790DE97FFC3DE160C470847EA7E841177C2F759571CBD837EA004A6CA8C6F4AEBFF2E9FD552D73EB8A30705D58D70C0B67AEEA280CBBF0A477358ACEF1E7508F2735CD9A0E4F9AC92B8C008F575D3B6278F1C18BD01227E3502E5255F3AB1893632AD00C717C588EF652A51A43209E7EE90", + "2B1A62F8FDFAA3C16470A21AD307C9A7D03ADE8EF72C69B06F8D738CDE578D7AEFD0D40BD9C022FB9F580DF5394C998ACCCEFC5471A3996FB8F1045A81FDC6F32D13502EA65A211390C8D882B8E0BEFD8DD8CBEF51D1597B124E9F7F", + "C873E02A22DB89EB0787DB6A60B99F7E4A0A085D5C4232A81ADCE2D60AA36F92DDC33F93DD8640AC0E08416B187FB382B3EC3EE85A64B0E6EE41C1366A5AD2A282F66605E87031CCBA2FA7B2DA201D975994AADE3DD1EE122AE09604AD489B84BF0C1AB7129EE16C6934850E"}, + {"A51300285E554FDBDE7F771A9A9A80955639DD87129FAEF74987C91FB9687C71", + "81691D5D20EC818FCFF24B33DECC", + "C948093218AA9EB2A8E44A87EEA73FC8B6B75A196819A14BD83709EA323E8DF8B491045220E1D88729A38DBCFFB60D3056DAD4564498FD6574F74512945DEB34B69329ACED9FFC05D5D59DFCD5B973E2ACAFE6AD1EF8BBBC49351A2DD12508ED89ED", + "EB861165DAF7625F827C6B574ED703F03215", + "C6CD1CE76D2B3679C1B5AA1CFD67CCB55444B6BFD3E22C81CBC9BB738796B83E54E3"}, + {"8CE0156D26FAEB7E0B9B800BBB2E9D4075B5EAC5C62358B0E7F6FCE610223282", + "D2A7B94DD12CDACA909D3AD7", + "E021A78F374FC271389AB9A3E97077D755", + "7C26000B58929F5095E1CEE154F76C2A299248E299F9B5ADE6C403AA1FD4A67FD4E0232F214CE7B919EE7A1027D2B76C57475715CD078461", + "C556FB38DF069B56F337B5FF5775CE6EAA16824DFA754F20B78819028EA635C3BB7AA731DE8776B2DCB67DCA2D33EEDF3C7E52EA450013722A41755A0752433ED17BDD5991AAE77A"}, + {"1E8000A2CE00A561C9920A30BF0D7B983FEF8A1014C8F04C35CA6970E6BA02BD", + "65ED3D63F79F90BBFD19775E", + "336A8C0B7243582A46B221AA677647FCAE91", + "134A8B34824A290E7B", + "914FBEF80D0E6E17F8BDBB6097EBF5FBB0554952DC2B9E5151"}, + {"53D5607BBE690B6E8D8F6D97F3DF2BA853B682597A214B8AA0EA6E598650AF15", + "C391A856B9FE234E14BA1AC7BB40FF", + "479682BC21349C4BE1641D5E78FE2C79EC1B9CF5470936DCAD9967A4DCD7C4EFADA593BC9EDE71E6A08829B8580901B61E274227E9D918502DE3", + "EAD154DC09C5E26C5D26FF33ED148B27120C7F2C23225CC0D0631B03E1F6C6D96FEB88C1A4052ACB4CE746B884B6502931F407021126C6AAB8C514C077A5A38438AE88EE", + "938821286EBB671D999B87C032E1D6055392EB564E57970D55E545FC5E8BAB90E6E3E3C0913F6320995FC636D72CD9919657CC38BD51552F4A502D8D1FE56DB33EBAC5092630E69EBB986F0E15CEE9FC8C052501"}, + {"294362FCC984F440CEA3E9F7D2C06AF20C53AAC1B3738CA2186C914A6E193ABB", + "B15B61C8BB39261A8F55AB178EC3", + "D0729B6B75BB", + "2BD089ADCE9F334BAE3B065996C7D616DD0C27DF4218DCEEA0FBCA0F968837CE26B0876083327E25681FDDD620A32EC0DA12F73FAE826CC94BFF2B90A54D2651", + "AC94B25E4E21DE2437B806966CCD5D9385EF0CD4A51AB9FA6DE675C7B8952D67802E9FEC1FDE9F5D1EAB06057498BC0EEA454804FC9D2068982A3E24182D9AC2E7AB9994DDC899A604264583F63D066B"}, + {"959DBFEB039B1A5B8CE6A44649B602AAA5F98A906DB96143D202CD2024F749D9", + "01D7BDB1133E9C347486C1EFA6", + "F3843955BD741F379DD750585EDC55E2CDA05CCBA8C1F4622AC2FE35214BC3A019B8BD12C4CC42D9213D1E1556941E8D8450830287FFB3B763A13722DD4140ED9846FB5FFF745D7B0B967D810A068222E10B259AF1D392035B0D83DC1498A6830B11B2418A840212599171E0258A1C203B05362978", + "A21811232C950FA8B12237C2EBD6A7CD2C3A155905E9E0C7C120", + "63C1CE397B22F1A03F1FA549B43178BC405B152D3C95E977426D519B3DFCA28498823240592B6EEE7A14"}, + {"096AE499F5294173F34FF2B375F0E5D5AB79D0D03B33B1A74D7D576826345DF4", + "0C52B3D11D636E5910A4DD76D32C", + "229E9ECA3053789E937447BC719467075B6138A142DA528DA8F0CF8DDF022FD9AF8E74779BA3AC306609", + "8B7A00038783E8BAF6EDEAE0C4EAB48FC8FD501A588C7E4A4DB71E3604F2155A97687D3D2FFF8569261375A513CF4398CE0F87CA1658A1050F6EF6C4EA3E25", + "C20B6CF8D3C8241825FD90B2EDAC7593600646E579A8D8DAAE9E2E40C3835FE801B2BE4379131452BC5182C90307B176DFBE2049544222FE7783147B690774F6D9D7CEF52A91E61E298E9AA15464AC"}, +} diff --git a/vendor/github.com/ProtonMail/go-crypto/ocb/rfc7253_test_vectors_suite_a.go b/vendor/github.com/ProtonMail/go-crypto/ocb/rfc7253_test_vectors_suite_a.go new file mode 100644 index 0000000000000..843085589c838 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/ocb/rfc7253_test_vectors_suite_a.go @@ -0,0 +1,78 @@ +package ocb + +import ( + "encoding/hex" +) + +// Test vectors from https://tools.ietf.org/html/rfc7253. Note that key is +// shared accross tests. +var testKey, _ = hex.DecodeString("000102030405060708090A0B0C0D0E0F") + +var rfc7253testVectors = []struct { + nonce, header, plaintext, ciphertext string +}{ + {"BBAA99887766554433221100", + "", + "", + "785407BFFFC8AD9EDCC5520AC9111EE6"}, + {"BBAA99887766554433221101", + "0001020304050607", + "0001020304050607", + "6820B3657B6F615A5725BDA0D3B4EB3A257C9AF1F8F03009"}, + {"BBAA99887766554433221102", + "0001020304050607", + "", + "81017F8203F081277152FADE694A0A00"}, + {"BBAA99887766554433221103", + "", + "0001020304050607", + "45DD69F8F5AAE72414054CD1F35D82760B2CD00D2F99BFA9"}, + {"BBAA99887766554433221104", + "000102030405060708090A0B0C0D0E0F", + "000102030405060708090A0B0C0D0E0F", + "571D535B60B277188BE5147170A9A22C3AD7A4FF3835B8C5701C1CCEC8FC3358"}, + {"BBAA99887766554433221105", + "000102030405060708090A0B0C0D0E0F", + "", + "8CF761B6902EF764462AD86498CA6B97"}, + {"BBAA99887766554433221106", + "", + "000102030405060708090A0B0C0D0E0F", + "5CE88EC2E0692706A915C00AEB8B2396F40E1C743F52436BDF06D8FA1ECA343D"}, + {"BBAA99887766554433221107", + "000102030405060708090A0B0C0D0E0F1011121314151617", + "000102030405060708090A0B0C0D0E0F1011121314151617", + "1CA2207308C87C010756104D8840CE1952F09673A448A122C92C62241051F57356D7F3C90BB0E07F"}, + {"BBAA99887766554433221108", + "000102030405060708090A0B0C0D0E0F1011121314151617", + "", + "6DC225A071FC1B9F7C69F93B0F1E10DE"}, + {"BBAA99887766554433221109", + "", + "000102030405060708090A0B0C0D0E0F1011121314151617", + "221BD0DE7FA6FE993ECCD769460A0AF2D6CDED0C395B1C3CE725F32494B9F914D85C0B1EB38357FF"}, + {"BBAA9988776655443322110A", + "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F", + "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F", + "BD6F6C496201C69296C11EFD138A467ABD3C707924B964DEAFFC40319AF5A48540FBBA186C5553C68AD9F592A79A4240"}, + {"BBAA9988776655443322110B", + "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F", + "", + "FE80690BEE8A485D11F32965BC9D2A32"}, + {"BBAA9988776655443322110C", + "", + "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F", + "2942BFC773BDA23CABC6ACFD9BFD5835BD300F0973792EF46040C53F1432BCDFB5E1DDE3BC18A5F840B52E653444D5DF"}, + {"BBAA9988776655443322110D", + "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627", + "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627", + "D5CA91748410C1751FF8A2F618255B68A0A12E093FF454606E59F9C1D0DDC54B65E8628E568BAD7AED07BA06A4A69483A7035490C5769E60"}, + {"BBAA9988776655443322110E", + "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627", + "", + "C5CD9D1850C141E358649994EE701B68"}, + {"BBAA9988776655443322110F", + "", + "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627", + "4412923493C57D5DE0D700F753CCE0D1D2D95060122E9F15A5DDBFC5787E50B5CC55EE507BCB084E479AD363AC366B95A98CA5F3000B1479"}, +} diff --git a/vendor/github.com/ProtonMail/go-crypto/ocb/rfc7253_test_vectors_suite_b.go b/vendor/github.com/ProtonMail/go-crypto/ocb/rfc7253_test_vectors_suite_b.go new file mode 100644 index 0000000000000..5dc158f0128e1 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/ocb/rfc7253_test_vectors_suite_b.go @@ -0,0 +1,24 @@ +package ocb + +// Second set of test vectors from https://tools.ietf.org/html/rfc7253 +var rfc7253TestVectorTaglen96 = struct { + key, nonce, header, plaintext, ciphertext string +}{"0F0E0D0C0B0A09080706050403020100", + "BBAA9988776655443322110D", + "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627", + "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627", + "1792A4E31E0755FB03E31B22116E6C2DDF9EFD6E33D536F1A0124B0A55BAE884ED93481529C76B6AD0C515F4D1CDD4FDAC4F02AA"} + +var rfc7253AlgorithmTest = []struct { + KEYLEN, TAGLEN int + OUTPUT string }{ + {128, 128, "67E944D23256C5E0B6C61FA22FDF1EA2"}, + {192, 128, "F673F2C3E7174AAE7BAE986CA9F29E17"}, + {256, 128, "D90EB8E9C977C88B79DD793D7FFA161C"}, + {128, 96, "77A3D8E73589158D25D01209"}, + {192, 96, "05D56EAD2752C86BE6932C5E"}, + {256, 96, "5458359AC23B0CBA9E6330DD"}, + {128, 64, "192C9B7BD90BA06A"}, + {192, 64, "0066BC6E0EF34E24"}, + {256, 64, "7D4EA5D445501CBE"}, + } diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/aes/keywrap/keywrap.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/aes/keywrap/keywrap.go new file mode 100644 index 0000000000000..3c6251d1ce67a --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/aes/keywrap/keywrap.go @@ -0,0 +1,153 @@ +// Copyright 2014 Matthew Endsley +// All rights reserved +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted providing that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + +// Package keywrap is an implementation of the RFC 3394 AES key wrapping +// algorithm. This is used in OpenPGP with elliptic curve keys. +package keywrap + +import ( + "crypto/aes" + "encoding/binary" + "errors" +) + +var ( + // ErrWrapPlaintext is returned if the plaintext is not a multiple + // of 64 bits. + ErrWrapPlaintext = errors.New("keywrap: plainText must be a multiple of 64 bits") + + // ErrUnwrapCiphertext is returned if the ciphertext is not a + // multiple of 64 bits. + ErrUnwrapCiphertext = errors.New("keywrap: cipherText must by a multiple of 64 bits") + + // ErrUnwrapFailed is returned if unwrapping a key fails. + ErrUnwrapFailed = errors.New("keywrap: failed to unwrap key") + + // NB: the AES NewCipher call only fails if the key is an invalid length. + + // ErrInvalidKey is returned when the AES key is invalid. + ErrInvalidKey = errors.New("keywrap: invalid AES key") +) + +// Wrap a key using the RFC 3394 AES Key Wrap Algorithm. +func Wrap(key, plainText []byte) ([]byte, error) { + if len(plainText)%8 != 0 { + return nil, ErrWrapPlaintext + } + + c, err := aes.NewCipher(key) + if err != nil { + return nil, ErrInvalidKey + } + + nblocks := len(plainText) / 8 + + // 1) Initialize variables. + var block [aes.BlockSize]byte + // - Set A = IV, an initial value (see 2.2.3) + for ii := 0; ii < 8; ii++ { + block[ii] = 0xA6 + } + + // - For i = 1 to n + // - Set R[i] = P[i] + intermediate := make([]byte, len(plainText)) + copy(intermediate, plainText) + + // 2) Calculate intermediate values. + for ii := 0; ii < 6; ii++ { + for jj := 0; jj < nblocks; jj++ { + // - B = AES(K, A | R[i]) + copy(block[8:], intermediate[jj*8:jj*8+8]) + c.Encrypt(block[:], block[:]) + + // - A = MSB(64, B) ^ t where t = (n*j)+1 + t := uint64(ii*nblocks + jj + 1) + val := binary.BigEndian.Uint64(block[:8]) ^ t + binary.BigEndian.PutUint64(block[:8], val) + + // - R[i] = LSB(64, B) + copy(intermediate[jj*8:jj*8+8], block[8:]) + } + } + + // 3) Output results. + // - Set C[0] = A + // - For i = 1 to n + // - C[i] = R[i] + return append(block[:8], intermediate...), nil +} + +// Unwrap a key using the RFC 3394 AES Key Wrap Algorithm. +func Unwrap(key, cipherText []byte) ([]byte, error) { + if len(cipherText)%8 != 0 { + return nil, ErrUnwrapCiphertext + } + + c, err := aes.NewCipher(key) + if err != nil { + return nil, ErrInvalidKey + } + + nblocks := len(cipherText)/8 - 1 + + // 1) Initialize variables. + var block [aes.BlockSize]byte + // - Set A = C[0] + copy(block[:8], cipherText[:8]) + + // - For i = 1 to n + // - Set R[i] = C[i] + intermediate := make([]byte, len(cipherText)-8) + copy(intermediate, cipherText[8:]) + + // 2) Compute intermediate values. + for jj := 5; jj >= 0; jj-- { + for ii := nblocks - 1; ii >= 0; ii-- { + // - B = AES-1(K, (A ^ t) | R[i]) where t = n*j+1 + // - A = MSB(64, B) + t := uint64(jj*nblocks + ii + 1) + val := binary.BigEndian.Uint64(block[:8]) ^ t + binary.BigEndian.PutUint64(block[:8], val) + + copy(block[8:], intermediate[ii*8:ii*8+8]) + c.Decrypt(block[:], block[:]) + + // - R[i] = LSB(B, 64) + copy(intermediate[ii*8:ii*8+8], block[8:]) + } + } + + // 3) Output results. + // - If A is an appropriate initial value (see 2.2.3), + for ii := 0; ii < 8; ii++ { + if block[ii] != 0xA6 { + return nil, ErrUnwrapFailed + } + } + + // - For i = 1 to n + // - P[i] = R[i] + return intermediate, nil +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/armor/armor.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/armor/armor.go new file mode 100644 index 0000000000000..3b357e5851b78 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/armor/armor.go @@ -0,0 +1,224 @@ +// Copyright 2010 The 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 armor implements OpenPGP ASCII Armor, see RFC 4880. OpenPGP Armor is +// very similar to PEM except that it has an additional CRC checksum. +package armor // import "github.com/ProtonMail/go-crypto/openpgp/armor" + +import ( + "bufio" + "bytes" + "encoding/base64" + "github.com/ProtonMail/go-crypto/openpgp/errors" + "io" +) + +// A Block represents an OpenPGP armored structure. +// +// The encoded form is: +// -----BEGIN Type----- +// Headers +// +// base64-encoded Bytes +// '=' base64 encoded checksum +// -----END Type----- +// where Headers is a possibly empty sequence of Key: Value lines. +// +// Since the armored data can be very large, this package presents a streaming +// interface. +type Block struct { + Type string // The type, taken from the preamble (i.e. "PGP SIGNATURE"). + Header map[string]string // Optional headers. + Body io.Reader // A Reader from which the contents can be read + lReader lineReader + oReader openpgpReader +} + +var ArmorCorrupt error = errors.StructuralError("armor invalid") + +const crc24Init = 0xb704ce +const crc24Poly = 0x1864cfb +const crc24Mask = 0xffffff + +// crc24 calculates the OpenPGP checksum as specified in RFC 4880, section 6.1 +func crc24(crc uint32, d []byte) uint32 { + for _, b := range d { + crc ^= uint32(b) << 16 + for i := 0; i < 8; i++ { + crc <<= 1 + if crc&0x1000000 != 0 { + crc ^= crc24Poly + } + } + } + return crc +} + +var armorStart = []byte("-----BEGIN ") +var armorEnd = []byte("-----END ") +var armorEndOfLine = []byte("-----") + +// lineReader wraps a line based reader. It watches for the end of an armor +// block and records the expected CRC value. +type lineReader struct { + in *bufio.Reader + buf []byte + eof bool + crc uint32 + crcSet bool +} + +func (l *lineReader) Read(p []byte) (n int, err error) { + if l.eof { + return 0, io.EOF + } + + if len(l.buf) > 0 { + n = copy(p, l.buf) + l.buf = l.buf[n:] + return + } + + line, isPrefix, err := l.in.ReadLine() + if err != nil { + return + } + if isPrefix { + return 0, ArmorCorrupt + } + + if bytes.HasPrefix(line, armorEnd) { + l.eof = true + return 0, io.EOF + } + + if len(line) == 5 && line[0] == '=' { + // This is the checksum line + var expectedBytes [3]byte + var m int + m, err = base64.StdEncoding.Decode(expectedBytes[0:], line[1:]) + if m != 3 || err != nil { + return + } + l.crc = uint32(expectedBytes[0])<<16 | + uint32(expectedBytes[1])<<8 | + uint32(expectedBytes[2]) + + line, _, err = l.in.ReadLine() + if err != nil && err != io.EOF { + return + } + if !bytes.HasPrefix(line, armorEnd) { + return 0, ArmorCorrupt + } + + l.eof = true + l.crcSet = true + return 0, io.EOF + } + + if len(line) > 96 { + return 0, ArmorCorrupt + } + + n = copy(p, line) + bytesToSave := len(line) - n + if bytesToSave > 0 { + if cap(l.buf) < bytesToSave { + l.buf = make([]byte, 0, bytesToSave) + } + l.buf = l.buf[0:bytesToSave] + copy(l.buf, line[n:]) + } + + return +} + +// openpgpReader passes Read calls to the underlying base64 decoder, but keeps +// a running CRC of the resulting data and checks the CRC against the value +// found by the lineReader at EOF. +type openpgpReader struct { + lReader *lineReader + b64Reader io.Reader + currentCRC uint32 +} + +func (r *openpgpReader) Read(p []byte) (n int, err error) { + n, err = r.b64Reader.Read(p) + r.currentCRC = crc24(r.currentCRC, p[:n]) + + if err == io.EOF && r.lReader.crcSet && r.lReader.crc != uint32(r.currentCRC&crc24Mask) { + return 0, ArmorCorrupt + } + + return +} + +// Decode reads a PGP armored block from the given Reader. It will ignore +// leading garbage. If it doesn't find a block, it will return nil, io.EOF. The +// given Reader is not usable after calling this function: an arbitrary amount +// of data may have been read past the end of the block. +func Decode(in io.Reader) (p *Block, err error) { + r := bufio.NewReaderSize(in, 100) + var line []byte + ignoreNext := false + +TryNextBlock: + p = nil + + // Skip leading garbage + for { + ignoreThis := ignoreNext + line, ignoreNext, err = r.ReadLine() + if err != nil { + return + } + if ignoreNext || ignoreThis { + continue + } + line = bytes.TrimSpace(line) + if len(line) > len(armorStart)+len(armorEndOfLine) && bytes.HasPrefix(line, armorStart) { + break + } + } + + p = new(Block) + p.Type = string(line[len(armorStart) : len(line)-len(armorEndOfLine)]) + p.Header = make(map[string]string) + nextIsContinuation := false + var lastKey string + + // Read headers + for { + isContinuation := nextIsContinuation + line, nextIsContinuation, err = r.ReadLine() + if err != nil { + p = nil + return + } + if isContinuation { + p.Header[lastKey] += string(line) + continue + } + line = bytes.TrimSpace(line) + if len(line) == 0 { + break + } + + i := bytes.Index(line, []byte(": ")) + if i == -1 { + goto TryNextBlock + } + lastKey = string(line[:i]) + p.Header[lastKey] = string(line[i+2:]) + } + + p.lReader.in = r + p.oReader.currentCRC = crc24Init + p.oReader.lReader = &p.lReader + p.oReader.b64Reader = base64.NewDecoder(base64.StdEncoding, &p.lReader) + p.Body = &p.oReader + + return +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/armor/encode.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/armor/encode.go new file mode 100644 index 0000000000000..6f07582c37cea --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/armor/encode.go @@ -0,0 +1,160 @@ +// Copyright 2010 The 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 armor + +import ( + "encoding/base64" + "io" +) + +var armorHeaderSep = []byte(": ") +var blockEnd = []byte("\n=") +var newline = []byte("\n") +var armorEndOfLineOut = []byte("-----\n") + +// writeSlices writes its arguments to the given Writer. +func writeSlices(out io.Writer, slices ...[]byte) (err error) { + for _, s := range slices { + _, err = out.Write(s) + if err != nil { + return err + } + } + return +} + +// lineBreaker breaks data across several lines, all of the same byte length +// (except possibly the last). Lines are broken with a single '\n'. +type lineBreaker struct { + lineLength int + line []byte + used int + out io.Writer + haveWritten bool +} + +func newLineBreaker(out io.Writer, lineLength int) *lineBreaker { + return &lineBreaker{ + lineLength: lineLength, + line: make([]byte, lineLength), + used: 0, + out: out, + } +} + +func (l *lineBreaker) Write(b []byte) (n int, err error) { + n = len(b) + + if n == 0 { + return + } + + if l.used == 0 && l.haveWritten { + _, err = l.out.Write([]byte{'\n'}) + if err != nil { + return + } + } + + if l.used+len(b) < l.lineLength { + l.used += copy(l.line[l.used:], b) + return + } + + l.haveWritten = true + _, err = l.out.Write(l.line[0:l.used]) + if err != nil { + return + } + excess := l.lineLength - l.used + l.used = 0 + + _, err = l.out.Write(b[0:excess]) + if err != nil { + return + } + + _, err = l.Write(b[excess:]) + return +} + +func (l *lineBreaker) Close() (err error) { + if l.used > 0 { + _, err = l.out.Write(l.line[0:l.used]) + if err != nil { + return + } + } + + return +} + +// encoding keeps track of a running CRC24 over the data which has been written +// to it and outputs a OpenPGP checksum when closed, followed by an armor +// trailer. +// +// It's built into a stack of io.Writers: +// encoding -> base64 encoder -> lineBreaker -> out +type encoding struct { + out io.Writer + breaker *lineBreaker + b64 io.WriteCloser + crc uint32 + blockType []byte +} + +func (e *encoding) Write(data []byte) (n int, err error) { + e.crc = crc24(e.crc, data) + return e.b64.Write(data) +} + +func (e *encoding) Close() (err error) { + err = e.b64.Close() + if err != nil { + return + } + e.breaker.Close() + + var checksumBytes [3]byte + checksumBytes[0] = byte(e.crc >> 16) + checksumBytes[1] = byte(e.crc >> 8) + checksumBytes[2] = byte(e.crc) + + var b64ChecksumBytes [4]byte + base64.StdEncoding.Encode(b64ChecksumBytes[:], checksumBytes[:]) + + return writeSlices(e.out, blockEnd, b64ChecksumBytes[:], newline, armorEnd, e.blockType, armorEndOfLine) +} + +// Encode returns a WriteCloser which will encode the data written to it in +// OpenPGP armor. +func Encode(out io.Writer, blockType string, headers map[string]string) (w io.WriteCloser, err error) { + bType := []byte(blockType) + err = writeSlices(out, armorStart, bType, armorEndOfLineOut) + if err != nil { + return + } + + for k, v := range headers { + err = writeSlices(out, []byte(k), armorHeaderSep, []byte(v), newline) + if err != nil { + return + } + } + + _, err = out.Write(newline) + if err != nil { + return + } + + e := &encoding{ + out: out, + breaker: newLineBreaker(out, 64), + crc: crc24Init, + blockType: bType, + } + e.b64 = base64.NewEncoder(base64.StdEncoding, e.breaker) + return e, nil +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/canonical_text.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/canonical_text.go new file mode 100644 index 0000000000000..a94f6150c4b23 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/canonical_text.go @@ -0,0 +1,65 @@ +// Copyright 2011 The 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 openpgp + +import ( + "hash" + "io" +) + +// NewCanonicalTextHash reformats text written to it into the canonical +// form and then applies the hash h. See RFC 4880, section 5.2.1. +func NewCanonicalTextHash(h hash.Hash) hash.Hash { + return &canonicalTextHash{h, 0} +} + +type canonicalTextHash struct { + h hash.Hash + s int +} + +var newline = []byte{'\r', '\n'} + +func writeCanonical(cw io.Writer, buf []byte, s *int) (int, error) { + start := 0 + for i, c := range buf { + switch *s { + case 0: + if c == '\r' { + *s = 1 + } else if c == '\n' { + cw.Write(buf[start:i]) + cw.Write(newline) + start = i + 1 + } + case 1: + *s = 0 + } + } + + cw.Write(buf[start:]) + return len(buf), nil +} + +func (cth *canonicalTextHash) Write(buf []byte) (int, error) { + return writeCanonical(cth.h, buf, &cth.s) +} + +func (cth *canonicalTextHash) Sum(in []byte) []byte { + return cth.h.Sum(in) +} + +func (cth *canonicalTextHash) Reset() { + cth.h.Reset() + cth.s = 0 +} + +func (cth *canonicalTextHash) Size() int { + return cth.h.Size() +} + +func (cth *canonicalTextHash) BlockSize() int { + return cth.h.BlockSize() +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/ecdh/ecdh.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/ecdh/ecdh.go new file mode 100644 index 0000000000000..0b49be4bf3792 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/ecdh/ecdh.go @@ -0,0 +1,165 @@ +// Copyright 2017 The 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 ecdh implements ECDH encryption, suitable for OpenPGP, +// as specified in RFC 6637, section 8. +package ecdh + +import ( + "bytes" + "crypto/elliptic" + "errors" + "io" + "math/big" + + "github.com/ProtonMail/go-crypto/openpgp/aes/keywrap" + "github.com/ProtonMail/go-crypto/openpgp/internal/algorithm" + "github.com/ProtonMail/go-crypto/openpgp/internal/ecc" +) + +type KDF struct { + Hash algorithm.Hash + Cipher algorithm.Cipher +} + +type PublicKey struct { + ecc.CurveType + elliptic.Curve + X, Y *big.Int + KDF +} + +type PrivateKey struct { + PublicKey + D []byte +} + +func GenerateKey(c elliptic.Curve, kdf KDF, rand io.Reader) (priv *PrivateKey, err error) { + priv = new(PrivateKey) + priv.PublicKey.Curve = c + priv.PublicKey.KDF = kdf + priv.D, priv.PublicKey.X, priv.PublicKey.Y, err = elliptic.GenerateKey(c, rand) + return +} + +func Encrypt(random io.Reader, pub *PublicKey, msg, curveOID, fingerprint []byte) (vsG, c []byte, err error) { + if len(msg) > 40 { + return nil, nil, errors.New("ecdh: message too long") + } + // the sender MAY use 21, 13, and 5 bytes of padding for AES-128, + // AES-192, and AES-256, respectively, to provide the same number of + // octets, 40 total, as an input to the key wrapping method. + padding := make([]byte, 40-len(msg)) + for i := range padding { + padding[i] = byte(40 - len(msg)) + } + m := append(msg, padding...) + + if pub.CurveType == ecc.Curve25519 { + return X25519Encrypt(random, pub, m, curveOID, fingerprint) + } + + d, x, y, err := elliptic.GenerateKey(pub.Curve, random) + if err != nil { + return nil, nil, err + } + + vsG = elliptic.Marshal(pub.Curve, x, y) + zbBig, _ := pub.Curve.ScalarMult(pub.X, pub.Y, d) + + byteLen := (pub.Curve.Params().BitSize + 7) >> 3 + zb := make([]byte, byteLen) + zbBytes := zbBig.Bytes() + copy(zb[byteLen-len(zbBytes):], zbBytes) + + z, err := buildKey(pub, zb, curveOID, fingerprint, false, false) + if err != nil { + return nil, nil, err + } + + if c, err = keywrap.Wrap(z, m); err != nil { + return nil, nil, err + } + + return vsG, c, nil + +} + +func Decrypt(priv *PrivateKey, vsG, m, curveOID, fingerprint []byte) (msg []byte, err error) { + if priv.PublicKey.CurveType == ecc.Curve25519 { + return X25519Decrypt(priv, vsG, m, curveOID, fingerprint) + } + x, y := elliptic.Unmarshal(priv.Curve, vsG) + zbBig, _ := priv.Curve.ScalarMult(x, y, priv.D) + + byteLen := (priv.Curve.Params().BitSize + 7) >> 3 + zb := make([]byte, byteLen) + zbBytes := zbBig.Bytes() + copy(zb[byteLen-len(zbBytes):], zbBytes) + + z, err := buildKey(&priv.PublicKey, zb, curveOID, fingerprint, false, false) + if err != nil { + return nil, err + } + + c, err := keywrap.Unwrap(z, m) + if err != nil { + return nil, err + } + + return c[:len(c)-int(c[len(c)-1])], nil +} + +func buildKey(pub *PublicKey, zb []byte, curveOID, fingerprint []byte, stripLeading, stripTrailing bool) ([]byte, error) { + // Param = curve_OID_len || curve_OID || public_key_alg_ID || 03 + // || 01 || KDF_hash_ID || KEK_alg_ID for AESKeyWrap + // || "Anonymous Sender " || recipient_fingerprint; + param := new(bytes.Buffer) + if _, err := param.Write(curveOID); err != nil { + return nil, err + } + algKDF := []byte{18, 3, 1, pub.KDF.Hash.Id(), pub.KDF.Cipher.Id()} + if _, err := param.Write(algKDF); err != nil { + return nil, err + } + if _, err := param.Write([]byte("Anonymous Sender ")); err != nil { + return nil, err + } + // For v5 keys, the 20 leftmost octets of the fingerprint are used. + if _, err := param.Write(fingerprint[:20]); err != nil { + return nil, err + } + if param.Len() - len(curveOID) != 45 { + return nil, errors.New("ecdh: malformed KDF Param") + } + + // MB = Hash ( 00 || 00 || 00 || 01 || ZB || Param ); + h := pub.KDF.Hash.New() + if _, err := h.Write([]byte{0x0, 0x0, 0x0, 0x1}); err != nil { + return nil, err + } + zbLen := len(zb) + i := 0 + j := zbLen - 1 + if stripLeading { + // Work around old go crypto bug where the leading zeros are missing. + for ; i < zbLen && zb[i] == 0; i++ {} + } + if stripTrailing { + // Work around old OpenPGP.js bug where insignificant trailing zeros in + // this little-endian number are missing. + // (See https://github.com/openpgpjs/openpgpjs/pull/853.) + for ; j >= 0 && zb[j] == 0; j-- {} + } + if _, err := h.Write(zb[i:j+1]); err != nil { + return nil, err + } + if _, err := h.Write(param.Bytes()); err != nil { + return nil, err + } + mb := h.Sum(nil) + + return mb[:pub.KDF.Cipher.KeySize()], nil // return oBits leftmost bits of MB. + +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/ecdh/x25519.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/ecdh/x25519.go new file mode 100644 index 0000000000000..15b41a31dff78 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/ecdh/x25519.go @@ -0,0 +1,157 @@ +// Copyright 2017 The 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 ecdh implements ECDH encryption, suitable for OpenPGP, +// as specified in RFC 6637, section 8. +package ecdh + +import ( + "errors" + "io" + "math/big" + + "github.com/ProtonMail/go-crypto/openpgp/aes/keywrap" + "github.com/ProtonMail/go-crypto/openpgp/internal/ecc" + "golang.org/x/crypto/curve25519" +) + +// Generates a private-public key-pair. +// 'priv' is a private key; a scalar belonging to the set +// 2^{254} + 8 * [0, 2^{251}), in order to avoid the small subgroup of the +// curve. 'pub' is simply 'priv' * G where G is the base point. +// See https://cr.yp.to/ecdh.html and RFC7748, sec 5. +func x25519GenerateKeyPairBytes(rand io.Reader) (priv [32]byte, pub [32]byte, err error) { + var n, helper = new(big.Int), new(big.Int) + n.SetUint64(1) + n.Lsh(n, 252) + helper.SetString("27742317777372353535851937790883648493", 10) + n.Add(n, helper) + + for true { + _, err = io.ReadFull(rand, priv[:]) + if err != nil { + return + } + // The following ensures that the private key is a number of the form + // 2^{254} + 8 * [0, 2^{251}), in order to avoid the small subgroup of + // of the curve. + priv[0] &= 248 + priv[31] &= 127 + priv[31] |= 64 + + // If the scalar is out of range, sample another random number. + if new(big.Int).SetBytes(priv[:]).Cmp(n) >= 0 { + continue + } + + curve25519.ScalarBaseMult(&pub, &priv) + return + } + return +} + +// X25519GenerateKey samples the key pair according to the correct distribution. +// It also sets the given key-derivation function and returns the *PrivateKey +// object along with an error. +func X25519GenerateKey(rand io.Reader, kdf KDF) (priv *PrivateKey, err error) { + ci := ecc.FindByName("Curve25519") + priv = new(PrivateKey) + priv.PublicKey.Curve = ci.Curve + d, pubKey, err := x25519GenerateKeyPairBytes(rand) + if err != nil { + return nil, err + } + priv.PublicKey.KDF = kdf + priv.D = make([]byte, 32) + copyReversed(priv.D, d[:]) + priv.PublicKey.CurveType = ci.CurveType + priv.PublicKey.Curve = ci.Curve + /* + * Note that ECPoint.point differs from the definition of public keys in + * [Curve25519] in two ways: (1) the byte-ordering is big-endian, which is + * more uniform with how big integers are represented in TLS, and (2) there + * is an additional length byte (so ECpoint.point is actually 33 bytes), + * again for uniformity (and extensibility). + */ + var encodedKey = make([]byte, 33) + encodedKey[0] = 0x40 + copy(encodedKey[1:], pubKey[:]) + priv.PublicKey.X = new(big.Int).SetBytes(encodedKey[:]) + priv.PublicKey.Y = new(big.Int) + return priv, nil +} + +func X25519Encrypt(random io.Reader, pub *PublicKey, msg, curveOID, fingerprint []byte) (vsG, c []byte, err error) { + d, ephemeralKey, err := x25519GenerateKeyPairBytes(random) + if err != nil { + return nil, nil, err + } + var pubKey [32]byte + + if pub.X.BitLen() > 33*264 { + return nil, nil, errors.New("ecdh: invalid key") + } + copy(pubKey[:], pub.X.Bytes()[1:]) + + var zb [32]byte + curve25519.ScalarBaseMult(&zb, &d) + curve25519.ScalarMult(&zb, &d, &pubKey) + z, err := buildKey(pub, zb[:], curveOID, fingerprint, false, false) + + if err != nil { + return nil, nil, err + } + + if c, err = keywrap.Wrap(z, msg); err != nil { + return nil, nil, err + } + + var vsg [33]byte + vsg[0] = 0x40 + copy(vsg[1:], ephemeralKey[:]) + + return vsg[:], c, nil +} + +func X25519Decrypt(priv *PrivateKey, vsG, m, curveOID, fingerprint []byte) (msg []byte, err error) { + var zb, d, ephemeralKey [32]byte + if len(vsG) != 33 || vsG[0] != 0x40 { + return nil, errors.New("ecdh: invalid key") + } + copy(ephemeralKey[:], vsG[1:33]) + + copyReversed(d[:], priv.D) + curve25519.ScalarBaseMult(&zb, &d) + curve25519.ScalarMult(&zb, &d, &ephemeralKey) + + var c []byte + + for i := 0; i < 3; i++ { + // Try buildKey three times for compat, see comments in buildKey. + z, err := buildKey(&priv.PublicKey, zb[:], curveOID, fingerprint, i == 1, i == 2) + if err != nil { + return nil, err + } + + res, err := keywrap.Unwrap(z, m) + if i == 2 && err != nil { + // Only return an error after we've tried all variants of buildKey. + return nil, err + } + + c = res + if err == nil { + break + } + } + + return c[:len(c)-int(c[len(c)-1])], nil +} + +func copyReversed(out []byte, in []byte) { + l := len(in) + for i := 0; i < l; i++ { + out[i] = in[l-i-1] + } +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/elgamal/elgamal.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/elgamal/elgamal.go new file mode 100644 index 0000000000000..6a07d8ff279a5 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/elgamal/elgamal.go @@ -0,0 +1,124 @@ +// Copyright 2011 The 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 elgamal implements ElGamal encryption, suitable for OpenPGP, +// as specified in "A Public-Key Cryptosystem and a Signature Scheme Based on +// Discrete Logarithms," IEEE Transactions on Information Theory, v. IT-31, +// n. 4, 1985, pp. 469-472. +// +// This form of ElGamal embeds PKCS#1 v1.5 padding, which may make it +// unsuitable for other protocols. RSA should be used in preference in any +// case. +package elgamal // import "github.com/ProtonMail/go-crypto/openpgp/elgamal" + +import ( + "crypto/rand" + "crypto/subtle" + "errors" + "io" + "math/big" +) + +// PublicKey represents an ElGamal public key. +type PublicKey struct { + G, P, Y *big.Int +} + +// PrivateKey represents an ElGamal private key. +type PrivateKey struct { + PublicKey + X *big.Int +} + +// Encrypt encrypts the given message to the given public key. The result is a +// pair of integers. Errors can result from reading random, or because msg is +// too large to be encrypted to the public key. +func Encrypt(random io.Reader, pub *PublicKey, msg []byte) (c1, c2 *big.Int, err error) { + pLen := (pub.P.BitLen() + 7) / 8 + if len(msg) > pLen-11 { + err = errors.New("elgamal: message too long") + return + } + + // EM = 0x02 || PS || 0x00 || M + em := make([]byte, pLen-1) + em[0] = 2 + ps, mm := em[1:len(em)-len(msg)-1], em[len(em)-len(msg):] + err = nonZeroRandomBytes(ps, random) + if err != nil { + return + } + em[len(em)-len(msg)-1] = 0 + copy(mm, msg) + + m := new(big.Int).SetBytes(em) + + k, err := rand.Int(random, pub.P) + if err != nil { + return + } + + c1 = new(big.Int).Exp(pub.G, k, pub.P) + s := new(big.Int).Exp(pub.Y, k, pub.P) + c2 = s.Mul(s, m) + c2.Mod(c2, pub.P) + + return +} + +// Decrypt takes two integers, resulting from an ElGamal encryption, and +// returns the plaintext of the message. An error can result only if the +// ciphertext is invalid. Users should keep in mind that this is a padding +// oracle and thus, if exposed to an adaptive chosen ciphertext attack, can +// be used to break the cryptosystem. See ``Chosen Ciphertext Attacks +// Against Protocols Based on the RSA Encryption Standard PKCS #1'', Daniel +// Bleichenbacher, Advances in Cryptology (Crypto '98), +func Decrypt(priv *PrivateKey, c1, c2 *big.Int) (msg []byte, err error) { + s := new(big.Int).Exp(c1, priv.X, priv.P) + if s.ModInverse(s, priv.P) == nil { + return nil, errors.New("elgamal: invalid private key") + } + s.Mul(s, c2) + s.Mod(s, priv.P) + em := s.Bytes() + + firstByteIsTwo := subtle.ConstantTimeByteEq(em[0], 2) + + // The remainder of the plaintext must be a string of non-zero random + // octets, followed by a 0, followed by the message. + // lookingForIndex: 1 iff we are still looking for the zero. + // index: the offset of the first zero byte. + var lookingForIndex, index int + lookingForIndex = 1 + + for i := 1; i < len(em); i++ { + equals0 := subtle.ConstantTimeByteEq(em[i], 0) + index = subtle.ConstantTimeSelect(lookingForIndex&equals0, i, index) + lookingForIndex = subtle.ConstantTimeSelect(equals0, 0, lookingForIndex) + } + + if firstByteIsTwo != 1 || lookingForIndex != 0 || index < 9 { + return nil, errors.New("elgamal: decryption error") + } + return em[index+1:], nil +} + +// nonZeroRandomBytes fills the given slice with non-zero random octets. +func nonZeroRandomBytes(s []byte, rand io.Reader) (err error) { + _, err = io.ReadFull(rand, s) + if err != nil { + return + } + + for i := 0; i < len(s); i++ { + for s[i] == 0 { + _, err = io.ReadFull(rand, s[i:i+1]) + if err != nil { + return + } + } + } + + return +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/errors/errors.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/errors/errors.go new file mode 100644 index 0000000000000..17e2bcfed20a0 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/errors/errors.go @@ -0,0 +1,116 @@ +// Copyright 2010 The 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 errors contains common error types for the OpenPGP packages. +package errors // import "github.com/ProtonMail/go-crypto/openpgp/errors" + +import ( + "strconv" +) + +// A StructuralError is returned when OpenPGP data is found to be syntactically +// invalid. +type StructuralError string + +func (s StructuralError) Error() string { + return "openpgp: invalid data: " + string(s) +} + +// UnsupportedError indicates that, although the OpenPGP data is valid, it +// makes use of currently unimplemented features. +type UnsupportedError string + +func (s UnsupportedError) Error() string { + return "openpgp: unsupported feature: " + string(s) +} + +// InvalidArgumentError indicates that the caller is in error and passed an +// incorrect value. +type InvalidArgumentError string + +func (i InvalidArgumentError) Error() string { + return "openpgp: invalid argument: " + string(i) +} + +// SignatureError indicates that a syntactically valid signature failed to +// validate. +type SignatureError string + +func (b SignatureError) Error() string { + return "openpgp: invalid signature: " + string(b) +} + +var ErrMDCHashMismatch error = SignatureError("MDC hash mismatch") +var ErrMDCMissing error = SignatureError("MDC packet not found") + +type signatureExpiredError int + +func (se signatureExpiredError) Error() string { + return "openpgp: signature expired" +} + +var ErrSignatureExpired error = signatureExpiredError(0) + +type keyExpiredError int + +func (ke keyExpiredError) Error() string { + return "openpgp: key expired" +} + +var ErrKeyExpired error = keyExpiredError(0) + +type keyIncorrectError int + +func (ki keyIncorrectError) Error() string { + return "openpgp: incorrect key" +} + +var ErrKeyIncorrect error = keyIncorrectError(0) + +// KeyInvalidError indicates that the public key parameters are invalid +// as they do not match the private ones +type KeyInvalidError string + +func (e KeyInvalidError) Error() string { + return "openpgp: invalid key: " + string(e) +} + +type unknownIssuerError int + +func (unknownIssuerError) Error() string { + return "openpgp: signature made by unknown entity" +} + +var ErrUnknownIssuer error = unknownIssuerError(0) + +type keyRevokedError int + +func (keyRevokedError) Error() string { + return "openpgp: signature made by revoked key" +} + +var ErrKeyRevoked error = keyRevokedError(0) + +type UnknownPacketTypeError uint8 + +func (upte UnknownPacketTypeError) Error() string { + return "openpgp: unknown packet type: " + strconv.Itoa(int(upte)) +} + +// AEADError indicates that there is a problem when initializing or using a +// AEAD instance, configuration struct, nonces or index values. +type AEADError string + +func (ae AEADError) Error() string { + return "openpgp: aead error: " + string(ae) +} + +// ErrDummyPrivateKey results when operations are attempted on a private key +// that is just a dummy key. See +// https://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=doc/DETAILS;h=fe55ae16ab4e26d8356dc574c9e8bc935e71aef1;hb=23191d7851eae2217ecdac6484349849a24fd94a#l1109 +type ErrDummyPrivateKey string + +func (dke ErrDummyPrivateKey) Error() string { + return "openpgp: s2k GNU dummy key: " + string(dke) +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/aead.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/aead.go new file mode 100644 index 0000000000000..17a1bfe9cb505 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/aead.go @@ -0,0 +1,65 @@ +// Copyright (C) 2019 ProtonTech AG + +package algorithm + +import ( + "crypto/cipher" + "github.com/ProtonMail/go-crypto/eax" + "github.com/ProtonMail/go-crypto/ocb" +) + +// AEADMode defines the Authenticated Encryption with Associated Data mode of +// operation. +type AEADMode uint8 + +// Supported modes of operation (see RFC4880bis [EAX] and RFC7253) +const ( + AEADModeEAX = AEADMode(1) + AEADModeOCB = AEADMode(2) + AEADModeGCM = AEADMode(100) +) + +// TagLength returns the length in bytes of authentication tags. +func (mode AEADMode) TagLength() int { + switch mode { + case AEADModeEAX: + return 16 + case AEADModeOCB: + return 16 + case AEADModeGCM: + return 16 + default: + return 0 + } +} + +// NonceLength returns the length in bytes of nonces. +func (mode AEADMode) NonceLength() int { + switch mode { + case AEADModeEAX: + return 16 + case AEADModeOCB: + return 15 + case AEADModeGCM: + return 12 + default: + return 0 + } +} + +// New returns a fresh instance of the given mode +func (mode AEADMode) New(block cipher.Block) (alg cipher.AEAD) { + var err error + switch mode { + case AEADModeEAX: + alg, err = eax.NewEAX(block) + case AEADModeOCB: + alg, err = ocb.NewOCB(block) + case AEADModeGCM: + alg, err = cipher.NewGCM(block) + } + if err != nil { + panic(err.Error()) + } + return alg +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/cipher.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/cipher.go new file mode 100644 index 0000000000000..5760cff80ea3f --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/cipher.go @@ -0,0 +1,107 @@ +// Copyright 2017 The 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 algorithm + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/des" + + "golang.org/x/crypto/cast5" +) + +// Cipher is an official symmetric key cipher algorithm. See RFC 4880, +// section 9.2. +type Cipher interface { + // Id returns the algorithm ID, as a byte, of the cipher. + Id() uint8 + // KeySize returns the key size, in bytes, of the cipher. + KeySize() int + // BlockSize returns the block size, in bytes, of the cipher. + BlockSize() int + // New returns a fresh instance of the given cipher. + New(key []byte) cipher.Block +} + +// The following constants mirror the OpenPGP standard (RFC 4880). +const ( + TripleDES = CipherFunction(2) + CAST5 = CipherFunction(3) + AES128 = CipherFunction(7) + AES192 = CipherFunction(8) + AES256 = CipherFunction(9) +) + +// CipherById represents the different block ciphers specified for OpenPGP. See +// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-13 +var CipherById = map[uint8]Cipher{ + TripleDES.Id(): TripleDES, + CAST5.Id(): CAST5, + AES128.Id(): AES128, + AES192.Id(): AES192, + AES256.Id(): AES256, +} + +type CipherFunction uint8 + +// ID returns the algorithm Id, as a byte, of cipher. +func (sk CipherFunction) Id() uint8 { + return uint8(sk) +} + +var keySizeByID = map[uint8]int{ + TripleDES.Id(): 24, + CAST5.Id(): cast5.KeySize, + AES128.Id(): 16, + AES192.Id(): 24, + AES256.Id(): 32, +} + +// KeySize returns the key size, in bytes, of cipher. +func (cipher CipherFunction) KeySize() int { + switch cipher { + case TripleDES: + return 24 + case CAST5: + return cast5.KeySize + case AES128: + return 16 + case AES192: + return 24 + case AES256: + return 32 + } + return 0 +} + +// BlockSize returns the block size, in bytes, of cipher. +func (cipher CipherFunction) BlockSize() int { + switch cipher { + case TripleDES: + return des.BlockSize + case CAST5: + return 8 + case AES128, AES192, AES256: + return 16 + } + return 0 +} + +// New returns a fresh instance of the given cipher. +func (cipher CipherFunction) New(key []byte) (block cipher.Block) { + var err error + switch cipher { + case TripleDES: + block, err = des.NewTripleDESCipher(key) + case CAST5: + block, err = cast5.NewCipher(key) + case AES128, AES192, AES256: + block, err = aes.NewCipher(key) + } + if err != nil { + panic(err.Error()) + } + return +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/hash.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/hash.go new file mode 100644 index 0000000000000..3f1b61b88e6cb --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/hash.go @@ -0,0 +1,86 @@ +// Copyright 2017 The 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 algorithm + +import ( + "crypto" + "fmt" + "hash" +) + +// Hash is an official hash function algorithm. See RFC 4880, section 9.4. +type Hash interface { + // Id returns the algorithm ID, as a byte, of Hash. + Id() uint8 + // Available reports whether the given hash function is linked into the binary. + Available() bool + // HashFunc simply returns the value of h so that Hash implements SignerOpts. + HashFunc() crypto.Hash + // New returns a new hash.Hash calculating the given hash function. New + // panics if the hash function is not linked into the binary. + New() hash.Hash + // Size returns the length, in bytes, of a digest resulting from the given + // hash function. It doesn't require that the hash function in question be + // linked into the program. + Size() int + // String is the name of the hash function corresponding to the given + // OpenPGP hash id. + String() string +} + +// The following vars mirror the crypto/Hash supported hash functions. +var ( + MD5 Hash = cryptoHash{1, crypto.MD5} + SHA1 Hash = cryptoHash{2, crypto.SHA1} + RIPEMD160 Hash = cryptoHash{3, crypto.RIPEMD160} + SHA256 Hash = cryptoHash{8, crypto.SHA256} + SHA384 Hash = cryptoHash{9, crypto.SHA384} + SHA512 Hash = cryptoHash{10, crypto.SHA512} + SHA224 Hash = cryptoHash{11, crypto.SHA224} +) + +// HashById represents the different hash functions specified for OpenPGP. See +// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-14 +var ( + HashById = map[uint8]Hash{ + MD5.Id(): MD5, + SHA1.Id(): SHA1, + RIPEMD160.Id(): RIPEMD160, + SHA256.Id(): SHA256, + SHA384.Id(): SHA384, + SHA512.Id(): SHA512, + SHA224.Id(): SHA224, + } +) + +// cryptoHash contains pairs relating OpenPGP's hash identifier with +// Go's crypto.Hash type. See RFC 4880, section 9.4. +type cryptoHash struct { + id uint8 + crypto.Hash +} + +// Id returns the algorithm ID, as a byte, of cryptoHash. +func (h cryptoHash) Id() uint8 { + return h.id +} + +var hashNames = map[uint8]string{ + MD5.Id(): "MD5", + SHA1.Id(): "SHA1", + RIPEMD160.Id(): "RIPEMD160", + SHA256.Id(): "SHA256", + SHA384.Id(): "SHA384", + SHA512.Id(): "SHA512", + SHA224.Id(): "SHA224", +} + +func (h cryptoHash) String() string { + s, ok := hashNames[h.id] + if !ok { + panic(fmt.Sprintf("Unsupported hash function %d", h.id)) + } + return s +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/curveInfo.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/curveInfo.go new file mode 100644 index 0000000000000..f91042fd85eec --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/curveInfo.go @@ -0,0 +1,118 @@ +package ecc + +import ( + "github.com/ProtonMail/go-crypto/openpgp/internal/encoding" + "crypto/elliptic" + "bytes" + "github.com/ProtonMail/go-crypto/bitcurves" + "github.com/ProtonMail/go-crypto/brainpool" +) + +type SignatureAlgorithm uint8 + +const ( + ECDSA SignatureAlgorithm = 1 + EdDSA SignatureAlgorithm = 2 +) + +type CurveInfo struct { + Name string + Oid *encoding.OID + Curve elliptic.Curve + SigAlgorithm SignatureAlgorithm + CurveType CurveType +} + +var curves = []CurveInfo{ + { + Name: "NIST curve P-256", + Oid: encoding.NewOID([]byte{0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07}), + Curve: elliptic.P256(), + CurveType: NISTCurve, + SigAlgorithm: ECDSA, + }, + { + Name: "NIST curve P-384", + Oid: encoding.NewOID([]byte{0x2B, 0x81, 0x04, 0x00, 0x22}), + Curve: elliptic.P384(), + CurveType: NISTCurve, + SigAlgorithm: ECDSA, + }, + { + Name: "NIST curve P-521", + Oid: encoding.NewOID([]byte{0x2B, 0x81, 0x04, 0x00, 0x23}), + Curve: elliptic.P521(), + CurveType: NISTCurve, + SigAlgorithm: ECDSA, + }, + { + Name: "SecP256k1", + Oid: encoding.NewOID([]byte{0x2B, 0x81, 0x04, 0x00, 0x0A}), + Curve: bitcurves.S256(), + CurveType: BitCurve, + SigAlgorithm: ECDSA, + }, + { + Name: "Curve25519", + Oid: encoding.NewOID([]byte{0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01}), + Curve: elliptic.P256(),// filler + CurveType: Curve25519, + SigAlgorithm: ECDSA, + }, + { + Name: "Ed25519", + Oid: encoding.NewOID([]byte{0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01}), + Curve: elliptic.P256(), // filler + CurveType: NISTCurve, + SigAlgorithm: EdDSA, + }, + { + Name: "Brainpool P256r1", + Oid: encoding.NewOID([]byte{0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07}), + Curve: brainpool.P256r1(), + CurveType: BrainpoolCurve, + SigAlgorithm: ECDSA, + }, + { + Name: "BrainpoolP384r1", + Oid: encoding.NewOID([]byte{0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B}), + Curve: brainpool.P384r1(), + CurveType: BrainpoolCurve, + SigAlgorithm: ECDSA, + }, + { + Name: "BrainpoolP512r1", + Oid: encoding.NewOID([]byte{0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D}), + Curve: brainpool.P512r1(), + CurveType: BrainpoolCurve, + SigAlgorithm: ECDSA, + }, +} + +func FindByCurve(curve elliptic.Curve) *CurveInfo { + for _, curveInfo := range curves { + if curveInfo.Curve == curve { + return &curveInfo + } + } + return nil +} + +func FindByOid(oid encoding.Field) *CurveInfo { + var rawBytes = oid.Bytes() + for _, curveInfo := range curves { + if bytes.Equal(curveInfo.Oid.Bytes(), rawBytes) { + return &curveInfo + } + } + return nil +} + +func FindByName(name string) *CurveInfo { + for _, curveInfo := range curves { + if curveInfo.Name == name { + return &curveInfo + } + } + return nil +} \ No newline at end of file diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/curveType.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/curveType.go new file mode 100644 index 0000000000000..de8bca0ac2348 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/curveType.go @@ -0,0 +1,10 @@ +package ecc + +type CurveType uint8 + +const ( + NISTCurve CurveType = 1 + Curve25519 CurveType = 2 + BitCurve CurveType = 3 + BrainpoolCurve CurveType = 4 +) \ No newline at end of file diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/encoding/encoding.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/encoding/encoding.go new file mode 100644 index 0000000000000..6c921481b7b24 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/encoding/encoding.go @@ -0,0 +1,27 @@ +// Copyright 2017 The 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 encoding implements openpgp packet field encodings as specified in +// RFC 4880 and 6637. +package encoding + +import "io" + +// Field is an encoded field of an openpgp packet. +type Field interface { + // Bytes returns the decoded data. + Bytes() []byte + + // BitLength is the size in bits of the decoded data. + BitLength() uint16 + + // EncodedBytes returns the encoded data. + EncodedBytes() []byte + + // EncodedLength is the size in bytes of the encoded data. + EncodedLength() uint16 + + // ReadFrom reads the next Field from r. + ReadFrom(r io.Reader) (int64, error) +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/encoding/mpi.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/encoding/mpi.go new file mode 100644 index 0000000000000..02e5e695c38ce --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/encoding/mpi.go @@ -0,0 +1,91 @@ +// Copyright 2017 The 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 encoding + +import ( + "io" + "math/big" + "math/bits" +) + +// An MPI is used to store the contents of a big integer, along with the bit +// length that was specified in the original input. This allows the MPI to be +// reserialized exactly. +type MPI struct { + bytes []byte + bitLength uint16 +} + +// NewMPI returns a MPI initialized with bytes. +func NewMPI(bytes []byte) *MPI { + for len(bytes) != 0 && bytes[0] == 0 { + bytes = bytes[1:] + } + if len(bytes) == 0 { + bitLength := uint16(0) + return &MPI{bytes, bitLength} + } + bitLength := 8*uint16(len(bytes)-1) + uint16(bits.Len8(bytes[0])) + return &MPI{bytes, bitLength} +} + +// Bytes returns the decoded data. +func (m *MPI) Bytes() []byte { + return m.bytes +} + +// BitLength is the size in bits of the decoded data. +func (m *MPI) BitLength() uint16 { + return m.bitLength +} + +// EncodedBytes returns the encoded data. +func (m *MPI) EncodedBytes() []byte { + return append([]byte{byte(m.bitLength >> 8), byte(m.bitLength)}, m.bytes...) +} + +// EncodedLength is the size in bytes of the encoded data. +func (m *MPI) EncodedLength() uint16 { + return uint16(2 + len(m.bytes)) +} + +// ReadFrom reads into m the next MPI from r. +func (m *MPI) ReadFrom(r io.Reader) (int64, error) { + var buf [2]byte + n, err := io.ReadFull(r, buf[0:]) + if err != nil { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + return int64(n), err + } + + m.bitLength = uint16(buf[0])<<8 | uint16(buf[1]) + m.bytes = make([]byte, (int(m.bitLength)+7)/8) + + nn, err := io.ReadFull(r, m.bytes) + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + + // remove leading zero bytes from malformed GnuPG encoded MPIs: + // https://bugs.gnupg.org/gnupg/issue1853 + // for _, b := range m.bytes { + // if b != 0 { + // break + // } + // m.bytes = m.bytes[1:] + // m.bitLength -= 8 + // } + + return int64(n) + int64(nn), err +} + +// SetBig initializes m with the bits from n. +func (m *MPI) SetBig(n *big.Int) *MPI { + m.bytes = n.Bytes() + m.bitLength = uint16(n.BitLen()) + return m +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/encoding/oid.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/encoding/oid.go new file mode 100644 index 0000000000000..ee39fd6bbb1ad --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/encoding/oid.go @@ -0,0 +1,88 @@ +// Copyright 2017 The 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 encoding + +import ( + "io" + + "github.com/ProtonMail/go-crypto/openpgp/errors" +) + +// OID is used to store a variable-length field with a one-octet size +// prefix. See https://tools.ietf.org/html/rfc6637#section-9. +type OID struct { + bytes []byte +} + +const ( + // maxOID is the maximum number of bytes in a OID. + maxOID = 254 + // reservedOIDLength1 and reservedOIDLength2 are OID lengths that the RFC + // specifies are reserved. + reservedOIDLength1 = 0 + reservedOIDLength2 = 0xff +) + +// NewOID returns a OID initialized with bytes. +func NewOID(bytes []byte) *OID { + switch len(bytes) { + case reservedOIDLength1, reservedOIDLength2: + panic("encoding: NewOID argument length is reserved") + default: + if len(bytes) > maxOID { + panic("encoding: NewOID argment too large") + } + } + + return &OID{ + bytes: bytes, + } +} + +// Bytes returns the decoded data. +func (o *OID) Bytes() []byte { + return o.bytes +} + +// BitLength is the size in bits of the decoded data. +func (o *OID) BitLength() uint16 { + return uint16(len(o.bytes) * 8) +} + +// EncodedBytes returns the encoded data. +func (o *OID) EncodedBytes() []byte { + return append([]byte{byte(len(o.bytes))}, o.bytes...) +} + +// EncodedLength is the size in bytes of the encoded data. +func (o *OID) EncodedLength() uint16 { + return uint16(1 + len(o.bytes)) +} + +// ReadFrom reads into b the next OID from r. +func (o *OID) ReadFrom(r io.Reader) (int64, error) { + var buf [1]byte + n, err := io.ReadFull(r, buf[:]) + if err != nil { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + return int64(n), err + } + + switch buf[0] { + case reservedOIDLength1, reservedOIDLength2: + return int64(n), errors.UnsupportedError("reserved for future extensions") + } + + o.bytes = make([]byte, buf[0]) + + nn, err := io.ReadFull(r, o.bytes) + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + + return int64(n) + int64(nn), err +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/key_generation.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/key_generation.go new file mode 100644 index 0000000000000..0d2eb45b8e124 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/key_generation.go @@ -0,0 +1,375 @@ +// Copyright 2011 The 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 openpgp + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" + goerrors "errors" + "io" + "math/big" + + "github.com/ProtonMail/go-crypto/openpgp/ecdh" + "github.com/ProtonMail/go-crypto/openpgp/errors" + "github.com/ProtonMail/go-crypto/openpgp/internal/algorithm" + "github.com/ProtonMail/go-crypto/openpgp/packet" + "golang.org/x/crypto/ed25519" +) + +// NewEntity returns an Entity that contains a fresh RSA/RSA keypair with a +// single identity composed of the given full name, comment and email, any of +// which may be empty but must not contain any of "()<>\x00". +// If config is nil, sensible defaults will be used. +func NewEntity(name, comment, email string, config *packet.Config) (*Entity, error) { + creationTime := config.Now() + keyLifetimeSecs := config.KeyLifetime() + + uid := packet.NewUserId(name, comment, email) + if uid == nil { + return nil, errors.InvalidArgumentError("user id field contained invalid characters") + } + + // Generate a primary signing key + primaryPrivRaw, err := newSigner(config) + if err != nil { + return nil, err + } + primary := packet.NewSignerPrivateKey(creationTime, primaryPrivRaw) + if config != nil && config.V5Keys { + primary.UpgradeToV5() + } + + isPrimaryId := true + selfSignature := &packet.Signature{ + Version: primary.PublicKey.Version, + SigType: packet.SigTypePositiveCert, + PubKeyAlgo: primary.PublicKey.PubKeyAlgo, + Hash: config.Hash(), + CreationTime: creationTime, + KeyLifetimeSecs: &keyLifetimeSecs, + IssuerKeyId: &primary.PublicKey.KeyId, + IssuerFingerprint: primary.PublicKey.Fingerprint, + IsPrimaryId: &isPrimaryId, + FlagsValid: true, + FlagSign: true, + FlagCertify: true, + MDC: true, // true by default, see 5.8 vs. 5.14 + AEAD: config.AEAD() != nil, + V5Keys: config != nil && config.V5Keys, + } + + // Set the PreferredHash for the SelfSignature from the packet.Config. + // If it is not the must-implement algorithm from rfc4880bis, append that. + selfSignature.PreferredHash = []uint8{hashToHashId(config.Hash())} + if config.Hash() != crypto.SHA256 { + selfSignature.PreferredHash = append(selfSignature.PreferredHash, hashToHashId(crypto.SHA256)) + } + + // Likewise for DefaultCipher. + selfSignature.PreferredSymmetric = []uint8{uint8(config.Cipher())} + if config.Cipher() != packet.CipherAES128 { + selfSignature.PreferredSymmetric = append(selfSignature.PreferredSymmetric, uint8(packet.CipherAES128)) + } + + // And for DefaultMode. + selfSignature.PreferredAEAD = []uint8{uint8(config.AEAD().Mode())} + if config.AEAD().Mode() != packet.AEADModeEAX { + selfSignature.PreferredAEAD = append(selfSignature.PreferredAEAD, uint8(packet.AEADModeEAX)) + } + + // User ID binding signature + err = selfSignature.SignUserId(uid.Id, &primary.PublicKey, primary, config) + if err != nil { + return nil, err + } + + // Generate an encryption subkey + subPrivRaw, err := newDecrypter(config) + if err != nil { + return nil, err + } + sub := packet.NewDecrypterPrivateKey(creationTime, subPrivRaw) + sub.IsSubkey = true + sub.PublicKey.IsSubkey = true + if config != nil && config.V5Keys { + sub.UpgradeToV5() + } + + // NOTE: No KeyLifetimeSecs here, but we will not return this subkey in EncryptionKey() + // if the primary/master key has expired. + subKey := Subkey{ + PublicKey: &sub.PublicKey, + PrivateKey: sub, + Sig: &packet.Signature{ + Version: primary.PublicKey.Version, + CreationTime: creationTime, + SigType: packet.SigTypeSubkeyBinding, + PubKeyAlgo: primary.PublicKey.PubKeyAlgo, + Hash: config.Hash(), + FlagsValid: true, + FlagEncryptStorage: true, + FlagEncryptCommunications: true, + IssuerKeyId: &primary.PublicKey.KeyId, + }, + } + + // Subkey binding signature + err = subKey.Sig.SignKey(subKey.PublicKey, primary, config) + if err != nil { + return nil, err + } + + return &Entity{ + PrimaryKey: &primary.PublicKey, + PrivateKey: primary, + Identities: map[string]*Identity{ + uid.Id: &Identity{ + Name: uid.Id, + UserId: uid, + SelfSignature: selfSignature, + Signatures: []*packet.Signature{selfSignature}, + }, + }, + Subkeys: []Subkey{subKey}, + }, nil +} + +// AddSigningSubkey adds a signing keypair as a subkey to the Entity. +// If config is nil, sensible defaults will be used. +func (e *Entity) AddSigningSubkey(config *packet.Config) error { + creationTime := config.Now() + keyLifetimeSecs := config.KeyLifetime() + + subPrivRaw, err := newSigner(config) + if err != nil { + return err + } + sub := packet.NewSignerPrivateKey(creationTime, subPrivRaw) + + subkey := Subkey{ + PublicKey: &sub.PublicKey, + PrivateKey: sub, + Sig: &packet.Signature{ + Version: e.PrimaryKey.Version, + CreationTime: creationTime, + KeyLifetimeSecs: &keyLifetimeSecs, + SigType: packet.SigTypeSubkeyBinding, + PubKeyAlgo: e.PrimaryKey.PubKeyAlgo, + Hash: config.Hash(), + FlagsValid: true, + FlagSign: true, + IssuerKeyId: &e.PrimaryKey.KeyId, + EmbeddedSignature: &packet.Signature{ + Version: e.PrimaryKey.Version, + CreationTime: creationTime, + SigType: packet.SigTypePrimaryKeyBinding, + PubKeyAlgo: sub.PublicKey.PubKeyAlgo, + Hash: config.Hash(), + IssuerKeyId: &e.PrimaryKey.KeyId, + }, + }, + } + if config != nil && config.V5Keys { + subkey.PublicKey.UpgradeToV5() + } + + err = subkey.Sig.EmbeddedSignature.CrossSignKey(subkey.PublicKey, e.PrimaryKey, subkey.PrivateKey, config) + if err != nil { + return err + } + + subkey.PublicKey.IsSubkey = true + subkey.PrivateKey.IsSubkey = true + if err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config); err != nil { + return err + } + + e.Subkeys = append(e.Subkeys, subkey) + return nil +} + +// AddEncryptionSubkey adds an encryption keypair as a subkey to the Entity. +// If config is nil, sensible defaults will be used. +func (e *Entity) AddEncryptionSubkey(config *packet.Config) error { + creationTime := config.Now() + keyLifetimeSecs := config.KeyLifetime() + + subPrivRaw, err := newDecrypter(config) + if err != nil { + return err + } + sub := packet.NewDecrypterPrivateKey(creationTime, subPrivRaw) + + subkey := Subkey{ + PublicKey: &sub.PublicKey, + PrivateKey: sub, + Sig: &packet.Signature{ + Version: e.PrimaryKey.Version, + CreationTime: creationTime, + KeyLifetimeSecs: &keyLifetimeSecs, + SigType: packet.SigTypeSubkeyBinding, + PubKeyAlgo: e.PrimaryKey.PubKeyAlgo, + Hash: config.Hash(), + FlagsValid: true, + FlagEncryptStorage: true, + FlagEncryptCommunications: true, + IssuerKeyId: &e.PrimaryKey.KeyId, + }, + } + if config != nil && config.V5Keys { + subkey.PublicKey.UpgradeToV5() + } + + subkey.PublicKey.IsSubkey = true + subkey.PrivateKey.IsSubkey = true + if err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config); err != nil { + return err + } + + e.Subkeys = append(e.Subkeys, subkey) + return nil +} + +// Generates a signing key +func newSigner(config *packet.Config) (signer crypto.Signer, err error) { + switch config.PublicKeyAlgorithm() { + case packet.PubKeyAlgoRSA: + bits := config.RSAModulusBits() + if bits < 1024 { + return nil, errors.InvalidArgumentError("bits must be >= 1024") + } + if config != nil && len(config.RSAPrimes) >= 2 { + primes := config.RSAPrimes[0:2] + config.RSAPrimes = config.RSAPrimes[2:] + return generateRSAKeyWithPrimes(config.Random(), 2, bits, primes) + } + return rsa.GenerateKey(config.Random(), bits) + case packet.PubKeyAlgoEdDSA: + _, priv, err := ed25519.GenerateKey(config.Random()) + if err != nil { + return nil, err + } + return &priv, nil + default: + return nil, errors.InvalidArgumentError("unsupported public key algorithm") + } +} + +// Generates an encryption/decryption key +func newDecrypter(config *packet.Config) (decrypter interface{}, err error) { + switch config.PublicKeyAlgorithm() { + case packet.PubKeyAlgoRSA: + bits := config.RSAModulusBits() + if bits < 1024 { + return nil, errors.InvalidArgumentError("bits must be >= 1024") + } + if config != nil && len(config.RSAPrimes) >= 2 { + primes := config.RSAPrimes[0:2] + config.RSAPrimes = config.RSAPrimes[2:] + return generateRSAKeyWithPrimes(config.Random(), 2, bits, primes) + } + return rsa.GenerateKey(config.Random(), bits) + case packet.PubKeyAlgoEdDSA: + fallthrough // When passing EdDSA, we generate an ECDH subkey + case packet.PubKeyAlgoECDH: + var kdf = ecdh.KDF{ + Hash: algorithm.SHA512, + Cipher: algorithm.AES256, + } + return ecdh.X25519GenerateKey(config.Random(), kdf) + default: + return nil, errors.InvalidArgumentError("unsupported public key algorithm") + } +} + +var bigOne = big.NewInt(1) + +// generateRSAKeyWithPrimes generates a multi-prime RSA keypair of the +// given bit size, using the given random source and prepopulated primes. +func generateRSAKeyWithPrimes(random io.Reader, nprimes int, bits int, prepopulatedPrimes []*big.Int) (*rsa.PrivateKey, error) { + priv := new(rsa.PrivateKey) + priv.E = 65537 + + if nprimes < 2 { + return nil, goerrors.New("generateRSAKeyWithPrimes: nprimes must be >= 2") + } + + if bits < 1024 { + return nil, goerrors.New("generateRSAKeyWithPrimes: bits must be >= 1024") + } + + primes := make([]*big.Int, nprimes) + +NextSetOfPrimes: + for { + todo := bits + // crypto/rand should set the top two bits in each prime. + // Thus each prime has the form + // p_i = 2^bitlen(p_i) × 0.11... (in base 2). + // And the product is: + // P = 2^todo × α + // where α is the product of nprimes numbers of the form 0.11... + // + // If α < 1/2 (which can happen for nprimes > 2), we need to + // shift todo to compensate for lost bits: the mean value of 0.11... + // is 7/8, so todo + shift - nprimes * log2(7/8) ~= bits - 1/2 + // will give good results. + if nprimes >= 7 { + todo += (nprimes - 2) / 5 + } + for i := 0; i < nprimes; i++ { + var err error + if len(prepopulatedPrimes) == 0 { + primes[i], err = rand.Prime(random, todo/(nprimes-i)) + if err != nil { + return nil, err + } + } else { + primes[i] = prepopulatedPrimes[0] + prepopulatedPrimes = prepopulatedPrimes[1:] + } + + todo -= primes[i].BitLen() + } + + // Make sure that primes is pairwise unequal. + for i, prime := range primes { + for j := 0; j < i; j++ { + if prime.Cmp(primes[j]) == 0 { + continue NextSetOfPrimes + } + } + } + + n := new(big.Int).Set(bigOne) + totient := new(big.Int).Set(bigOne) + pminus1 := new(big.Int) + for _, prime := range primes { + n.Mul(n, prime) + pminus1.Sub(prime, bigOne) + totient.Mul(totient, pminus1) + } + if n.BitLen() != bits { + // This should never happen for nprimes == 2 because + // crypto/rand should set the top two bits in each prime. + // For nprimes > 2 we hope it does not happen often. + continue NextSetOfPrimes + } + + priv.D = new(big.Int) + e := big.NewInt(int64(priv.E)) + ok := priv.D.ModInverse(e, totient) + + if ok != nil { + priv.Primes = primes + priv.N = n + break + } + } + + priv.Precompute() + return priv, nil +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/keys.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/keys.go new file mode 100644 index 0000000000000..c3fedf7a2506d --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/keys.go @@ -0,0 +1,707 @@ +// Copyright 2011 The 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 openpgp + +import ( + goerrors "errors" + "io" + "time" + + "github.com/ProtonMail/go-crypto/openpgp/armor" + "github.com/ProtonMail/go-crypto/openpgp/errors" + "github.com/ProtonMail/go-crypto/openpgp/packet" +) + +// PublicKeyType is the armor type for a PGP public key. +var PublicKeyType = "PGP PUBLIC KEY BLOCK" + +// PrivateKeyType is the armor type for a PGP private key. +var PrivateKeyType = "PGP PRIVATE KEY BLOCK" + +// An Entity represents the components of an OpenPGP key: a primary public key +// (which must be a signing key), one or more identities claimed by that key, +// and zero or more subkeys, which may be encryption keys. +type Entity struct { + PrimaryKey *packet.PublicKey + PrivateKey *packet.PrivateKey + Identities map[string]*Identity // indexed by Identity.Name + Revocations []*packet.Signature + Subkeys []Subkey +} + +// An Identity represents an identity claimed by an Entity and zero or more +// assertions by other entities about that claim. +type Identity struct { + Name string // by convention, has the form "Full Name (comment) " + UserId *packet.UserId + SelfSignature *packet.Signature + Signatures []*packet.Signature +} + +// A Subkey is an additional public key in an Entity. Subkeys can be used for +// encryption. +type Subkey struct { + PublicKey *packet.PublicKey + PrivateKey *packet.PrivateKey + Sig *packet.Signature +} + +// A Key identifies a specific public key in an Entity. This is either the +// Entity's primary key or a subkey. +type Key struct { + Entity *Entity + PublicKey *packet.PublicKey + PrivateKey *packet.PrivateKey + SelfSignature *packet.Signature +} + +// A KeyRing provides access to public and private keys. +type KeyRing interface { + // KeysById returns the set of keys that have the given key id. + KeysById(id uint64) []Key + // KeysByIdAndUsage returns the set of keys with the given id + // that also meet the key usage given by requiredUsage. + // The requiredUsage is expressed as the bitwise-OR of + // packet.KeyFlag* values. + KeysByIdUsage(id uint64, requiredUsage byte) []Key + // DecryptionKeys returns all private keys that are valid for + // decryption. + DecryptionKeys() []Key +} + +// PrimaryIdentity returns the Identity marked as primary or the first identity +// if none are so marked. +func (e *Entity) PrimaryIdentity() *Identity { + var firstIdentity *Identity + for _, ident := range e.Identities { + if firstIdentity == nil { + firstIdentity = ident + } + if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId { + return ident + } + } + return firstIdentity +} + +// EncryptionKey returns the best candidate Key for encrypting a message to the +// given Entity. +func (e *Entity) EncryptionKey(now time.Time) (Key, bool) { + // Fail to find any encryption key if the primary key has expired. + i := e.PrimaryIdentity() + primaryKeyExpired := e.PrimaryKey.KeyExpired(i.SelfSignature, now) + if primaryKeyExpired { + return Key{}, false + } + + // Iterate the keys to find the newest, unexpired one + candidateSubkey := -1 + var maxTime time.Time + for i, subkey := range e.Subkeys { + if subkey.Sig.FlagsValid && + subkey.Sig.FlagEncryptCommunications && + subkey.PublicKey.PubKeyAlgo.CanEncrypt() && + !subkey.PublicKey.KeyExpired(subkey.Sig, now) && + (maxTime.IsZero() || subkey.Sig.CreationTime.After(maxTime)) { + candidateSubkey = i + maxTime = subkey.Sig.CreationTime + } + } + + if candidateSubkey != -1 { + subkey := e.Subkeys[candidateSubkey] + return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig}, true + } + + // If we don't have any candidate subkeys for encryption and + // the primary key doesn't have any usage metadata then we + // assume that the primary key is ok. Or, if the primary key is + // marked as ok to encrypt with, then we can obviously use it. + // Also, check expiry again just to be safe. + if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagEncryptCommunications && + e.PrimaryKey.PubKeyAlgo.CanEncrypt() && !primaryKeyExpired { + return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}, true + } + + return Key{}, false +} + +// SigningKey return the best candidate Key for signing a message with this +// Entity. +func (e *Entity) SigningKey(now time.Time) (Key, bool) { + return e.SigningKeyById(now, 0) +} + +// SigningKeyById return the Key for signing a message with this +// Entity and keyID. +func (e *Entity) SigningKeyById(now time.Time, id uint64) (Key, bool) { + // Fail to find any signing key if the primary key has expired. + i := e.PrimaryIdentity() + primaryKeyExpired := e.PrimaryKey.KeyExpired(i.SelfSignature, now) + if primaryKeyExpired { + return Key{}, false + } + + // Iterate the keys to find the newest, unexpired one + candidateSubkey := -1 + var maxTime time.Time + for idx, subkey := range e.Subkeys { + if subkey.Sig.FlagsValid && + subkey.Sig.FlagSign && + subkey.PublicKey.PubKeyAlgo.CanSign() && + !subkey.PublicKey.KeyExpired(subkey.Sig, now) && + (maxTime.IsZero() || subkey.Sig.CreationTime.After(maxTime)) && + (id == 0 || subkey.PrivateKey.KeyId == id) { + candidateSubkey = idx + maxTime = subkey.Sig.CreationTime + } + } + + if candidateSubkey != -1 { + subkey := e.Subkeys[candidateSubkey] + return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig}, true + } + + // If we have no candidate subkey then we assume that it's ok to sign + // with the primary key. Or, if the primary key is marked as ok to + // sign with, then we can use it. Also, check expiry again just to be safe. + if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagSign && + e.PrimaryKey.PubKeyAlgo.CanSign() && !primaryKeyExpired && + (id == 0 || e.PrivateKey.KeyId == id) { + return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}, true + } + + // No keys with a valid Signing Flag or no keys matched the id passed in + return Key{}, false +} + +// An EntityList contains one or more Entities. +type EntityList []*Entity + +// KeysById returns the set of keys that have the given key id. +func (el EntityList) KeysById(id uint64) (keys []Key) { + for _, e := range el { + if e.PrimaryKey.KeyId == id { + var selfSig *packet.Signature + for _, ident := range e.Identities { + if selfSig == nil { + selfSig = ident.SelfSignature + } else if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId { + selfSig = ident.SelfSignature + break + } + } + keys = append(keys, Key{e, e.PrimaryKey, e.PrivateKey, selfSig}) + } + + for _, subKey := range e.Subkeys { + if subKey.PublicKey.KeyId == id { + keys = append(keys, Key{e, subKey.PublicKey, subKey.PrivateKey, subKey.Sig}) + } + } + } + return +} + +// KeysByIdAndUsage returns the set of keys with the given id that also meet +// the key usage given by requiredUsage. The requiredUsage is expressed as +// the bitwise-OR of packet.KeyFlag* values. +func (el EntityList) KeysByIdUsage(id uint64, requiredUsage byte) (keys []Key) { + for _, key := range el.KeysById(id) { + if len(key.Entity.Revocations) > 0 { + continue + } + + if key.SelfSignature.RevocationReason != nil { + continue + } + + if key.SelfSignature.FlagsValid && requiredUsage != 0 { + var usage byte + if key.SelfSignature.FlagCertify { + usage |= packet.KeyFlagCertify + } + if key.SelfSignature.FlagSign { + usage |= packet.KeyFlagSign + } + if key.SelfSignature.FlagEncryptCommunications { + usage |= packet.KeyFlagEncryptCommunications + } + if key.SelfSignature.FlagEncryptStorage { + usage |= packet.KeyFlagEncryptStorage + } + if usage&requiredUsage != requiredUsage { + continue + } + } + + keys = append(keys, key) + } + return +} + +// DecryptionKeys returns all private keys that are valid for decryption. +func (el EntityList) DecryptionKeys() (keys []Key) { + for _, e := range el { + for _, subKey := range e.Subkeys { + if subKey.PrivateKey != nil && (!subKey.Sig.FlagsValid || subKey.Sig.FlagEncryptStorage || subKey.Sig.FlagEncryptCommunications) { + keys = append(keys, Key{e, subKey.PublicKey, subKey.PrivateKey, subKey.Sig}) + } + } + } + return +} + +// ReadArmoredKeyRing reads one or more public/private keys from an armor keyring file. +func ReadArmoredKeyRing(r io.Reader) (EntityList, error) { + block, err := armor.Decode(r) + if err == io.EOF { + return nil, errors.InvalidArgumentError("no armored data found") + } + if err != nil { + return nil, err + } + if block.Type != PublicKeyType && block.Type != PrivateKeyType { + return nil, errors.InvalidArgumentError("expected public or private key block, got: " + block.Type) + } + + return ReadKeyRing(block.Body) +} + +// ReadKeyRing reads one or more public/private keys. Unsupported keys are +// ignored as long as at least a single valid key is found. +func ReadKeyRing(r io.Reader) (el EntityList, err error) { + packets := packet.NewReader(r) + var lastUnsupportedError error + + for { + var e *Entity + e, err = ReadEntity(packets) + if err != nil { + // TODO: warn about skipped unsupported/unreadable keys + if _, ok := err.(errors.UnsupportedError); ok { + lastUnsupportedError = err + err = readToNextPublicKey(packets) + } else if _, ok := err.(errors.StructuralError); ok { + // Skip unreadable, badly-formatted keys + lastUnsupportedError = err + err = readToNextPublicKey(packets) + } + if err == io.EOF { + err = nil + break + } + if err != nil { + el = nil + break + } + } else { + el = append(el, e) + } + } + + if len(el) == 0 && err == nil { + err = lastUnsupportedError + } + return +} + +// readToNextPublicKey reads packets until the start of the entity and leaves +// the first packet of the new entity in the Reader. +func readToNextPublicKey(packets *packet.Reader) (err error) { + var p packet.Packet + for { + p, err = packets.Next() + if err == io.EOF { + return + } else if err != nil { + if _, ok := err.(errors.UnsupportedError); ok { + err = nil + continue + } + return + } + + if pk, ok := p.(*packet.PublicKey); ok && !pk.IsSubkey { + packets.Unread(p) + return + } + } +} + +// ReadEntity reads an entity (public key, identities, subkeys etc) from the +// given Reader. +func ReadEntity(packets *packet.Reader) (*Entity, error) { + e := new(Entity) + e.Identities = make(map[string]*Identity) + + p, err := packets.Next() + if err != nil { + return nil, err + } + + var ok bool + if e.PrimaryKey, ok = p.(*packet.PublicKey); !ok { + if e.PrivateKey, ok = p.(*packet.PrivateKey); !ok { + packets.Unread(p) + return nil, errors.StructuralError("first packet was not a public/private key") + } + e.PrimaryKey = &e.PrivateKey.PublicKey + } + + if !e.PrimaryKey.PubKeyAlgo.CanSign() { + return nil, errors.StructuralError("primary key cannot be used for signatures") + } + + var revocations []*packet.Signature +EachPacket: + for { + p, err := packets.Next() + if err == io.EOF { + break + } else if err != nil { + return nil, err + } + + switch pkt := p.(type) { + case *packet.UserId: + if err := addUserID(e, packets, pkt); err != nil { + return nil, err + } + case *packet.Signature: + if pkt.SigType == packet.SigTypeKeyRevocation { + revocations = append(revocations, pkt) + } else if pkt.SigType == packet.SigTypeDirectSignature { + // TODO: RFC4880 5.2.1 permits signatures + // directly on keys (eg. to bind additional + // revocation keys). + } + // Else, ignoring the signature as it does not follow anything + // we would know to attach it to. + case *packet.PrivateKey: + if pkt.IsSubkey == false { + packets.Unread(p) + break EachPacket + } + err = addSubkey(e, packets, &pkt.PublicKey, pkt) + if err != nil { + return nil, err + } + case *packet.PublicKey: + if pkt.IsSubkey == false { + packets.Unread(p) + break EachPacket + } + err = addSubkey(e, packets, pkt, nil) + if err != nil { + return nil, err + } + default: + // we ignore unknown packets + } + } + + if len(e.Identities) == 0 { + return nil, errors.StructuralError("entity without any identities") + } + + for _, revocation := range revocations { + err = e.PrimaryKey.VerifyRevocationSignature(revocation) + if err == nil { + e.Revocations = append(e.Revocations, revocation) + } else { + // TODO: RFC 4880 5.2.3.15 defines revocation keys. + return nil, errors.StructuralError("revocation signature signed by alternate key") + } + } + + return e, nil +} + +func addUserID(e *Entity, packets *packet.Reader, pkt *packet.UserId) error { + // Make a new Identity object, that we might wind up throwing away. + // We'll only add it if we get a valid self-signature over this + // userID. + identity := new(Identity) + identity.Name = pkt.Id + identity.UserId = pkt + + for { + p, err := packets.Next() + if err == io.EOF { + break + } else if err != nil { + return err + } + + sig, ok := p.(*packet.Signature) + if !ok { + packets.Unread(p) + break + } + + if (sig.SigType == packet.SigTypePositiveCert || sig.SigType == packet.SigTypeGenericCert) && sig.CheckKeyIdOrFingerprint(e.PrimaryKey) { + if err = e.PrimaryKey.VerifyUserIdSignature(pkt.Id, e.PrimaryKey, sig); err != nil { + return errors.StructuralError("user ID self-signature invalid: " + err.Error()) + } + if identity.SelfSignature == nil || sig.CreationTime.After(identity.SelfSignature.CreationTime) { + identity.SelfSignature = sig + } + identity.Signatures = append(identity.Signatures, sig) + e.Identities[pkt.Id] = identity + } else { + identity.Signatures = append(identity.Signatures, sig) + } + } + + return nil +} + +func addSubkey(e *Entity, packets *packet.Reader, pub *packet.PublicKey, priv *packet.PrivateKey) error { + var subKey Subkey + subKey.PublicKey = pub + subKey.PrivateKey = priv + + for { + p, err := packets.Next() + if err == io.EOF { + break + } else if err != nil { + return errors.StructuralError("subkey signature invalid: " + err.Error()) + } + + sig, ok := p.(*packet.Signature) + if !ok { + packets.Unread(p) + break + } + + if sig.SigType != packet.SigTypeSubkeyBinding && sig.SigType != packet.SigTypeSubkeyRevocation { + return errors.StructuralError("subkey signature with wrong type") + } + + if err := e.PrimaryKey.VerifyKeySignature(subKey.PublicKey, sig); err != nil { + return errors.StructuralError("subkey signature invalid: " + err.Error()) + } + + switch sig.SigType { + case packet.SigTypeSubkeyRevocation: + subKey.Sig = sig + case packet.SigTypeSubkeyBinding: + if shouldReplaceSubkeySig(subKey.Sig, sig) { + subKey.Sig = sig + } + } + } + + if subKey.Sig == nil { + return errors.StructuralError("subkey packet not followed by signature") + } + + e.Subkeys = append(e.Subkeys, subKey) + + return nil +} + +func shouldReplaceSubkeySig(existingSig, potentialNewSig *packet.Signature) bool { + if potentialNewSig == nil { + return false + } + + if existingSig == nil { + return true + } + + if existingSig.SigType == packet.SigTypeSubkeyRevocation { + return false // never override a revocation signature + } + + return potentialNewSig.CreationTime.After(existingSig.CreationTime) +} + +// SerializePrivate serializes an Entity, including private key material, but +// excluding signatures from other entities, to the given Writer. +// Identities and subkeys are re-signed in case they changed since NewEntry. +// If config is nil, sensible defaults will be used. +func (e *Entity) SerializePrivate(w io.Writer, config *packet.Config) (err error) { + if e.PrivateKey.Dummy() { + return errors.ErrDummyPrivateKey("dummy private key cannot re-sign identities") + } + return e.serializePrivate(w, config, true) +} + +// SerializePrivateWithoutSigning serializes an Entity, including private key +// material, but excluding signatures from other entities, to the given Writer. +// Self-signatures of identities and subkeys are not re-signed. This is useful +// when serializing GNU dummy keys, among other things. +// If config is nil, sensible defaults will be used. +func (e *Entity) SerializePrivateWithoutSigning(w io.Writer, config *packet.Config) (err error) { + return e.serializePrivate(w, config, false) +} + +func (e *Entity) serializePrivate(w io.Writer, config *packet.Config, reSign bool) (err error) { + if e.PrivateKey == nil { + return goerrors.New("openpgp: private key is missing") + } + err = e.PrivateKey.Serialize(w) + if err != nil { + return + } + for _, ident := range e.Identities { + err = ident.UserId.Serialize(w) + if err != nil { + return + } + if reSign { + err = ident.SelfSignature.SignUserId(ident.UserId.Id, e.PrimaryKey, e.PrivateKey, config) + if err != nil { + return + } + } + err = ident.SelfSignature.Serialize(w) + if err != nil { + return + } + } + for _, subkey := range e.Subkeys { + err = subkey.PrivateKey.Serialize(w) + if err != nil { + return + } + if reSign { + err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config) + if err != nil { + return + } + if subkey.Sig.EmbeddedSignature != nil { + err = subkey.Sig.EmbeddedSignature.CrossSignKey(subkey.PublicKey, e.PrimaryKey, + subkey.PrivateKey, config) + if err != nil { + return + } + } + } + err = subkey.Sig.Serialize(w) + if err != nil { + return + } + } + return nil +} + +// Serialize writes the public part of the given Entity to w, including +// signatures from other entities. No private key material will be output. +func (e *Entity) Serialize(w io.Writer) error { + err := e.PrimaryKey.Serialize(w) + if err != nil { + return err + } + for _, ident := range e.Identities { + err = ident.UserId.Serialize(w) + if err != nil { + return err + } + for _, sig := range ident.Signatures { + err = sig.Serialize(w) + if err != nil { + return err + } + } + } + for _, subkey := range e.Subkeys { + err = subkey.PublicKey.Serialize(w) + if err != nil { + return err + } + err = subkey.Sig.Serialize(w) + if err != nil { + return err + } + } + return nil +} + +// SignIdentity adds a signature to e, from signer, attesting that identity is +// associated with e. The provided identity must already be an element of +// e.Identities and the private key of signer must have been decrypted if +// necessary. +// If config is nil, sensible defaults will be used. +func (e *Entity) SignIdentity(identity string, signer *Entity, config *packet.Config) error { + if signer.PrivateKey == nil { + return errors.InvalidArgumentError("signing Entity must have a private key") + } + if signer.PrivateKey.Encrypted { + return errors.InvalidArgumentError("signing Entity's private key must be decrypted") + } + ident, ok := e.Identities[identity] + if !ok { + return errors.InvalidArgumentError("given identity string not found in Entity") + } + + sig := &packet.Signature{ + Version: signer.PrivateKey.Version, + SigType: packet.SigTypeGenericCert, + PubKeyAlgo: signer.PrivateKey.PubKeyAlgo, + Hash: config.Hash(), + CreationTime: config.Now(), + IssuerKeyId: &signer.PrivateKey.KeyId, + } + if err := sig.SignUserId(identity, e.PrimaryKey, signer.PrivateKey, config); err != nil { + return err + } + ident.Signatures = append(ident.Signatures, sig) + return nil +} + +// RevokeKey generates a key revocation signature (packet.SigTypeKeyRevocation) with the +// specified reason code and text (RFC4880 section-5.2.3.23). +// If config is nil, sensible defaults will be used. +func (e *Entity) RevokeKey(reason packet.ReasonForRevocation, reasonText string, config *packet.Config) error { + reasonCode := uint8(reason) + revSig := &packet.Signature{ + Version: e.PrimaryKey.Version, + CreationTime: config.Now(), + SigType: packet.SigTypeKeyRevocation, + PubKeyAlgo: packet.PubKeyAlgoRSA, + Hash: config.Hash(), + RevocationReason: &reasonCode, + RevocationReasonText: reasonText, + IssuerKeyId: &e.PrimaryKey.KeyId, + } + + if err := revSig.RevokeKey(e.PrimaryKey, e.PrivateKey, config); err != nil { + return err + } + e.Revocations = append(e.Revocations, revSig) + return nil +} + +// RevokeSubkey generates a subkey revocation signature (packet.SigTypeSubkeyRevocation) for +// a subkey with the specified reason code and text (RFC4880 section-5.2.3.23). +// If config is nil, sensible defaults will be used. +func (e *Entity) RevokeSubkey(sk *Subkey, reason packet.ReasonForRevocation, reasonText string, config *packet.Config) error { + if err := e.PrimaryKey.VerifyKeySignature(sk.PublicKey, sk.Sig); err != nil { + return errors.InvalidArgumentError("given subkey is not associated with this key") + } + + reasonCode := uint8(reason) + revSig := &packet.Signature{ + Version: e.PrimaryKey.Version, + CreationTime: config.Now(), + SigType: packet.SigTypeSubkeyRevocation, + PubKeyAlgo: packet.PubKeyAlgoRSA, + Hash: config.Hash(), + RevocationReason: &reasonCode, + RevocationReasonText: reasonText, + IssuerKeyId: &e.PrimaryKey.KeyId, + } + + if err := revSig.RevokeKey(sk.PublicKey, e.PrivateKey, config); err != nil { + return err + } + + sk.Sig = revSig + return nil +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/keys_test_data.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/keys_test_data.go new file mode 100644 index 0000000000000..21d17c2f8bec4 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/keys_test_data.go @@ -0,0 +1,336 @@ +package openpgp + +const expiringKeyHex = "c6c04d0451d0c680010800abbb021fd03ffc4e96618901180c3fdcb060ee69eeead97b91256d11420d80b5f1b51930248044130bd300605cf8a05b7a40d3d8cfb0a910be2e3db50dcd50a9c54064c2a5550801daa834ff4480b33d3d3ca495ff8a4e84a886977d17d998f881241a874083d8b995beab555b6d22b8a4817ab17ac3e7304f7d4d2c05c495fb2218348d3bc13651db1d92732e368a9dd7dcefa6eddff30b94706a9aaee47e9d39321460b740c59c6fc3c2fd8ab6c0fb868cb87c0051f0321301fe0f0e1820b15e7fb7063395769b525005c7e30a7ce85984f5cac00504e7b4fdc45d74958de8388436fd5c7ba9ea121f1c851b5911dd1b47a14d81a09e92ef37721e2325b6790011010001cd00c2c07b041001080025050251d0c680050900278d00060b09070803020415080a0203160201021901021b03021e01000a0910e7b484133a890a35ae4b0800a1beb82e7f28eaf5273d6af9d3391314f6280b2b624eaca2851f89a9ebcaf80ac589ebd509f168bc4322106ca2e2ce77a76e071a3c7444787d65216b5f05e82c77928860b92aace3b7d0327db59492f422eb9dfab7249266d37429870b091a98aba8724c2259ebf8f85093f21255eafa75aa841e31d94f2ac891b9755fed455e539044ee69fc47950b80e003fc9f298d695660f28329eaa38037c367efde1727458e514faf990d439a21461b719edaddf9296d3d0647b43ca56cb8dbf63b4fcf8b9968e7928c463470fab3b98e44d0d95645062f94b2d04fe56bd52822b71934db8ce845622c40b92fcbe765a142e7f38b61a6aa9606c8e8858dcd3b6eb1894acec04d0451d1f06b01080088bea67444e1789390e7c0335c86775502d58ec783d99c8ef4e06de235ed3dd4b0467f6f358d818c7d8989d43ec6d69fcbc8c32632d5a1b605e3fa8e41d695fcdcaa535936cd0157f9040dce362519803b908eafe838bb13216c885c6f93e9e8d5745607f0d062322085d6bdc760969149a8ff8dd9f5c18d9bfe2e6f63a06e17694cf1f67587c6fb70e9aebf90ffc528ca3b615ac7c9d4a21ea4f7c06f2e98fbbd90a859b8608bf9ea638e3a54289ce44c283110d0c45fa458de6251cd6e7baf71f80f12c8978340490fd90c92b81736ae902ed958e478dceae2835953d189c45d182aff02ea2be61b81d8e94430f041d638647b43e2fcb45fd512fbf5068b810011010001c2c06504180108000f050251d1f06b050900081095021b0c000a0910e7b484133a890a35e63407fe2ec88d6d1e6c9ce7553ece0cb2524747217bad29f251d33df84599ffcc900141a355abd62126800744068a5e05dc167056aa9205273dc7765a2ed49db15c2a83b8d6e6429c902136f1e12229086c1c10c0053242c2a4ae1930db58163387a48cad64607ff2153c320e42843dec28e3fce90e7399d63ac0affa2fee1f0adc0953c89eb3f46ef1d6c04328ed13b491669d5120a3782e3ffb7c69575fb77eebd108794f4dda9d34be2bae57e8e59ec8ebfda2f6f06104b2321be408ea146e2db482b00c5055c8618de36ac9716f80da2617e225556d0fce61b01c8cea2d1e0ea982c31711060ca370f2739366e1e708f38405d784b49d16a26cf62d152eae734327cec04d0451d1f07b010800d5af91c5e7c2fd8951c8d254eab0c97cdcb66822f868b79b78c366255059a68fd74ebca9adb9b970cd9e586690e6e0756705432306878c897b10a4b4ca0005966f99ac8fa4e6f9caf54bf8e53844544beee9872a7ac64c119cf1393d96e674254b661f61ee975633d0e8a8672531edb6bb8e211204e7754a9efa802342118eee850beea742bac95a3f706cc2024cf6037a308bb68162b2f53b9a6346a96e6d31871a2456186e24a1c7a82b82ac04afdfd57cd7fb9ba77a9c760d40b76a170f7be525e5fb6a9848cc726e806187710d9b190387df28700f321f988a392899f93815cc937f309129eb94d5299c5547cb2c085898e6639496e70d746c9d3fb9881d0011010001c2c06504180108000f050251d1f07b050900266305021b0c000a0910e7b484133a890a35bff207fd10dfe8c4a6ea1dd30568012b6fd6891a763c87ad0f7a1d112aad9e8e3239378a3b85588c235865bac2e614348cb4f216d7217f53b3ef48c192e0a4d31d64d7bfa5faccf21155965fa156e887056db644a05ad08a85cc6152d1377d9e37b46f4ff462bbe68ace2dc586ef90070314576c985d8037c2ba63f0a7dc17a62e15bd77e88bc61d9d00858979709f12304264a4cf4225c5cf86f12c8e19486cb9cdcc69f18f027e5f16f4ca8b50e28b3115eaff3a345acd21f624aef81f6ede515c1b55b26b84c1e32264754eab672d5489b287e7277ea855e0a5ff2aa9e8b8c76d579a964ec225255f4d57bf66639ccb34b64798846943e162a41096a7002ca21c7f56" +const subkeyUsageHex = "988d04533a52bc010400d26af43085558f65b9e7dbc90cb9238015259aed5e954637adcfa2181548b2d0b60c65f1f42ec5081cbf1bc0a8aa4900acfb77070837c58f26012fbce297d70afe96e759ad63531f0037538e70dbf8e384569b9720d99d8eb39d8d0a2947233ed242436cb6ac7dfe74123354b3d0119b5c235d3dd9c9d6c004f8ffaf67ad8583001101000188b7041f010200210502533b8552170c8001ce094aa433f7040bb2ddf0be3893cb843d0fe70c020700000a0910a42704b92866382aa98404009d63d916a27543da4221c60087c33f1c44bec9998c5438018ed370cca4962876c748e94b73eb39c58eb698063f3fd6346d58dd2a11c0247934c4a9d71f24754f7468f96fb24c3e791dd2392b62f626148ad724189498cbf993db2df7c0cdc2d677c35da0f16cb16c9ce7c33b4de65a4a91b1d21a130ae9cc26067718910ef8e2b417556d627261203c756d627261407379642e65642e61753e88b80413010200220502533a52bc021b03060b090807030206150802090a0b0416020301021e01021780000a0910a42704b92866382a47840400c0c2bd04f5fca586de408b395b3c280a278259c93eaaa8b79a53b97003f8ed502a8a00446dd9947fb462677e4fcac0dac2f0701847d15130aadb6cd9e0705ea0cf5f92f129136c7be21a718d46c8e641eb7f044f2adae573e11ae423a0a9ca51324f03a8a2f34b91fa40c3cc764bee4dccadedb54c768ba0469b683ea53f1c29b88d04533a52bc01040099c92a5d6f8b744224da27bc2369127c35269b58bec179de6bbc038f749344222f85a31933224f26b70243c4e4b2d242f0c4777eaef7b5502f9dad6d8bf3aaeb471210674b74de2d7078af497d55f5cdad97c7bedfbc1b41e8065a97c9c3d344b21fc81d27723af8e374bc595da26ea242dccb6ae497be26eea57e563ed517e90011010001889f0418010200090502533a52bc021b0c000a0910a42704b92866382afa1403ff70284c2de8a043ff51d8d29772602fa98009b7861c540535f874f2c230af8caf5638151a636b21f8255003997ccd29747fdd06777bb24f9593bd7d98a3e887689bf902f999915fcc94625ae487e5d13e6616f89090ebc4fdc7eb5cad8943e4056995bb61c6af37f8043016876a958ec7ebf39c43d20d53b7f546cfa83e8d2604b88d04533b8283010400c0b529316dbdf58b4c54461e7e669dc11c09eb7f73819f178ccd4177b9182b91d138605fcf1e463262fabefa73f94a52b5e15d1904635541c7ea540f07050ce0fb51b73e6f88644cec86e91107c957a114f69554548a85295d2b70bd0b203992f76eb5d493d86d9eabcaa7ef3fc7db7e458438db3fcdb0ca1cc97c638439a9170011010001889f0418010200090502533b8283021b0c000a0910a42704b92866382adc6d0400cfff6258485a21675adb7a811c3e19ebca18851533f75a7ba317950b9997fda8d1a4c8c76505c08c04b6c2cc31dc704d33da36a21273f2b388a1a706f7c3378b66d887197a525936ed9a69acb57fe7f718133da85ec742001c5d1864e9c6c8ea1b94f1c3759cebfd93b18606066c063a63be86085b7e37bdbc65f9a915bf084bb901a204533b85cd110400aed3d2c52af2b38b5b67904b0ef73d6dd7aef86adb770e2b153cd22489654dcc91730892087bb9856ae2d9f7ed1eb48f214243fe86bfe87b349ebd7c30e630e49c07b21fdabf78b7a95c8b7f969e97e3d33f2e074c63552ba64a2ded7badc05ce0ea2be6d53485f6900c7860c7aa76560376ce963d7271b9b54638a4028b573f00a0d8854bfcdb04986141568046202192263b9b67350400aaa1049dbc7943141ef590a70dcb028d730371d92ea4863de715f7f0f16d168bd3dc266c2450457d46dcbbf0b071547e5fbee7700a820c3750b236335d8d5848adb3c0da010e998908dfd93d961480084f3aea20b247034f8988eccb5546efaa35a92d0451df3aaf1aee5aa36a4c4d462c760ecd9cebcabfbe1412b1f21450f203fd126687cd486496e971a87fd9e1a8a765fe654baa219a6871ab97768596ab05c26c1aeea8f1a2c72395a58dbc12ef9640d2b95784e974a4d2d5a9b17c25fedacfe551bda52602de8f6d2e48443f5dd1a2a2a8e6a5e70ecdb88cd6e766ad9745c7ee91d78cc55c3d06536b49c3fee6c3d0b6ff0fb2bf13a314f57c953b8f4d93bf88e70418010200090502533b85cd021b0200520910a42704b92866382a47200419110200060502533b85cd000a091042ce2c64bc0ba99214b2009e26b26852c8b13b10c35768e40e78fbbb48bd084100a0c79d9ea0844fa5853dd3c85ff3ecae6f2c9dd6c557aa04008bbbc964cd65b9b8299d4ebf31f41cc7264b8cf33a00e82c5af022331fac79efc9563a822497ba012953cefe2629f1242fcdcb911dbb2315985bab060bfd58261ace3c654bdbbe2e8ed27a46e836490145c86dc7bae15c011f7e1ffc33730109b9338cd9f483e7cef3d2f396aab5bd80efb6646d7e778270ee99d934d187dd98" +const revokedKeyHex = "988d045331ce82010400c4fdf7b40a5477f206e6ee278eaef888ca73bf9128a9eef9f2f1ddb8b7b71a4c07cfa241f028a04edb405e4d916c61d6beabc333813dc7b484d2b3c52ee233c6a79b1eea4e9cc51596ba9cd5ac5aeb9df62d86ea051055b79d03f8a4fa9f38386f5bd17529138f3325d46801514ea9047977e0829ed728e68636802796801be10011010001889f04200102000905025331d0e3021d03000a0910a401d9f09a34f7c042aa040086631196405b7e6af71026b88e98012eab44aa9849f6ef3fa930c7c9f23deaedba9db1538830f8652fb7648ec3fcade8dbcbf9eaf428e83c6cbcc272201bfe2fbb90d41963397a7c0637a1a9d9448ce695d9790db2dc95433ad7be19eb3de72dacf1d6db82c3644c13eae2a3d072b99bb341debba012c5ce4006a7d34a1f4b94b444526567205265766f6b657220283c52656727732022424d204261726973746122204b657920262530305c303e5c29203c72656740626d626172697374612e636f2e61753e88b704130102002205025331ce82021b03060b090807030206150802090a0b0416020301021e01021780000a0910a401d9f09a34f7c0019c03f75edfbeb6a73e7225ad3cc52724e2872e04260d7daf0d693c170d8c4b243b8767bc7785763533febc62ec2600c30603c433c095453ede59ff2fcabeb84ce32e0ed9d5cf15ffcbc816202b64370d4d77c1e9077d74e94a16fb4fa2e5bec23a56d7a73cf275f91691ae1801a976fcde09e981a2f6327ac27ea1fecf3185df0d56889c04100102000605025331cfb5000a0910fe9645554e8266b64b4303fc084075396674fb6f778d302ac07cef6bc0b5d07b66b2004c44aef711cbac79617ef06d836b4957522d8772dd94bf41a2f4ac8b1ee6d70c57503f837445a74765a076d07b829b8111fc2a918423ddb817ead7ca2a613ef0bfb9c6b3562aec6c3cf3c75ef3031d81d95f6563e4cdcc9960bcb386c5d757b104fcca5fe11fc709df884604101102000605025331cfe7000a09107b15a67f0b3ddc0317f6009e360beea58f29c1d963a22b962b80788c3fa6c84e009d148cfde6b351469b8eae91187eff07ad9d08fcaab88d045331ce820104009f25e20a42b904f3fa555530fe5c46737cf7bd076c35a2a0d22b11f7e0b61a69320b768f4a80fe13980ce380d1cfc4a0cd8fbe2d2e2ef85416668b77208baa65bf973fe8e500e78cc310d7c8705cdb34328bf80e24f0385fce5845c33bc7943cf6b11b02348a23da0bf6428e57c05135f2dc6bd7c1ce325d666d5a5fd2fd5e410011010001889f04180102000905025331ce82021b0c000a0910a401d9f09a34f7c0418003fe34feafcbeaef348a800a0d908a7a6809cc7304017d820f70f0474d5e23cb17e38b67dc6dca282c6ca00961f4ec9edf2738d0f087b1d81e4871ef08e1798010863afb4eac4c44a376cb343be929c5be66a78cfd4456ae9ec6a99d97f4e1c3ff3583351db2147a65c0acef5c003fb544ab3a2e2dc4d43646f58b811a6c3a369d1f" +const revokedSubkeyHex = "988d04533121f6010400aefc803a3e4bb1a61c86e8a86d2726c6a43e0079e9f2713f1fa017e9854c83877f4aced8e331d675c67ea83ddab80aacbfa0b9040bb12d96f5a3d6be09455e2a76546cbd21677537db941cab710216b6d24ec277ee0bd65b910f416737ed120f6b93a9d3b306245c8cfd8394606fdb462e5cf43c551438d2864506c63367fc890011010001b41d416c696365203c616c69636540626d626172697374612e636f2e61753e88bb041301020025021b03060b090807030206150802090a0b0416020301021e01021780050253312798021901000a09104ef7e4beccde97f015a803ff5448437780f63263b0df8442a995e7f76c221351a51edd06f2063d8166cf3157aada4923dfc44aa0f2a6a4da5cf83b7fe722ba8ab416c976e77c6b5682e7f1069026673bd0de56ba06fd5d7a9f177607f277d9b55ff940a638c3e68525c67517e2b3d976899b93ca267f705b3e5efad7d61220e96b618a4497eab8d04403d23f8846041011020006050253312910000a09107b15a67f0b3ddc03d96e009f50b6365d86c4be5d5e9d0ea42d5e56f5794c617700a0ab274e19c2827780016d23417ce89e0a2c0d987d889c04100102000605025331cf7a000a0910a401d9f09a34f7c0ee970400aca292f213041c9f3b3fc49148cbda9d84afee6183c8dd6c5ff2600b29482db5fecd4303797be1ee6d544a20a858080fec43412061c9a71fae4039fd58013b4ae341273e6c66ad4c7cdd9e68245bedb260562e7b166f2461a1032f2b38c0e0e5715fb3d1656979e052b55ca827a76f872b78a9fdae64bc298170bfcebedc1271b41a416c696365203c616c696365407379646973702e6f722e61753e88b804130102002205025331278b021b03060b090807030206150802090a0b0416020301021e01021780000a09104ef7e4beccde97f06a7003fa03c3af68d272ebc1fa08aa72a03b02189c26496a2833d90450801c4e42c5b5f51ad96ce2d2c9cef4b7c02a6a2fcf1412d6a2d486098eb762f5010a201819c17fd2888aec8eda20c65a3b75744de7ee5cc8ac7bfc470cbe3cb982720405a27a3c6a8c229cfe36905f881b02ed5680f6a8f05866efb9d6c5844897e631deb949ca8846041011020006050253312910000a09107b15a67f0b3ddc0347bc009f7fa35db59147469eb6f2c5aaf6428accb138b22800a0caa2f5f0874bacc5909c652a57a31beda65eddd5889c04100102000605025331cf7a000a0910a401d9f09a34f7c0316403ff46f2a5c101256627f16384d34a38fb47a6c88ba60506843e532d91614339fccae5f884a5741e7582ffaf292ba38ee10a270a05f139bde3814b6a077e8cd2db0f105ebea2a83af70d385f13b507fac2ad93ff79d84950328bb86f3074745a8b7f9b64990fb142e2a12976e27e8d09a28dc5621f957ac49091116da410ac3cbde1b88d04533121f6010400cbd785b56905e4192e2fb62a720727d43c4fa487821203cf72138b884b78b701093243e1d8c92a0248a6c0203a5a88693da34af357499abacaf4b3309c640797d03093870a323b4b6f37865f6eaa2838148a67df4735d43a90ca87942554cdf1c4a751b1e75f9fd4ce4e97e278d6c1c7ed59d33441df7d084f3f02beb68896c70011010001889f0418010200090502533121f6021b0c000a09104ef7e4beccde97f0b98b03fc0a5ccf6a372995835a2f5da33b282a7d612c0ab2a97f59cf9fff73e9110981aac2858c41399afa29624a7fd8a0add11654e3d882c0fd199e161bdad65e5e2548f7b68a437ea64293db1246e3011cbb94dc1bcdeaf0f2539bd88ff16d95547144d97cead6a8c5927660a91e6db0d16eb36b7b49a3525b54d1644e65599b032b7eb901a204533127a0110400bd3edaa09eff9809c4edc2c2a0ebe52e53c50a19c1e49ab78e6167bf61473bb08f2050d78a5cbbc6ed66aff7b42cd503f16b4a0b99fa1609681fca9b7ce2bbb1a5b3864d6cdda4d7ef7849d156d534dea30fb0efb9e4cf8959a2b2ce623905882d5430b995a15c3b9fe92906086788b891002924f94abe139b42cbbfaaabe42f00a0b65dc1a1ad27d798adbcb5b5ad02d2688c89477b03ff4eebb6f7b15a73b96a96bed201c0e5e4ea27e4c6e2dd1005b94d4b90137a5b1cf5e01c6226c070c4cc999938101578877ee76d296b9aab8246d57049caacf489e80a3f40589cade790a020b1ac146d6f7a6241184b8c7fcde680eae3188f5dcbe846d7f7bdad34f6fcfca08413e19c1d5df83fc7c7c627d493492e009c2f52a80400a2fe82de87136fd2e8845888c4431b032ba29d9a29a804277e31002a8201fb8591a3e55c7a0d0881496caf8b9fb07544a5a4879291d0dc026a0ea9e5bd88eb4aa4947bbd694b25012e208a250d65ddc6f1eea59d3aed3b4ec15fcab85e2afaa23a40ab1ef9ce3e11e1bc1c34a0e758e7aa64deb8739276df0af7d4121f834a9b88e70418010200090502533127a0021b02005209104ef7e4beccde97f047200419110200060502533127a0000a0910dbce4ee19529437fe045009c0b32f5ead48ee8a7e98fac0dea3d3e6c0e2c552500a0ad71fadc5007cfaf842d9b7db3335a8cdad15d3d1a6404009b08e2c68fe8f3b45c1bb72a4b3278cdf3012aa0f229883ad74aa1f6000bb90b18301b2f85372ca5d6b9bf478d235b733b1b197d19ccca48e9daf8e890cb64546b4ce1b178faccfff07003c172a2d4f5ebaba9f57153955f3f61a9b80a4f5cb959908f8b211b03b7026a8a82fc612bfedd3794969bcf458c4ce92be215a1176ab88d045331d144010400a5063000c5aaf34953c1aa3bfc95045b3aab9882b9a8027fecfe2142dc6b47ba8aca667399990244d513dd0504716908c17d92c65e74219e004f7b83fc125e575dd58efec3ab6dd22e3580106998523dea42ec75bf9aa111734c82df54630bebdff20fe981cfc36c76f865eb1c2fb62c9e85bc3a6e5015a361a2eb1c8431578d0011010001889f04280102000905025331d433021d03000a09104ef7e4beccde97f02e5503ff5e0630d1b65291f4882b6d40a29da4616bb5088717d469fbcc3648b8276de04a04988b1f1b9f3e18f52265c1f8b6c85861691c1a6b8a3a25a1809a0b32ad330aec5667cb4262f4450649184e8113849b05e5ad06a316ea80c001e8e71838190339a6e48bbde30647bcf245134b9a97fa875c1d83a9862cae87ffd7e2c4ce3a1b89013d04180102000905025331d144021b0200a809104ef7e4beccde97f09d2004190102000605025331d144000a0910677815e371c2fd23522203fe22ab62b8e7a151383cea3edd3a12995693911426f8ccf125e1f6426388c0010f88d9ca7da2224aee8d1c12135998640c5e1813d55a93df472faae75bef858457248db41b4505827590aeccf6f9eb646da7f980655dd3050c6897feddddaca90676dee856d66db8923477d251712bb9b3186b4d0114daf7d6b59272b53218dd1da94a03ff64006fcbe71211e5daecd9961fba66cdb6de3f914882c58ba5beddeba7dcb950c1156d7fba18c19ea880dccc800eae335deec34e3b84ac75ffa24864f782f87815cda1c0f634b3dd2fa67cea30811d21723d21d9551fa12ccbcfa62b6d3a15d01307b99925707992556d50065505b090aadb8579083a20fe65bd2a270da9b011" + +const missingCrossSignatureKey = `-----BEGIN PGP PUBLIC KEY BLOCK----- +Charset: UTF-8 + +mQENBFMYynYBCACVOZ3/e8Bm2b9KH9QyIlHGo/i1bnkpqsgXj8tpJ2MIUOnXMMAY +ztW7kKFLCmgVdLIC0vSoLA4yhaLcMojznh/2CcUglZeb6Ao8Gtelr//Rd5DRfPpG +zqcfUo+m+eO1co2Orabw0tZDfGpg5p3AYl0hmxhUyYSc/xUq93xL1UJzBFgYXY54 +QsM8dgeQgFseSk/YvdP5SMx1ev+eraUyiiUtWzWrWC1TdyRa5p4UZg6Rkoppf+WJ +QrW6BWrhAtqATHc8ozV7uJjeONjUEq24roRc/OFZdmQQGK6yrzKnnbA6MdHhqpdo +9kWDcXYb7pSE63Lc+OBa5X2GUVvXJLS/3nrtABEBAAG0F2ludmFsaWQtc2lnbmlu +Zy1zdWJrZXlziQEoBBMBAgASBQJTnKB5AhsBAgsHAhUIAh4BAAoJEO3UDQUIHpI/ +dN4H/idX4FQ1LIZCnpHS/oxoWQWfpRgdKAEM0qCqjMgiipJeEwSQbqjTCynuh5/R +JlODDz85ABR06aoF4l5ebGLQWFCYifPnJZ/Yf5OYcMGtb7dIbqxWVFL9iLMO/oDL +ioI3dotjPui5e+2hI9pVH1UHB/bZ/GvMGo6Zg0XxLPolKQODMVjpjLAQ0YJ3spew +RAmOGre6tIvbDsMBnm8qREt7a07cBJ6XK7xjxYaZHQBiHVxyEWDa6gyANONx8duW +/fhQ/zDTnyVM/ik6VO0Ty9BhPpcEYLFwh5c1ilFari1ta3e6qKo6ZGa9YMk/REhu +yBHd9nTkI+0CiQUmbckUiVjDKKe5AQ0EUxjKdgEIAJcXQeP+NmuciE99YcJoffxv +2gVLU4ZXBNHEaP0mgaJ1+tmMD089vUQAcyGRvw8jfsNsVZQIOAuRxY94aHQhIRHR +bUzBN28ofo/AJJtfx62C15xt6fDKRV6HXYqAiygrHIpEoRLyiN69iScUsjIJeyFL +C8wa72e8pSL6dkHoaV1N9ZH/xmrJ+k0vsgkQaAh9CzYufncDxcwkoP+aOlGtX1gP +WwWoIbz0JwLEMPHBWvDDXQcQPQTYQyj+LGC9U6f9VZHN25E94subM1MjuT9OhN9Y +MLfWaaIc5WyhLFyQKW2Upofn9wSFi8ubyBnv640Dfd0rVmaWv7LNTZpoZ/GbJAMA +EQEAAYkBHwQYAQIACQUCU5ygeQIbAgAKCRDt1A0FCB6SP0zCB/sEzaVR38vpx+OQ +MMynCBJrakiqDmUZv9xtplY7zsHSQjpd6xGflbU2n+iX99Q+nav0ETQZifNUEd4N +1ljDGQejcTyKD6Pkg6wBL3x9/RJye7Zszazm4+toJXZ8xJ3800+BtaPoI39akYJm ++ijzbskvN0v/j5GOFJwQO0pPRAFtdHqRs9Kf4YanxhedB4dIUblzlIJuKsxFit6N +lgGRblagG3Vv2eBszbxzPbJjHCgVLR3RmrVezKOsZjr/2i7X+xLWIR0uD3IN1qOW +CXQxLBizEEmSNVNxsp7KPGTLnqO3bPtqFirxS9PJLIMPTPLNBY7ZYuPNTMqVIUWF +4artDmrG +=7FfJ +-----END PGP PUBLIC KEY BLOCK-----` + +const invalidCrossSignatureKey = `-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQENBFMYynYBCACVOZ3/e8Bm2b9KH9QyIlHGo/i1bnkpqsgXj8tpJ2MIUOnXMMAY +ztW7kKFLCmgVdLIC0vSoLA4yhaLcMojznh/2CcUglZeb6Ao8Gtelr//Rd5DRfPpG +zqcfUo+m+eO1co2Orabw0tZDfGpg5p3AYl0hmxhUyYSc/xUq93xL1UJzBFgYXY54 +QsM8dgeQgFseSk/YvdP5SMx1ev+eraUyiiUtWzWrWC1TdyRa5p4UZg6Rkoppf+WJ +QrW6BWrhAtqATHc8ozV7uJjeONjUEq24roRc/OFZdmQQGK6yrzKnnbA6MdHhqpdo +9kWDcXYb7pSE63Lc+OBa5X2GUVvXJLS/3nrtABEBAAG0F2ludmFsaWQtc2lnbmlu +Zy1zdWJrZXlziQEoBBMBAgASBQJTnKB5AhsBAgsHAhUIAh4BAAoJEO3UDQUIHpI/ +dN4H/idX4FQ1LIZCnpHS/oxoWQWfpRgdKAEM0qCqjMgiipJeEwSQbqjTCynuh5/R +JlODDz85ABR06aoF4l5ebGLQWFCYifPnJZ/Yf5OYcMGtb7dIbqxWVFL9iLMO/oDL +ioI3dotjPui5e+2hI9pVH1UHB/bZ/GvMGo6Zg0XxLPolKQODMVjpjLAQ0YJ3spew +RAmOGre6tIvbDsMBnm8qREt7a07cBJ6XK7xjxYaZHQBiHVxyEWDa6gyANONx8duW +/fhQ/zDTnyVM/ik6VO0Ty9BhPpcEYLFwh5c1ilFari1ta3e6qKo6ZGa9YMk/REhu +yBHd9nTkI+0CiQUmbckUiVjDKKe5AQ0EUxjKdgEIAIINDqlj7X6jYKc6DjwrOkjQ +UIRWbQQar0LwmNilehmt70g5DCL1SYm9q4LcgJJ2Nhxj0/5qqsYib50OSWMcKeEe +iRXpXzv1ObpcQtI5ithp0gR53YPXBib80t3bUzomQ5UyZqAAHzMp3BKC54/vUrSK +FeRaxDzNLrCeyI00+LHNUtwghAqHvdNcsIf8VRumK8oTm3RmDh0TyjASWYbrt9c8 +R1Um3zuoACOVy+mEIgIzsfHq0u7dwYwJB5+KeM7ZLx+HGIYdUYzHuUE1sLwVoELh ++SHIGHI1HDicOjzqgajShuIjj5hZTyQySVprrsLKiXS6NEwHAP20+XjayJ/R3tEA +EQEAAYkCPgQYAQIBKAUCU5ygeQIbAsBdIAQZAQIABgUCU5ygeQAKCRCpVlnFZmhO +52RJB/9uD1MSa0wjY6tHOIgquZcP3bHBvHmrHNMw9HR2wRCMO91ZkhrpdS3ZHtgb +u3/55etj0FdvDo1tb8P8FGSVtO5Vcwf5APM8sbbqoi8L951Q3i7qt847lfhu6sMl +w0LWFvPTOLHrliZHItPRjOltS1WAWfr2jUYhsU9ytaDAJmvf9DujxEOsN5G1YJep +54JCKVCkM/y585Zcnn+yxk/XwqoNQ0/iJUT9qRrZWvoeasxhl1PQcwihCwss44A+ +YXaAt3hbk+6LEQuZoYS73yR3WHj+42tfm7YxRGeubXfgCEz/brETEWXMh4pe0vCL +bfWrmfSPq2rDegYcAybxRQz0lF8PAAoJEO3UDQUIHpI/exkH/0vQfdHA8g/N4T6E +i6b1CUVBAkvtdJpCATZjWPhXmShOw62gkDw306vHPilL4SCvEEi4KzG72zkp6VsB +DSRcpxCwT4mHue+duiy53/aRMtSJ+vDfiV1Vhq+3sWAck/yUtfDU9/u4eFaiNok1 +8/Gd7reyuZt5CiJnpdPpjCwelK21l2w7sHAnJF55ITXdOxI8oG3BRKufz0z5lyDY +s2tXYmhhQIggdgelN8LbcMhWs/PBbtUr6uZlNJG2lW1yscD4aI529VjwJlCeo745 +U7pO4eF05VViUJ2mmfoivL3tkhoTUWhx8xs8xCUcCg8DoEoSIhxtOmoTPR22Z9BL +6LCg2mg= +=Dhm4 +-----END PGP PUBLIC KEY BLOCK-----` + +const goodCrossSignatureKey = `-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1 + +mI0EVUqeVwEEAMufHRrMPWK3gyvi0O0tABCs/oON9zV9KDZlr1a1M91ShCSFwCPo +7r80PxdWVWcj0V5h50/CJYtpN3eE/mUIgW2z1uDYQF1OzrQ8ubrksfsJvpAhENom +lTQEppv9mV8qhcM278teb7TX0pgrUHLYF5CfPdp1L957JLLXoQR/lwLVABEBAAG0 +E2dvb2Qtc2lnbmluZy1zdWJrZXmIuAQTAQIAIgUCVUqeVwIbAwYLCQgHAwIGFQgC +CQoLBBYCAwECHgECF4AACgkQNRjL95IRWP69XQQAlH6+eyXJN4DZTLX78KGjHrsw +6FCvxxClEPtPUjcJy/1KCRQmtLAt9PbbA78dvgzjDeZMZqRAwdjyJhjyg/fkU2OH +7wq4ktjUu+dLcOBb+BFMEY+YjKZhf6EJuVfxoTVr5f82XNPbYHfTho9/OABKH6kv +X70PaKZhbwnwij8Nts65AaIEVUqftREEAJ3WxZfqAX0bTDbQPf2CMT2IVMGDfhK7 +GyubOZgDFFjwUJQvHNvsrbeGLZ0xOBumLINyPO1amIfTgJNm1iiWFWfmnHReGcDl +y5mpYG60Mb79Whdcer7CMm3AqYh/dW4g6IB02NwZMKoUHo3PXmFLxMKXnWyJ0clw +R0LI/Qn509yXAKDh1SO20rqrBM+EAP2c5bfI98kyNwQAi3buu94qo3RR1ZbvfxgW +CKXDVm6N99jdZGNK7FbRifXqzJJDLcXZKLnstnC4Sd3uyfyf1uFhmDLIQRryn5m+ +LBYHfDBPN3kdm7bsZDDq9GbTHiFZUfm/tChVKXWxkhpAmHhU/tH6GGzNSMXuIWSO +aOz3Rqq0ED4NXyNKjdF9MiwD/i83S0ZBc0LmJYt4Z10jtH2B6tYdqnAK29uQaadx +yZCX2scE09UIm32/w7pV77CKr1Cp/4OzAXS1tmFzQ+bX7DR+Gl8t4wxr57VeEMvl +BGw4Vjh3X8//m3xynxycQU18Q1zJ6PkiMyPw2owZ/nss3hpSRKFJsxMLhW3fKmKr +Ey2KiOcEGAECAAkFAlVKn7UCGwIAUgkQNRjL95IRWP5HIAQZEQIABgUCVUqftQAK +CRD98VjDN10SqkWrAKDTpEY8D8HC02E/KVC5YUI01B30wgCgurpILm20kXEDCeHp +C5pygfXw1DJrhAP+NyPJ4um/bU1I+rXaHHJYroYJs8YSweiNcwiHDQn0Engh/mVZ +SqLHvbKh2dL/RXymC3+rjPvQf5cup9bPxNMa6WagdYBNAfzWGtkVISeaQW+cTEp/ +MtgVijRGXR/lGLGETPg2X3Afwn9N9bLMBkBprKgbBqU7lpaoPupxT61bL70= +=vtbN +-----END PGP PUBLIC KEY BLOCK-----` + +const revokedUserIDKey = `-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQENBFsgO5EBCADhREPmcjsPkXe1z7ctvyWL0S7oa9JaoGZ9oPDHFDlQxd0qlX2e +DZJZDg0qYvVixmaULIulApq1puEsaJCn3lHUbHlb4PYKwLEywYXM28JN91KtLsz/ +uaEX2KC5WqeP40utmzkNLq+oRX/xnRMgwbO7yUNVG2UlEa6eI+xOXO3YtLdmJMBW +ClQ066ZnOIzEo1JxnIwha1CDBMWLLfOLrg6l8InUqaXbtEBbnaIYO6fXVXELUjkx +nmk7t/QOk0tXCy8muH9UDqJkwDUESY2l79XwBAcx9riX8vY7vwC34pm22fAUVLCJ +x1SJx0J8bkeNp38jKM2Zd9SUQqSbfBopQ4pPABEBAAG0I0dvbGFuZyBHb3BoZXIg +PG5vLXJlcGx5QGdvbGFuZy5jb20+iQFUBBMBCgA+FiEE5Ik5JLcNx6l6rZfw1oFy +9I6cUoMFAlsgO5ECGwMFCQPCZwAFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AACgkQ +1oFy9I6cUoMIkwf8DNPeD23i4jRwd/pylbvxwZintZl1fSwTJW1xcOa1emXaEtX2 +depuqhP04fjlRQGfsYAQh7X9jOJxAHjTmhqFBi5sD7QvKU00cPFYbJ/JTx0B41bl +aXnSbGhRPh63QtEZL7ACAs+shwvvojJqysx7kyVRu0EW2wqjXdHwR/SJO6nhNBa2 +DXzSiOU/SUA42mmG+5kjF8Aabq9wPwT9wjraHShEweNerNMmOqJExBOy3yFeyDpa +XwEZFzBfOKoxFNkIaVf5GSdIUGhFECkGvBMB935khftmgR8APxdU4BE7XrXexFJU +8RCuPXonm4WQOwTWR0vQg64pb2WKAzZ8HhwTGbQiR29sYW5nIEdvcGhlciA8cmV2 +b2tlZEBnb2xhbmcuY29tPokBNgQwAQoAIBYhBOSJOSS3Dcepeq2X8NaBcvSOnFKD +BQJbIDv3Ah0AAAoJENaBcvSOnFKDfWMIAKhI/Tvu3h8fSUxp/gSAcduT6bC1JttG +0lYQ5ilKB/58lBUA5CO3ZrKDKlzW3M8VEcvohVaqeTMKeoQd5rCZq8KxHn/KvN6N +s85REfXfniCKfAbnGgVXX3kDmZ1g63pkxrFu0fDZjVDXC6vy+I0sGyI/Inro0Pzb +tvn0QCsxjapKK15BtmSrpgHgzVqVg0cUp8vqZeKFxarYbYB2idtGRci4b9tObOK0 +BSTVFy26+I/mrFGaPrySYiy2Kz5NMEcRhjmTxJ8jSwEr2O2sUR0yjbgUAXbTxDVE +/jg5fQZ1ACvBRQnB7LvMHcInbzjyeTM3FazkkSYQD6b97+dkWwb1iWG5AQ0EWyA7 +kQEIALkg04REDZo1JgdYV4x8HJKFS4xAYWbIva1ZPqvDNmZRUbQZR2+gpJGEwn7z +VofGvnOYiGW56AS5j31SFf5kro1+1bZQ5iOONBng08OOo58/l1hRseIIVGB5TGSa +PCdChKKHreJI6hS3mShxH6hdfFtiZuB45rwoaArMMsYcjaezLwKeLc396cpUwwcZ +snLUNd1Xu5EWEF2OdFkZ2a1qYdxBvAYdQf4+1Nr+NRIx1u1NS9c8jp3PuMOkrQEi +bNtc1v6v0Jy52mKLG4y7mC/erIkvkQBYJdxPaP7LZVaPYc3/xskcyijrJ/5ufoD8 +K71/ShtsZUXSQn9jlRaYR0EbojMAEQEAAYkBPAQYAQoAJhYhBOSJOSS3Dcepeq2X +8NaBcvSOnFKDBQJbIDuRAhsMBQkDwmcAAAoJENaBcvSOnFKDkFMIAIt64bVZ8x7+ +TitH1bR4pgcNkaKmgKoZz6FXu80+SnbuEt2NnDyf1cLOSimSTILpwLIuv9Uft5Pb +OraQbYt3xi9yrqdKqGLv80bxqK0NuryNkvh9yyx5WoG1iKqMj9/FjGghuPrRaT4l +QinNAghGVkEy1+aXGFrG2DsOC1FFI51CC2WVTzZ5RwR2GpiNRfESsU1rZAUqf/2V +yJl9bD5R4SUNy8oQmhOxi+gbhD4Ao34e4W0ilibslI/uawvCiOwlu5NGd8zv5n+U +heiQvzkApQup5c+BhH5zFDFdKJ2CBByxw9+7QjMFI/wgLixKuE0Ob2kAokXf7RlB +7qTZOahrETw= +=IKnw +-----END PGP PUBLIC KEY BLOCK-----` + +const keyWithSubKey = `-----BEGIN PGP PUBLIC KEY BLOCK----- + +mI0EWyKwKQEEALwXhKBnyaaNFeK3ljfc/qn9X/QFw+28EUfgZPHjRmHubuXLE2uR +s3ZoSXY2z7Dkv+NyHYMt8p+X8q5fR7JvUjK2XbPyKoiJVnHINll83yl67DaWfKNL +EjNoO0kIfbXfCkZ7EG6DL+iKtuxniGTcnGT47e+HJSqb/STpLMnWwXjBABEBAAG0 +I0dvbGFuZyBHb3BoZXIgPG5vLXJlcGx5QGdvbGFuZy5jb20+iM4EEwEKADgWIQQ/ +lRafP/p9PytHbwxMvYJsOQdOOAUCWyKwKQIbAwULCQgHAwUVCgkICwUWAgMBAAIe +AQIXgAAKCRBMvYJsOQdOOOsFBAC62mXww8XuqvYLcVOvHkWLT6mhxrQOJXnlfpn7 +2uBV9CMhoG/Ycd43NONsJrB95Apr9TDIqWnVszNbqPCuBhZQSGLdbiDKjxnCWBk0 +69qv4RNtkpOhYB7jK4s8F5oQZqId6JasT/PmJTH92mhBYhhTQr0GYFuPX2UJdkw9 +Sn9C67iNBFsisDUBBAC3A+Yo9lgCnxi/pfskyLrweYif6kIXWLAtLTsM6g/6jt7b +wTrknuCPyTv0QKGXsAEe/cK/Xq3HvX9WfXPGIHc/X56ZIsHQ+RLowbZV/Lhok1IW +FAuQm8axr/by80cRwFnzhfPc/ukkAq2Qyj4hLsGblu6mxeAhzcp8aqmWOO2H9QAR +AQABiLYEKAEKACAWIQQ/lRafP/p9PytHbwxMvYJsOQdOOAUCWyK16gIdAAAKCRBM +vYJsOQdOOB1vA/4u4uLONsE+2GVOyBsHyy7uTdkuxaR9b54A/cz6jT/tzUbeIzgx +22neWhgvIEghnUZd0vEyK9k1wy5vbDlEo6nKzHso32N1QExGr5upRERAxweDxGOj +7luDwNypI7QcifE64lS/JmlnunwRCdRWMKc0Fp+7jtRc5mpwyHN/Suf5RokBagQY +AQoAIBYhBD+VFp8/+n0/K0dvDEy9gmw5B044BQJbIrA1AhsCAL8JEEy9gmw5B044 +tCAEGQEKAB0WIQSNdnkaWY6t62iX336UXbGvYdhXJwUCWyKwNQAKCRCUXbGvYdhX +JxJSA/9fCPHP6sUtGF1o3G1a3yvOUDGr1JWcct9U+QpbCt1mZoNopCNDDQAJvDWl +mvDgHfuogmgNJRjOMznvahbF+wpTXmB7LS0SK412gJzl1fFIpK4bgnhu0TwxNsO1 +8UkCZWqxRMgcNUn9z6XWONK8dgt5JNvHSHrwF4CxxwjL23AAtK+FA/UUoi3U4kbC +0XnSr1Sl+mrzQi1+H7xyMe7zjqe+gGANtskqexHzwWPUJCPZ5qpIa2l8ghiUim6b +4ymJ+N8/T8Yva1FaPEqfMzzqJr8McYFm0URioXJPvOAlRxdHPteZ0qUopt/Jawxl +Xt6B9h1YpeLoJwjwsvbi98UTRs0jXwoY +=3fWu +-----END PGP PUBLIC KEY BLOCK-----` + +const keyWithSubKeyAndBadSelfSigOrder = `-----BEGIN PGP PUBLIC KEY BLOCK----- + +mI0EWyLLDQEEAOqIOpJ/ha1OYAGduu9tS3rBz5vyjbNgJO4sFveEM0mgsHQ0X9/L +plonW+d0gRoO1dhJ8QICjDAc6+cna1DE3tEb5m6JtQ30teLZuqrR398Cf6w7NNVz +r3lrlmnH9JaKRuXl7tZciwyovneBfZVCdtsRZjaLI1uMQCz/BToiYe3DABEBAAG0 +I0dvbGFuZyBHb3BoZXIgPG5vLXJlcGx5QGdvbGFuZy5jb20+iM4EEwEKADgWIQRZ +sixZOfQcZdW0wUqmgmdsv1O9xgUCWyLLDQIbAwULCQgHAwUVCgkICwUWAgMBAAIe +AQIXgAAKCRCmgmdsv1O9xql2A/4pix98NxjhdsXtazA9agpAKeADf9tG4Za27Gj+ +3DCww/E4iP2X35jZimSm/30QRB6j08uGCqd9vXkkJxtOt63y/IpVOtWX6vMWSTUm +k8xKkaYMP0/IzKNJ1qC/qYEUYpwERBKg9Z+k99E2Ql4kRHdxXUHq6OzY79H18Y+s +GdeM/riNBFsiyxsBBAC54Pxg/8ZWaZX1phGdwfe5mek27SOYpC0AxIDCSOdMeQ6G +HPk38pywl1d+S+KmF/F4Tdi+kWro62O4eG2uc/T8JQuRDUhSjX0Qa51gPzJrUOVT +CFyUkiZ/3ZDhtXkgfuso8ua2ChBgR9Ngr4v43tSqa9y6AK7v0qjxD1x+xMrjXQAR +AQABiQFxBBgBCgAmAhsCFiEEWbIsWTn0HGXVtMFKpoJnbL9TvcYFAlsizTIFCQAN +MRcAv7QgBBkBCgAdFiEEJcoVUVJIk5RWj1c/o62jUpRPICQFAlsiyxsACgkQo62j +UpRPICQq5gQApoWIigZxXFoM0uw4uJBS5JFZtirTANvirZV5RhndwHeMN6JttaBS +YnjyA4+n1D+zB2VqliD2QrsX12KJN6rGOehCtEIClQ1Hodo9nC6kMzzAwW1O8bZs +nRJmXV+bsvD4sidLZLjdwOVa3Cxh6pvq4Uur6a7/UYx121hEY0Qx0s8JEKaCZ2y/ +U73GGi0D/i20VW8AWYAPACm2zMlzExKTOAV01YTQH/3vW0WLrOse53WcIVZga6es +HuO4So0SOEAvxKMe5HpRIu2dJxTvd99Bo9xk9xJU0AoFrO0vNCRnL+5y68xMlODK +lEw5/kl0jeaTBp6xX0HDQOEVOpPGUwWV4Ij2EnvfNDXaE1vK1kffiQFrBBgBCgAg +AhsCFiEEWbIsWTn0HGXVtMFKpoJnbL9TvcYFAlsi0AYAv7QgBBkBCgAdFiEEJcoV +UVJIk5RWj1c/o62jUpRPICQFAlsiyxsACgkQo62jUpRPICQq5gQApoWIigZxXFoM +0uw4uJBS5JFZtirTANvirZV5RhndwHeMN6JttaBSYnjyA4+n1D+zB2VqliD2QrsX +12KJN6rGOehCtEIClQ1Hodo9nC6kMzzAwW1O8bZsnRJmXV+bsvD4sidLZLjdwOVa +3Cxh6pvq4Uur6a7/UYx121hEY0Qx0s8JEKaCZ2y/U73GRl0EAJokkXmy4zKDHWWi +wvK9gi2gQgRkVnu2AiONxJb5vjeLhM/07BRmH6K1o+w3fOeEQp4FjXj1eQ5fPSM6 +Hhwx2CTl9SDnPSBMiKXsEFRkmwQ2AAsQZLmQZvKBkLZYeBiwf+IY621eYDhZfo+G +1dh1WoUCyREZsJQg2YoIpWIcvw+a +=bNRo +-----END PGP PUBLIC KEY BLOCK----- +` + +const onlySubkeyNoPrivateKey = `-----BEGIN PGP PRIVATE KEY BLOCK----- +Version: GnuPG v1 + +lQCVBFggvocBBAC7vBsHn7MKmS6IiiZNTXdciplVgS9cqVd+RTdIAoyNTcsiV1H0 +GQ3QtodOPeDlQDNoqinqaobd7R9g3m3hS53Nor7yBZkCWQ5x9v9JxRtoAq0sklh1 +I1X2zEqZk2l6YrfBF/64zWrhjnW3j23szkrAIVu0faQXbQ4z56tmZrw11wARAQAB +/gdlAkdOVQG0CUdOVSBEdW1teYi4BBMBAgAiBQJYIL6HAhsDBgsJCAcDAgYVCAIJ +CgsEFgIDAQIeAQIXgAAKCRCd1xxWp1CYAnjGA/9synn6ZXJUKAXQzySgmCZvCIbl +rqBfEpxwLG4Q/lONhm5vthAE0z49I8hj5Gc5e2tLYUtq0o0OCRdCrYHa/efOYWpJ +6RsK99bePOisVzmOABLIgZkcr022kHoMCmkPgv9CUGKP1yqbGl+zzAwQfUjRUmvD +ZIcWLHi2ge4GzPMPi50B2ARYIL6cAQQAxWHnicKejAFcFcF1/3gUSgSH7eiwuBPX +M7vDdgGzlve1o1jbV4tzrjN9jsCl6r0nJPDMfBSzgLr1auNTRG6HpJ4abcOx86ED +Ad+avDcQPZb7z3dPhH/gb2lQejZsHh7bbeOS8WMSzHV3RqCLd8J/xwWPNR5zKn1f +yp4IGfopidMAEQEAAQAD+wQOelnR82+dxyM2IFmZdOB9wSXQeCVOvxSaNMh6Y3lk +UOOkO8Nlic4x0ungQRvjoRs4wBmCuwFK/MII6jKui0B7dn/NDf51i7rGdNGuJXDH +e676By1sEY/NGkc74jr74T+5GWNU64W0vkpfgVmjSAzsUtpmhJMXsc7beBhJdnVl +AgDKCb8hZqj1alcdmLoNvb7ibA3K/V8J462CPD7bMySPBa/uayoFhNxibpoXml2r +oOtHa5izF3b0/9JY97F6rqkdAgD6GdTJ+xmlCoz1Sewoif1I6krq6xoa7gOYpIXo +UL1Afr+LiJeyAnF/M34j/kjIVmPanZJjry0kkjHE5ILjH3uvAf4/6n9np+Th8ujS +YDCIzKwR7639+H+qccOaddCep8Y6KGUMVdD/vTKEx1rMtK+hK/CDkkkxnFslifMJ +kqoqv3WUqCWJAT0EGAECAAkFAlggvpwCGwIAqAkQndccVqdQmAKdIAQZAQIABgUC +WCC+nAAKCRDmGUholQPwvQk+A/9latnSsR5s5/1A9TFki11GzSEnfLbx46FYOdkW +n3YBxZoPQGxNA1vIn8GmouxZInw9CF4jdOJxEdzLlYQJ9YLTLtN5tQEMl/19/bR8 +/qLacAZ9IOezYRWxxZsyn6//jfl7A0Y+FV59d4YajKkEfItcIIlgVBSW6T+TNQT3 +R+EH5HJ/A/4/AN0CmBhhE2vGzTnVU0VPrE4V64pjn1rufFdclgpixNZCuuqpKpoE +VVHn6mnBf4njKjZrAGPs5kfQ+H4NsM7v3Zz4yV6deu9FZc4O6E+V1WJ38rO8eBix +7G2jko106CC6vtxsCPVIzY7aaG3H5pjRtomw+pX7SzrQ7FUg2PGumg== +=F/T0 +-----END PGP PRIVATE KEY BLOCK-----` + +const ecdsaPrivateKey = `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xaUEX1KsSRMIKoZIzj0DAQcCAwTpYqJsnJiFhKKh+8TulWD+lVmerBFNS+Ii +B+nlG3T0xQQ4Sy5eIjJ0CExIQQzi3EElF/Z2l4F3WC5taFA11NgA/gkDCHSS +PThf1M2K4LN8F1MRcvR+sb7i0nH55ojkwuVB1DE6jqIT9m9i+mX1tzjSAS+6 +lPQiweCJvG7xTC7Hs3AzRapf/r1At4TB+v+5G2/CKynNFEJpbGwgPGJpbGxA +aG9tZS5jb20+wncEEBMIAB8FAl9SrEkGCwkHCAMCBBUICgIDFgIBAhkBAhsD +Ah4BAAoJEMpwT3+q3+xqw5UBAMebZN9isEZ1ML+R/jWAAWMwa/knMugrEZ1v +Bl9+ZwM0AQCZdf80/wYY4Nve01qSRFv8OmKswLli3TvDv6FKc4cLz8epBF9S +rEkSCCqGSM49AwEHAgMEAjKnT9b5wY2bf9TpAV3d7OUfPOxKj9c4VzeVzSrH +AtQgo/MuI1cdYVURicV4i76DNjFhQHQFTk7BrC+C2u1yqQMBCAf+CQMIHImA +iYfzQtjgQWSFZYUkCFpbbwhNF0ch+3HNaZkaHCnZRIsWsRnc6FCb6lRQyK9+ +Dq59kHlduE5QgY40894jfmP2JdJHU6nBdYrivbEdbMJhBBgTCAAJBQJfUqxJ +AhsMAAoJEMpwT3+q3+xqUI0BAMykhV08kQ4Ip9Qlbss6Jdufv7YrU0Vd5hou +b5TmiPd0APoDBh3qIic+aLLUcAuG3+Gt1P1AbUlmqV61ozn1WfHxfw== +=KLN8 +-----END PGP PRIVATE KEY BLOCK-----` + +const dsaPrivateKeyWithElGamalSubkey = `-----BEGIN PGP PRIVATE KEY BLOCK----- + +lQOBBF9/MLsRCACeaF6BI0jTgDAs86t8/kXPfwlPvR2MCYzB0BCqAdcq1hV/GTYd +oNmJRna/ZJfsI/vf+d8Nv+EYOQkPheFS1MJVBitkAXjQPgm8i1tQWen1FCWZxqGk +/vwZYF4yo8GhZ+Wxi3w09W9Cp9QM/CTmyE1Xe7wpPBGe+oD+me8Zxjyt8JBS4Qx+ +gvWbfHxfHnggh4pz7U8QkItlLsBNQEdX4R5+zwRN66g2ZSX/shaa/EkVnihUhD7r +njP9I51ORWucTQD6OvgooaNQZCkQ/Se9TzdakwWKS2XSIFXiY/e2E5ZgKI/pfKDU +iA/KessxddPb7nP/05OIJqg9AoDrD4vmehLzAQD+zsUS3LDU1m9/cG4LMsQbT2VK +Te4HqbGIAle+eu/asQf8DDJMrbZpiJZvADum9j0TJ0oep6VdMbzo9RSDKvlLKT9m +kG63H8oDWnCZm1a+HmGq9YIX+JHWmsLXXsFLeEouLzHO+mZo0X28eji3V2T87hyR +MmUM0wFo4k7jK8uVmkDXv3XwNp2uByWxUKZd7EnWmcEZWqIiexJ7XpCS0Pg3tRaI +zxve0SRe/dxfUPnTk/9KQ9hS6DWroBKquL182zx1Fggh4LIWWE2zq+UYn8BI0E8A +rmIDFJdF8ymFQGRrEy6g79NnkPmkrZWsgMRYY65P6v4zLVmqohJKkpm3/Uxa6QAP +CCoPh/JTOvPeCP2bOJH8z4Z9Py3ouMIjofQW8sXqRgf/RIHbh0KsINHrwwZ4gVIr +MK3RofpaYxw1ztPIWb4cMWoWZHH1Pxh7ggTGSBpAhKXkiWw2Rxat8QF5aA7e962c +bLvVv8dqsPrD/RnVJHag89cbPTzjn7gY9elE8EM8ithV3oQkwHTr4avYlpDZsgNd +hUW3YgRwGo31tdzxoG04AcpV2t+07P8XMPr9hsfWs4rHohXPi38Hseu1Ji+dBoWQ +3+1w/HH3o55s+jy4Ruaz78AIrjbmAJq+6rA2mIcCgrhw3DnzuwQAKeBvSeqn9zfS +ZC812osMBVmkycwelpaIh64WZ0vWL3GvdXDctV2kXM+qVpDTLEny0LuiXxrwCKQL +Ev4HAwK9uQBcreDEEud7pfRb8EYP5lzO2ZA7RaIvje6EWAGBvJGMRT0QQE5SGqc7 +Fw5geigBdt+vVyRuNNhg3c2fdn/OBQaYu0J/8AiOogG8EaM8tCFlbGdhbWFsQGRz +YS5jb20gPGVsZ2FtYWxAZHNhLmNvbT6IkAQTEQgAOBYhBI+gnfiHQxB35/Dp0XAQ +aE/rsWC5BQJffzC7AhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEHAQaE/r +sWC5A4EA/0GcJmyPtN+Klc7b9sVT3JgKTRnB/URxOJfYJofP0hZLAQCkqyMO+adV +JvbgDH0zaITQWZSSXPqpgMpCA6juTrDsd50CawRffzC7EAgAxFFFSAAEQzWTgKU5 +EBtpxxoPzHqcChawTHRxHxjcELXzmUBS5PzfA1HXSPnNqK/x3Ut5ycC3CsW41Fnt +Gm3706Wu9VFbFZVn55F9lPiplUo61n5pqMvOr1gmuQsdXiTa0t5FRa4TZ2VSiHFw +vdAVSPTUsT4ZxJ1rPyFYRtq1n3pQcvdZowd07r0JnzTMjLLMFYCKhwIowoOC4zqJ +iB8enjwOlpaqBATRm9xpVF7SJkroPF6/B1vdhj7E3c1aJyHlo0PYBAg756sSHWHg +UuLyUQ4TA0hcCVenn/L/aSY2LnbdZB1EBhlYjA7dTCgwIqsQhfQmPkjz6g64A7+Y +HbbrLwADBQgAk14QIEQ+J/VHetpQV/jt2pNsFK1kVK7mXK0spTExaC2yj2sXlHjL +Ie3bO5T/KqmIaBEB5db5fA5xK9cZt79qrQHDKsEqUetUeMUWLBx77zBsus3grIgy +bwDZKseRzQ715pwxquxQlScGoDIBKEh08HpwHkq140eIj3w+MAIfndaZaSCNaxaP +Snky7BQmJ7Wc7qrIwoQP6yrnUqyW2yNi81nJYUhxjChqaFSlwzLs/iNGryBKo0ic +BqVIRjikKHBlwBng6WyrltQo/Vt9GG8w+lqaAVXbJRlaBZJUR+2NKi/YhP3qQse3 +v8fi4kns0gh5LK+2C01RvdX4T49QSExuIf4HAwLJqYIGwadA2uem5v7/765ZtFWV +oL0iZ0ueTJDby4wTFDpLVzzDi/uVcB0ZRFrGOp7w6OYcNYTtV8n3xmli2Q5Trw0c +wZVzvg+ABKWiv7faBjMczIFF8y6WZKOIeAQYEQgAIBYhBI+gnfiHQxB35/Dp0XAQ +aE/rsWC5BQJffzC7AhsMAAoJEHAQaE/rsWC5ZmIA/jhS4r4lClbvjuPWt0Yqdn7R +fss2SPMYvMrrDh42aE0OAQD8xn4G6CN8UtW9xihXOY6FpxiJ/sMc2VaneeUd34oa +4g== +=XZm8 +-----END PGP PRIVATE KEY BLOCK-----` + +// https://tests.sequoia-pgp.org/#Certificate_expiration +// P _ U p +const expiringPrimaryUIDKey = `-----BEGIN PGP PUBLIC KEY BLOCK----- + +xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv +/seOXpgecTdOcVttfzC8ycIKrt3aQTiwOG/ctaR4Bk/t6ayNFfdUNxHWk4WCKzdz +/56fW2O0F23qIRd8UUJp5IIlN4RDdRCtdhVQIAuzvp2oVy/LaS2kxQoKvph/5pQ/ +5whqsyroEWDJoSV0yOb25B/iwk/pLUFoyhDG9bj0kIzDxrEqW+7Ba8nocQlecMF3 +X5KMN5kp2zraLv9dlBBpWW43XktjcCZgMy20SouraVma8Je/ECwUWYUiAZxLIlMv +9CurEOtxUw6N3RdOtLmYZS9uEnn5y1UkF88o8Nku890uk6BrewFzJyLAx5wRZ4F0 +qV/yq36UWQ0JB/AUGhHVPdFf6pl6eaxBwT5GXvbBUibtf8YI2og5RsgTWtXfU7eb +SGXrl5ZMpbA6mbfhd0R8aPxWfmDWiIOhBufhMCvUHh1sApMKVZnvIff9/0Dca3wb +vLIwa3T4CyshfT0AEQEAAc0hQm9iIEJhYmJhZ2UgPGJvYkBvcGVucGdwLmV4YW1w +bGU+wsEUBBMBCgBIBYJfiWKKBYkBX+/UBQsJCAcCCRD7/MgqAV5zMAYVCgkICwIE +FgIDAQIXgAIbAwIeARYhBNGmbhojsYLJmA94jPv8yCoBXnMwAAAf0wv/a++a3DkL +CttbK4LRIiSry2wb97mxYQoWYzvkKYD1IP/KiamRwzjBKuRSE0qZ2uonQDRGc+zl +1H9dwkDLL4T9uJngCCPCBgFW/hOFPvF+WYEKHtOzunqx6KwDHkpdH+hpzfFzIhDo +aXiVnDGvJ3H/bVTKq1m2KPXO2ckkXPQXJX9Fx6kPHdvcq+3ZI75IzbD/ue5lBcsy +OKLzVu+KLxzlzGui6v1V0fTvU/uqvHlvUxcDAqMnIsDUPakjK2RDeQ38qtN6+PQ5 +/dT7vx2Wtzesqn2eDbDf5uRfSgmp2hJLJniAKjMCBVAJiOgPb0LXUIcwCGxaiWOA +g5ZvNWjX5bZ/FxqpLpOE9OReI5YY7ns7zqP4thLYYWe0Qdp9a2ezVrgzgrh/SLla +d73x/S9TrmLtYGGlbVUByJW+GXjW2Tt6iaa/WDFzx8NvZ/wzIAdGSEfLcvS+JBSP +2ppdY5Ac/2dK3PzYABkHvB/rhXIwlXnrFDU9efRHZfFqQqGauA64wfdIzsDNBF2l +nPIBDADWML9cbGMrp12CtF9b2P6z9TTT74S8iyBOzaSvdGDQY/sUtZXRg21HWamX +nn9sSXvIDEINOQ6A9QxdxoqWdCHrOuW3ofneYXoG+zeKc4dC86wa1TR2q9vW+RMX +SO4uImA+Uzula/6k1DogDf28qhCxMwG/i/m9g1c/0aApuDyKdQ1PXsHHNlgd/Dn6 +rrd5y2AObaifV7wIhEJnvqgFXDN2RXGjLeCOHV4Q2WTYPg/S4k1nMXVDwZXrvIsA +0YwIMgIT86Rafp1qKlgPNbiIlC1g9RY/iFaGN2b4Ir6GDohBQSfZW2+LXoPZuVE/ +wGlQ01rh827KVZW4lXvqsge+wtnWlszcselGATyzqOK9LdHPdZGzROZYI2e8c+pa +LNDdVPL6vdRBUnkCaEkOtl1mr2JpQi5nTU+gTX4IeInC7E+1a9UDF/Y85ybUz8XV +8rUnR76UqVC7KidNepdHbZjjXCt8/Zo+Tec9JNbYNQB/e9ExmDntmlHEsSEQzFwz +j8sxH48AEQEAAcLA9gQYAQoAIBYhBNGmbhojsYLJmA94jPv8yCoBXnMwBQJdpZzy +AhsMAAoJEPv8yCoBXnMw6f8L/26C34dkjBffTzMj5Bdzm8MtF67OYneJ4TQMw7+4 +1IL4rVcSKhIhk/3Ud5knaRtP2ef1+5F66h9/RPQOJ5+tvBwhBAcUWSupKnUrdVaZ +QanYmtSxcVV2PL9+QEiNN3tzluhaWO//rACxJ+K/ZXQlIzwQVTpNhfGzAaMVV9zp +f3u0k14itcv6alKY8+rLZvO1wIIeRZLmU0tZDD5HtWDvUV7rIFI1WuoLb+KZgbYn +3OWjCPHVdTrdZ2CqnZbG3SXw6awH9bzRLV9EXkbhIMez0deCVdeo+wFFklh8/5VK +2b0vk/+wqMJxfpa1lHvJLobzOP9fvrswsr92MA2+k901WeISR7qEzcI0Fdg8AyFA +ExaEK6VyjP7SXGLwvfisw34OxuZr3qmx1Sufu4toH3XrB7QJN8XyqqbsGxUCBqWi +f9RSK4xjzRTe56iPeiSJJOIciMP9i2ldI+KgLycyeDvGoBj0HCLO3gVaBe4ubVrj +5KjhX2PVNEJd3XZRzaXZE2aAMQ== +=522n +-----END PGP PUBLIC KEY BLOCK-----` diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_config.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_config.go new file mode 100644 index 0000000000000..7350974effc1c --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_config.go @@ -0,0 +1,56 @@ +// Copyright (C) 2019 ProtonTech AG + +package packet + +import "math/bits" + +// AEADConfig collects a number of AEAD parameters along with sensible defaults. +// A nil AEADConfig is valid and results in all default values. +type AEADConfig struct { + // The AEAD mode of operation. + DefaultMode AEADMode + // Amount of octets in each chunk of data + ChunkSize uint64 +} + +// Mode returns the AEAD mode of operation. +func (conf *AEADConfig) Mode() AEADMode { + if conf == nil || conf.DefaultMode == 0 { + return AEADModeEAX + } + mode := conf.DefaultMode + if mode != AEADModeEAX && mode != AEADModeOCB && + mode != AEADModeExperimentalGCM { + panic("AEAD mode unsupported") + } + return mode +} + +// ChunkSizeByte returns the byte indicating the chunk size. The effective +// chunk size is computed with the formula uint64(1) << (chunkSizeByte + 6) +func (conf *AEADConfig) ChunkSizeByte() byte { + if conf == nil || conf.ChunkSize == 0 { + return 12 // 1 << (12 + 6) == 262144 bytes + } + + chunkSize := conf.ChunkSize + exponent := bits.Len64(chunkSize) - 1 + switch { + case exponent < 6: + exponent = 6 + case exponent > 27: + exponent = 27 + } + + return byte(exponent - 6) +} + +// decodeAEADChunkSize returns the effective chunk size. In 32-bit systems, the +// maximum returned value is 1 << 30. +func decodeAEADChunkSize(c byte) int { + size := uint64(1 << (c + 6)) + if size != uint64(int(size)) { + return 1 << 30 + } + return int(size) +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_encrypted.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_encrypted.go new file mode 100644 index 0000000000000..85c53f08aa51a --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_encrypted.go @@ -0,0 +1,364 @@ +// Copyright (C) 2019 ProtonTech AG + +package packet + +import ( + "bytes" + "crypto/cipher" + "crypto/rand" + "encoding/binary" + "io" + + "github.com/ProtonMail/go-crypto/openpgp/errors" + "github.com/ProtonMail/go-crypto/openpgp/internal/algorithm" +) + +// AEADEncrypted represents an AEAD Encrypted Packet (tag 20, RFC4880bis-5.16). +type AEADEncrypted struct { + cipher CipherFunction + mode AEADMode + chunkSizeByte byte + Contents io.Reader // Encrypted chunks and tags + initialNonce []byte // Referred to as IV in RFC4880-bis +} + +// Only currently defined version +const aeadEncryptedVersion = 1 + +// An AEAD opener/sealer, its configuration, and data for en/decryption. +type aeadCrypter struct { + aead cipher.AEAD + chunkSize int + initialNonce []byte + associatedData []byte // Chunk-independent associated data + chunkIndex []byte // Chunk counter + bytesProcessed int // Amount of plaintext bytes encrypted/decrypted + buffer bytes.Buffer // Buffered bytes accross chunks +} + +// aeadEncrypter encrypts and writes bytes. It encrypts when necessary according +// to the AEAD block size, and buffers the extra encrypted bytes for next write. +type aeadEncrypter struct { + aeadCrypter // Embedded plaintext sealer + writer io.WriteCloser // 'writer' is a partialLengthWriter +} + +// aeadDecrypter reads and decrypts bytes. It buffers extra decrypted bytes when +// necessary, similar to aeadEncrypter. +type aeadDecrypter struct { + aeadCrypter // Embedded ciphertext opener + reader io.Reader // 'reader' is a partialLengthReader + peekedBytes []byte // Used to detect last chunk + eof bool +} + +func (ae *AEADEncrypted) parse(buf io.Reader) error { + headerData := make([]byte, 4) + if n, err := io.ReadFull(buf, headerData); n < 4 { + return errors.AEADError("could not read aead header:" + err.Error()) + } + // Read initial nonce + mode := AEADMode(headerData[2]) + nonceLen := mode.NonceLength() + if nonceLen == 0 { + return errors.AEADError("unknown mode") + } + initialNonce := make([]byte, nonceLen) + if n, err := io.ReadFull(buf, initialNonce); n < nonceLen { + return errors.AEADError("could not read aead nonce:" + err.Error()) + } + ae.Contents = buf + ae.initialNonce = initialNonce + c := headerData[1] + if _, ok := algorithm.CipherById[c]; !ok { + return errors.UnsupportedError("unknown cipher: " + string(c)) + } + ae.cipher = CipherFunction(c) + ae.mode = mode + ae.chunkSizeByte = byte(headerData[3]) + return nil +} + +// Decrypt returns a io.ReadCloser from which decrypted bytes can be read, or +// an error. +func (ae *AEADEncrypted) Decrypt(ciph CipherFunction, key []byte) (io.ReadCloser, error) { + return ae.decrypt(key) +} + +// decrypt prepares an aeadCrypter and returns a ReadCloser from which +// decrypted bytes can be read (see aeadDecrypter.Read()). +func (ae *AEADEncrypted) decrypt(key []byte) (io.ReadCloser, error) { + blockCipher := ae.cipher.new(key) + aead := ae.mode.new(blockCipher) + // Carry the first tagLen bytes + tagLen := ae.mode.TagLength() + peekedBytes := make([]byte, tagLen) + n, err := io.ReadFull(ae.Contents, peekedBytes) + if n < tagLen || (err != nil && err != io.EOF) { + return nil, errors.AEADError("Not enough data to decrypt:" + err.Error()) + } + chunkSize := decodeAEADChunkSize(ae.chunkSizeByte) + return &aeadDecrypter{ + aeadCrypter: aeadCrypter{ + aead: aead, + chunkSize: chunkSize, + initialNonce: ae.initialNonce, + associatedData: ae.associatedData(), + chunkIndex: make([]byte, 8), + }, + reader: ae.Contents, + peekedBytes: peekedBytes}, nil +} + +// Read decrypts bytes and reads them into dst. It decrypts when necessary and +// buffers extra decrypted bytes. It returns the number of bytes copied into dst +// and an error. +func (ar *aeadDecrypter) Read(dst []byte) (n int, err error) { + // Return buffered plaintext bytes from previous calls + if ar.buffer.Len() > 0 { + return ar.buffer.Read(dst) + } + + // Return EOF if we've previously validated the final tag + if ar.eof { + return 0, io.EOF + } + + // Read a chunk + tagLen := ar.aead.Overhead() + cipherChunkBuf := new(bytes.Buffer) + _, errRead := io.CopyN(cipherChunkBuf, ar.reader, int64(ar.chunkSize + tagLen)) + cipherChunk := cipherChunkBuf.Bytes() + if errRead != nil && errRead != io.EOF { + return 0, errRead + } + decrypted, errChunk := ar.openChunk(cipherChunk) + if errChunk != nil { + return 0, errChunk + } + + // Return decrypted bytes, buffering if necessary + if len(dst) < len(decrypted) { + n = copy(dst, decrypted[:len(dst)]) + ar.buffer.Write(decrypted[len(dst):]) + } else { + n = copy(dst, decrypted) + } + + // Check final authentication tag + if errRead == io.EOF { + errChunk := ar.validateFinalTag(ar.peekedBytes) + if errChunk != nil { + return n, errChunk + } + ar.eof = true // Mark EOF for when we've returned all buffered data + } + return +} + +// Close is noOp. The final authentication tag of the stream was already +// checked in the last Read call. In the future, this function could be used to +// wipe the reader and peeked, decrypted bytes, if necessary. +func (ar *aeadDecrypter) Close() (err error) { + return nil +} + +// SerializeAEADEncrypted initializes the aeadCrypter and returns a writer. +// This writer encrypts and writes bytes (see aeadEncrypter.Write()). +func SerializeAEADEncrypted(w io.Writer, key []byte, cipher CipherFunction, mode AEADMode, config *Config) (io.WriteCloser, error) { + writeCloser := noOpCloser{w} + writer, err := serializeStreamHeader(writeCloser, packetTypeAEADEncrypted) + if err != nil { + return nil, err + } + + // Data for en/decryption: tag, version, cipher, aead mode, chunk size + aeadConf := config.AEAD() + prefix := []byte{ + 0xD4, + aeadEncryptedVersion, + byte(config.Cipher()), + byte(aeadConf.Mode()), + aeadConf.ChunkSizeByte(), + } + n, err := writer.Write(prefix[1:]) + if err != nil || n < 4 { + return nil, errors.AEADError("could not write AEAD headers") + } + // Sample nonce + nonceLen := aeadConf.Mode().NonceLength() + nonce := make([]byte, nonceLen) + n, err = rand.Read(nonce) + if err != nil { + panic("Could not sample random nonce") + } + _, err = writer.Write(nonce) + if err != nil { + return nil, err + } + blockCipher := CipherFunction(config.Cipher()).new(key) + alg := AEADMode(aeadConf.Mode()).new(blockCipher) + + chunkSize := decodeAEADChunkSize(aeadConf.ChunkSizeByte()) + return &aeadEncrypter{ + aeadCrypter: aeadCrypter{ + aead: alg, + chunkSize: chunkSize, + associatedData: prefix, + chunkIndex: make([]byte, 8), + initialNonce: nonce, + }, + writer: writer}, nil +} + +// Write encrypts and writes bytes. It encrypts when necessary and buffers extra +// plaintext bytes for next call. When the stream is finished, Close() MUST be +// called to append the final tag. +func (aw *aeadEncrypter) Write(plaintextBytes []byte) (n int, err error) { + // Append plaintextBytes to existing buffered bytes + n, err = aw.buffer.Write(plaintextBytes) + if err != nil { + return n, err + } + // Encrypt and write chunks + for aw.buffer.Len() >= aw.chunkSize { + plainChunk := aw.buffer.Next(aw.chunkSize) + encryptedChunk, err := aw.sealChunk(plainChunk) + if err != nil { + return n, err + } + _, err = aw.writer.Write(encryptedChunk) + if err != nil { + return n, err + } + } + return +} + +// Close encrypts and writes the remaining buffered plaintext if any, appends +// the final authentication tag, and closes the embedded writer. This function +// MUST be called at the end of a stream. +func (aw *aeadEncrypter) Close() (err error) { + // Encrypt and write a chunk if there's buffered data left, or if we haven't + // written any chunks yet. + if aw.buffer.Len() > 0 || aw.bytesProcessed == 0 { + plainChunk := aw.buffer.Bytes() + lastEncryptedChunk, err := aw.sealChunk(plainChunk) + if err != nil { + return err + } + _, err = aw.writer.Write(lastEncryptedChunk) + if err != nil { + return err + } + } + // Compute final tag (associated data: packet tag, version, cipher, aead, + // chunk size, index, total number of encrypted octets). + adata := append(aw.associatedData[:], aw.chunkIndex[:]...) + adata = append(adata, make([]byte, 8)...) + binary.BigEndian.PutUint64(adata[13:], uint64(aw.bytesProcessed)) + nonce := aw.computeNextNonce() + finalTag := aw.aead.Seal(nil, nonce, nil, adata) + _, err = aw.writer.Write(finalTag) + if err != nil { + return err + } + return aw.writer.Close() +} + +// sealChunk Encrypts and authenticates the given chunk. +func (aw *aeadEncrypter) sealChunk(data []byte) ([]byte, error) { + if len(data) > aw.chunkSize { + return nil, errors.AEADError("chunk exceeds maximum length") + } + if aw.associatedData == nil { + return nil, errors.AEADError("can't seal without headers") + } + adata := append(aw.associatedData, aw.chunkIndex...) + nonce := aw.computeNextNonce() + encrypted := aw.aead.Seal(nil, nonce, data, adata) + aw.bytesProcessed += len(data) + if err := aw.aeadCrypter.incrementIndex(); err != nil { + return nil, err + } + return encrypted, nil +} + +// openChunk decrypts and checks integrity of an encrypted chunk, returning +// the underlying plaintext and an error. It access peeked bytes from next +// chunk, to identify the last chunk and decrypt/validate accordingly. +func (ar *aeadDecrypter) openChunk(data []byte) ([]byte, error) { + tagLen := ar.aead.Overhead() + // Restore carried bytes from last call + chunkExtra := append(ar.peekedBytes, data...) + // 'chunk' contains encrypted bytes, followed by an authentication tag. + chunk := chunkExtra[:len(chunkExtra)-tagLen] + ar.peekedBytes = chunkExtra[len(chunkExtra)-tagLen:] + adata := append(ar.associatedData, ar.chunkIndex...) + nonce := ar.computeNextNonce() + plainChunk, err := ar.aead.Open(nil, nonce, chunk, adata) + if err != nil { + return nil, err + } + ar.bytesProcessed += len(plainChunk) + if err = ar.aeadCrypter.incrementIndex(); err != nil { + return nil, err + } + return plainChunk, nil +} + +// Checks the summary tag. It takes into account the total decrypted bytes into +// the associated data. It returns an error, or nil if the tag is valid. +func (ar *aeadDecrypter) validateFinalTag(tag []byte) error { + // Associated: tag, version, cipher, aead, chunk size, index, and octets + amountBytes := make([]byte, 8) + binary.BigEndian.PutUint64(amountBytes, uint64(ar.bytesProcessed)) + adata := append(ar.associatedData, ar.chunkIndex...) + adata = append(adata, amountBytes...) + nonce := ar.computeNextNonce() + _, err := ar.aead.Open(nil, nonce, tag, adata) + if err != nil { + return err + } + return nil +} + +// Associated data for chunks: tag, version, cipher, mode, chunk size byte +func (ae *AEADEncrypted) associatedData() []byte { + return []byte{ + 0xD4, + aeadEncryptedVersion, + byte(ae.cipher), + byte(ae.mode), + ae.chunkSizeByte} +} + +// computeNonce takes the incremental index and computes an eXclusive OR with +// the least significant 8 bytes of the receivers' initial nonce (see sec. +// 5.16.1 and 5.16.2). It returns the resulting nonce. +func (wo *aeadCrypter) computeNextNonce() (nonce []byte) { + nonce = make([]byte, len(wo.initialNonce)) + copy(nonce, wo.initialNonce) + offset := len(wo.initialNonce) - 8 + for i := 0; i < 8; i++ { + nonce[i+offset] ^= wo.chunkIndex[i] + } + return +} + +// incrementIndex perfoms an integer increment by 1 of the integer represented by the +// slice, modifying it accordingly. +func (wo *aeadCrypter) incrementIndex() error { + index := wo.chunkIndex + if len(index) == 0 { + return errors.AEADError("Index has length 0") + } + for i := len(index) - 1; i >= 0; i-- { + if index[i] < 255 { + index[i]++ + return nil + } + index[i] = 0 + } + return errors.AEADError("cannot further increment index") +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/compressed.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/compressed.go new file mode 100644 index 0000000000000..c55cecd357721 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/compressed.go @@ -0,0 +1,123 @@ +// Copyright 2011 The 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 packet + +import ( + "compress/bzip2" + "compress/flate" + "compress/zlib" + "github.com/ProtonMail/go-crypto/openpgp/errors" + "io" + "strconv" +) + +// Compressed represents a compressed OpenPGP packet. The decompressed contents +// will contain more OpenPGP packets. See RFC 4880, section 5.6. +type Compressed struct { + Body io.Reader +} + +const ( + NoCompression = flate.NoCompression + BestSpeed = flate.BestSpeed + BestCompression = flate.BestCompression + DefaultCompression = flate.DefaultCompression +) + +// CompressionConfig contains compressor configuration settings. +type CompressionConfig struct { + // Level is the compression level to use. It must be set to + // between -1 and 9, with -1 causing the compressor to use the + // default compression level, 0 causing the compressor to use + // no compression and 1 to 9 representing increasing (better, + // slower) compression levels. If Level is less than -1 or + // more then 9, a non-nil error will be returned during + // encryption. See the constants above for convenient common + // settings for Level. + Level int +} + +func (c *Compressed) parse(r io.Reader) error { + var buf [1]byte + _, err := readFull(r, buf[:]) + if err != nil { + return err + } + + switch buf[0] { + case 1: + c.Body = flate.NewReader(r) + case 2: + c.Body, err = zlib.NewReader(r) + case 3: + c.Body = bzip2.NewReader(r) + default: + err = errors.UnsupportedError("unknown compression algorithm: " + strconv.Itoa(int(buf[0]))) + } + + return err +} + +// compressedWriterCloser represents the serialized compression stream +// header and the compressor. Its Close() method ensures that both the +// compressor and serialized stream header are closed. Its Write() +// method writes to the compressor. +type compressedWriteCloser struct { + sh io.Closer // Stream Header + c io.WriteCloser // Compressor +} + +func (cwc compressedWriteCloser) Write(p []byte) (int, error) { + return cwc.c.Write(p) +} + +func (cwc compressedWriteCloser) Close() (err error) { + err = cwc.c.Close() + if err != nil { + return err + } + + return cwc.sh.Close() +} + +// SerializeCompressed serializes a compressed data packet to w and +// returns a WriteCloser to which the literal data packets themselves +// can be written and which MUST be closed on completion. If cc is +// nil, sensible defaults will be used to configure the compression +// algorithm. +func SerializeCompressed(w io.WriteCloser, algo CompressionAlgo, cc *CompressionConfig) (literaldata io.WriteCloser, err error) { + compressed, err := serializeStreamHeader(w, packetTypeCompressed) + if err != nil { + return + } + + _, err = compressed.Write([]byte{uint8(algo)}) + if err != nil { + return + } + + level := DefaultCompression + if cc != nil { + level = cc.Level + } + + var compressor io.WriteCloser + switch algo { + case CompressionZIP: + compressor, err = flate.NewWriter(compressed, level) + case CompressionZLIB: + compressor, err = zlib.NewWriterLevel(compressed, level) + default: + s := strconv.Itoa(int(algo)) + err = errors.UnsupportedError("Unsupported compression algorithm: " + s) + } + if err != nil { + return + } + + literaldata = compressedWriteCloser{compressed, compressor} + + return +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config.go new file mode 100644 index 0000000000000..5652dd8148569 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config.go @@ -0,0 +1,167 @@ +// Copyright 2012 The 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 packet + +import ( + "crypto" + "crypto/rand" + "io" + "math/big" + "time" +) + +// Config collects a number of parameters along with sensible defaults. +// A nil *Config is valid and results in all default values. +type Config struct { + // Rand provides the source of entropy. + // If nil, the crypto/rand Reader is used. + Rand io.Reader + // DefaultHash is the default hash function to be used. + // If zero, SHA-256 is used. + DefaultHash crypto.Hash + // DefaultCipher is the cipher to be used. + // If zero, AES-128 is used. + DefaultCipher CipherFunction + // Time returns the current time as the number of seconds since the + // epoch. If Time is nil, time.Now is used. + Time func() time.Time + // DefaultCompressionAlgo is the compression algorithm to be + // applied to the plaintext before encryption. If zero, no + // compression is done. + DefaultCompressionAlgo CompressionAlgo + // CompressionConfig configures the compression settings. + CompressionConfig *CompressionConfig + // S2KCount is only used for symmetric encryption. It + // determines the strength of the passphrase stretching when + // the said passphrase is hashed to produce a key. S2KCount + // should be between 1024 and 65011712, inclusive. If Config + // is nil or S2KCount is 0, the value 65536 used. Not all + // values in the above range can be represented. S2KCount will + // be rounded up to the next representable value if it cannot + // be encoded exactly. When set, it is strongly encrouraged to + // use a value that is at least 65536. See RFC 4880 Section + // 3.7.1.3. + S2KCount int + // RSABits is the number of bits in new RSA keys made with NewEntity. + // If zero, then 2048 bit keys are created. + RSABits int + // The public key algorithm to use - will always create a signing primary + // key and encryption subkey. + Algorithm PublicKeyAlgorithm + // Some known primes that are optionally prepopulated by the caller + RSAPrimes []*big.Int + // AEADConfig configures the use of the new AEAD Encrypted Data Packet, + // defined in the draft of the next version of the OpenPGP specification. + // If a non-nil AEADConfig is passed, usage of this packet is enabled. By + // default, it is disabled. See the documentation of AEADConfig for more + // configuration options related to AEAD. + // **Note: using this option may break compatibility with other OpenPGP + // implementations, as well as future versions of this library.** + AEADConfig *AEADConfig + // V5Keys configures version 5 key generation. If false, this package still + // supports version 5 keys, but produces version 4 keys. + V5Keys bool + // "The validity period of the key. This is the number of seconds after + // the key creation time that the key expires. If this is not present + // or has a value of zero, the key never expires. This is found only on + // a self-signature."" + // https://tools.ietf.org/html/rfc4880#section-5.2.3.6 + KeyLifetimeSecs uint32 + // "The validity period of the signature. This is the number of seconds + // after the signature creation time that the signature expires. If + // this is not present or has a value of zero, it never expires." + // https://tools.ietf.org/html/rfc4880#section-5.2.3.10 + SigLifetimeSecs uint32 + // SigningKeyId is used to specify the signing key to use (by Key ID). + // By default, the signing key is selected automatically, preferring + // signing subkeys if available. + SigningKeyId uint64 +} + +func (c *Config) Random() io.Reader { + if c == nil || c.Rand == nil { + return rand.Reader + } + return c.Rand +} + +func (c *Config) Hash() crypto.Hash { + if c == nil || uint(c.DefaultHash) == 0 { + return crypto.SHA256 + } + return c.DefaultHash +} + +func (c *Config) Cipher() CipherFunction { + if c == nil || uint8(c.DefaultCipher) == 0 { + return CipherAES128 + } + return c.DefaultCipher +} + +func (c *Config) Now() time.Time { + if c == nil || c.Time == nil { + return time.Now() + } + return c.Time() +} + +// KeyLifetime returns the validity period of the key. +func (c *Config) KeyLifetime() uint32 { + if c == nil { + return 0 + } + return c.KeyLifetimeSecs +} + +// SigLifetime returns the validity period of the signature. +func (c *Config) SigLifetime() uint32 { + if c == nil { + return 0 + } + return c.SigLifetimeSecs +} + +func (c *Config) Compression() CompressionAlgo { + if c == nil { + return CompressionNone + } + return c.DefaultCompressionAlgo +} + +func (c *Config) PasswordHashIterations() int { + if c == nil || c.S2KCount == 0 { + return 0 + } + return c.S2KCount +} + +func (c *Config) RSAModulusBits() int { + if c == nil || c.RSABits == 0 { + return 2048 + } + return c.RSABits +} + +func (c *Config) PublicKeyAlgorithm() PublicKeyAlgorithm { + if c == nil || c.Algorithm == 0 { + return PubKeyAlgoRSA + } + return c.Algorithm +} + +func (c *Config) AEAD() *AEADConfig { + if c == nil { + return nil + } + return c.AEADConfig +} + +func (c *Config) SigningKey() uint64 { + if c == nil { + return 0 + } + return c.SigningKeyId +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/encrypted_key.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/encrypted_key.go new file mode 100644 index 0000000000000..801aec92b49a7 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/encrypted_key.go @@ -0,0 +1,282 @@ +// Copyright 2011 The 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 packet + +import ( + "crypto" + "crypto/rsa" + "encoding/binary" + "io" + "math/big" + "strconv" + + "github.com/ProtonMail/go-crypto/openpgp/ecdh" + "github.com/ProtonMail/go-crypto/openpgp/elgamal" + "github.com/ProtonMail/go-crypto/openpgp/errors" + "github.com/ProtonMail/go-crypto/openpgp/internal/encoding" +) + +const encryptedKeyVersion = 3 + +// EncryptedKey represents a public-key encrypted session key. See RFC 4880, +// section 5.1. +type EncryptedKey struct { + KeyId uint64 + Algo PublicKeyAlgorithm + CipherFunc CipherFunction // only valid after a successful Decrypt + Key []byte // only valid after a successful Decrypt + + encryptedMPI1, encryptedMPI2 encoding.Field +} + +func (e *EncryptedKey) parse(r io.Reader) (err error) { + var buf [10]byte + _, err = readFull(r, buf[:]) + if err != nil { + return + } + if buf[0] != encryptedKeyVersion { + return errors.UnsupportedError("unknown EncryptedKey version " + strconv.Itoa(int(buf[0]))) + } + e.KeyId = binary.BigEndian.Uint64(buf[1:9]) + e.Algo = PublicKeyAlgorithm(buf[9]) + switch e.Algo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: + e.encryptedMPI1 = new(encoding.MPI) + if _, err = e.encryptedMPI1.ReadFrom(r); err != nil { + return + } + case PubKeyAlgoElGamal: + e.encryptedMPI1 = new(encoding.MPI) + if _, err = e.encryptedMPI1.ReadFrom(r); err != nil { + return + } + + e.encryptedMPI2 = new(encoding.MPI) + if _, err = e.encryptedMPI2.ReadFrom(r); err != nil { + return + } + case PubKeyAlgoECDH: + e.encryptedMPI1 = new(encoding.MPI) + if _, err = e.encryptedMPI1.ReadFrom(r); err != nil { + return + } + + e.encryptedMPI2 = new(encoding.OID) + if _, err = e.encryptedMPI2.ReadFrom(r); err != nil { + return + } + } + _, err = consumeAll(r) + return +} + +func checksumKeyMaterial(key []byte) uint16 { + var checksum uint16 + for _, v := range key { + checksum += uint16(v) + } + return checksum +} + +// Decrypt decrypts an encrypted session key with the given private key. The +// private key must have been decrypted first. +// If config is nil, sensible defaults will be used. +func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error { + if e.KeyId != 0 && e.KeyId != priv.KeyId { + return errors.InvalidArgumentError("cannot decrypt encrypted session key for key id " + strconv.FormatUint(e.KeyId, 16) + " with private key id " + strconv.FormatUint(priv.KeyId, 16)) + } + if e.Algo != priv.PubKeyAlgo { + return errors.InvalidArgumentError("cannot decrypt encrypted session key of type " + strconv.Itoa(int(e.Algo)) + " with private key of type " + strconv.Itoa(int(priv.PubKeyAlgo))) + } + if priv.Dummy() { + return errors.ErrDummyPrivateKey("dummy key found") + } + + var err error + var b []byte + + // TODO(agl): use session key decryption routines here to avoid + // padding oracle attacks. + switch priv.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: + // Supports both *rsa.PrivateKey and crypto.Decrypter + k := priv.PrivateKey.(crypto.Decrypter) + b, err = k.Decrypt(config.Random(), padToKeySize(k.Public().(*rsa.PublicKey), e.encryptedMPI1.Bytes()), nil) + case PubKeyAlgoElGamal: + c1 := new(big.Int).SetBytes(e.encryptedMPI1.Bytes()) + c2 := new(big.Int).SetBytes(e.encryptedMPI2.Bytes()) + b, err = elgamal.Decrypt(priv.PrivateKey.(*elgamal.PrivateKey), c1, c2) + case PubKeyAlgoECDH: + vsG := e.encryptedMPI1.Bytes() + m := e.encryptedMPI2.Bytes() + oid := priv.PublicKey.oid.EncodedBytes() + b, err = ecdh.Decrypt(priv.PrivateKey.(*ecdh.PrivateKey), vsG, m, oid, priv.PublicKey.Fingerprint[:]) + default: + err = errors.InvalidArgumentError("cannot decrypt encrypted session key with private key of type " + strconv.Itoa(int(priv.PubKeyAlgo))) + } + + if err != nil { + return err + } + + e.CipherFunc = CipherFunction(b[0]) + e.Key = b[1 : len(b)-2] + expectedChecksum := uint16(b[len(b)-2])<<8 | uint16(b[len(b)-1]) + checksum := checksumKeyMaterial(e.Key) + if checksum != expectedChecksum { + return errors.StructuralError("EncryptedKey checksum incorrect") + } + + return nil +} + +// Serialize writes the encrypted key packet, e, to w. +func (e *EncryptedKey) Serialize(w io.Writer) error { + var mpiLen int + switch e.Algo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: + mpiLen = int(e.encryptedMPI1.EncodedLength()) + case PubKeyAlgoElGamal: + mpiLen = int(e.encryptedMPI1.EncodedLength()) + int(e.encryptedMPI2.EncodedLength()) + case PubKeyAlgoECDH: + mpiLen = int(e.encryptedMPI1.EncodedLength()) + int(e.encryptedMPI2.EncodedLength()) + default: + return errors.InvalidArgumentError("don't know how to serialize encrypted key type " + strconv.Itoa(int(e.Algo))) + } + + err := serializeHeader(w, packetTypeEncryptedKey, 1 /* version */ +8 /* key id */ +1 /* algo */ +mpiLen) + if err != nil { + return err + } + + w.Write([]byte{encryptedKeyVersion}) + binary.Write(w, binary.BigEndian, e.KeyId) + w.Write([]byte{byte(e.Algo)}) + + switch e.Algo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: + _, err := w.Write(e.encryptedMPI1.EncodedBytes()) + return err + case PubKeyAlgoElGamal: + if _, err := w.Write(e.encryptedMPI1.EncodedBytes()); err != nil { + return err + } + _, err := w.Write(e.encryptedMPI2.EncodedBytes()) + return err + case PubKeyAlgoECDH: + if _, err := w.Write(e.encryptedMPI1.EncodedBytes()); err != nil { + return err + } + _, err := w.Write(e.encryptedMPI2.EncodedBytes()) + return err + default: + panic("internal error") + } +} + +// SerializeEncryptedKey serializes an encrypted key packet to w that contains +// key, encrypted to pub. +// If config is nil, sensible defaults will be used. +func SerializeEncryptedKey(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, key []byte, config *Config) error { + var buf [10]byte + buf[0] = encryptedKeyVersion + binary.BigEndian.PutUint64(buf[1:9], pub.KeyId) + buf[9] = byte(pub.PubKeyAlgo) + + keyBlock := make([]byte, 1 /* cipher type */ +len(key)+2 /* checksum */) + keyBlock[0] = byte(cipherFunc) + copy(keyBlock[1:], key) + checksum := checksumKeyMaterial(key) + keyBlock[1+len(key)] = byte(checksum >> 8) + keyBlock[1+len(key)+1] = byte(checksum) + + switch pub.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: + return serializeEncryptedKeyRSA(w, config.Random(), buf, pub.PublicKey.(*rsa.PublicKey), keyBlock) + case PubKeyAlgoElGamal: + return serializeEncryptedKeyElGamal(w, config.Random(), buf, pub.PublicKey.(*elgamal.PublicKey), keyBlock) + case PubKeyAlgoECDH: + return serializeEncryptedKeyECDH(w, config.Random(), buf, pub.PublicKey.(*ecdh.PublicKey), keyBlock, pub.oid, pub.Fingerprint) + case PubKeyAlgoDSA, PubKeyAlgoRSASignOnly: + return errors.InvalidArgumentError("cannot encrypt to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo))) + } + + return errors.UnsupportedError("encrypting a key to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo))) +} + +func serializeEncryptedKeyRSA(w io.Writer, rand io.Reader, header [10]byte, pub *rsa.PublicKey, keyBlock []byte) error { + cipherText, err := rsa.EncryptPKCS1v15(rand, pub, keyBlock) + if err != nil { + return errors.InvalidArgumentError("RSA encryption failed: " + err.Error()) + } + + cipherMPI := encoding.NewMPI(cipherText) + packetLen := 10 /* header length */ + int(cipherMPI.EncodedLength()) + + err = serializeHeader(w, packetTypeEncryptedKey, packetLen) + if err != nil { + return err + } + _, err = w.Write(header[:]) + if err != nil { + return err + } + _, err = w.Write(cipherMPI.EncodedBytes()) + return err +} + +func serializeEncryptedKeyElGamal(w io.Writer, rand io.Reader, header [10]byte, pub *elgamal.PublicKey, keyBlock []byte) error { + c1, c2, err := elgamal.Encrypt(rand, pub, keyBlock) + if err != nil { + return errors.InvalidArgumentError("ElGamal encryption failed: " + err.Error()) + } + + packetLen := 10 /* header length */ + packetLen += 2 /* mpi size */ + (c1.BitLen()+7)/8 + packetLen += 2 /* mpi size */ + (c2.BitLen()+7)/8 + + err = serializeHeader(w, packetTypeEncryptedKey, packetLen) + if err != nil { + return err + } + _, err = w.Write(header[:]) + if err != nil { + return err + } + if _, err = w.Write(new(encoding.MPI).SetBig(c1).EncodedBytes()); err != nil { + return err + } + _, err = w.Write(new(encoding.MPI).SetBig(c2).EncodedBytes()) + return err +} + +func serializeEncryptedKeyECDH(w io.Writer, rand io.Reader, header [10]byte, pub *ecdh.PublicKey, keyBlock []byte, oid encoding.Field, fingerprint []byte) error { + vsG, c, err := ecdh.Encrypt(rand, pub, keyBlock, oid.EncodedBytes(), fingerprint) + if err != nil { + return errors.InvalidArgumentError("ECDH encryption failed: " + err.Error()) + } + + g := encoding.NewMPI(vsG) + m := encoding.NewOID(c) + + packetLen := 10 /* header length */ + packetLen += int(g.EncodedLength()) + int(m.EncodedLength()) + + err = serializeHeader(w, packetTypeEncryptedKey, packetLen) + if err != nil { + return err + } + + _, err = w.Write(header[:]) + if err != nil { + return err + } + if _, err = w.Write(g.EncodedBytes()); err != nil { + return err + } + _, err = w.Write(m.EncodedBytes()) + return err +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/literal.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/literal.go new file mode 100644 index 0000000000000..4be987609be56 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/literal.go @@ -0,0 +1,91 @@ +// Copyright 2011 The 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 packet + +import ( + "encoding/binary" + "io" +) + +// LiteralData represents an encrypted file. See RFC 4880, section 5.9. +type LiteralData struct { + Format uint8 + IsBinary bool + FileName string + Time uint32 // Unix epoch time. Either creation time or modification time. 0 means undefined. + Body io.Reader +} + +// ForEyesOnly returns whether the contents of the LiteralData have been marked +// as especially sensitive. +func (l *LiteralData) ForEyesOnly() bool { + return l.FileName == "_CONSOLE" +} + +func (l *LiteralData) parse(r io.Reader) (err error) { + var buf [256]byte + + _, err = readFull(r, buf[:2]) + if err != nil { + return + } + + l.Format = buf[0] + l.IsBinary = l.Format == 'b' + fileNameLen := int(buf[1]) + + _, err = readFull(r, buf[:fileNameLen]) + if err != nil { + return + } + + l.FileName = string(buf[:fileNameLen]) + + _, err = readFull(r, buf[:4]) + if err != nil { + return + } + + l.Time = binary.BigEndian.Uint32(buf[:4]) + l.Body = r + return +} + +// SerializeLiteral serializes a literal data packet to w and returns a +// WriteCloser to which the data itself can be written and which MUST be closed +// on completion. The fileName is truncated to 255 bytes. +func SerializeLiteral(w io.WriteCloser, isBinary bool, fileName string, time uint32) (plaintext io.WriteCloser, err error) { + var buf [4]byte + buf[0] = 't' + if isBinary { + buf[0] = 'b' + } + if len(fileName) > 255 { + fileName = fileName[:255] + } + buf[1] = byte(len(fileName)) + + inner, err := serializeStreamHeader(w, packetTypeLiteralData) + if err != nil { + return + } + + _, err = inner.Write(buf[:2]) + if err != nil { + return + } + _, err = inner.Write([]byte(fileName)) + if err != nil { + return + } + binary.BigEndian.PutUint32(buf[:], time) + _, err = inner.Write(buf[:]) + if err != nil { + return + } + + plaintext = inner + return +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/ocfb.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/ocfb.go new file mode 100644 index 0000000000000..4f26d0a00b7a4 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/ocfb.go @@ -0,0 +1,137 @@ +// Copyright 2010 The 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. + +// OpenPGP CFB Mode. http://tools.ietf.org/html/rfc4880#section-13.9 + +package packet + +import ( + "crypto/cipher" +) + +type ocfbEncrypter struct { + b cipher.Block + fre []byte + outUsed int +} + +// An OCFBResyncOption determines if the "resynchronization step" of OCFB is +// performed. +type OCFBResyncOption bool + +const ( + OCFBResync OCFBResyncOption = true + OCFBNoResync OCFBResyncOption = false +) + +// NewOCFBEncrypter returns a cipher.Stream which encrypts data with OpenPGP's +// cipher feedback mode using the given cipher.Block, and an initial amount of +// ciphertext. randData must be random bytes and be the same length as the +// cipher.Block's block size. Resync determines if the "resynchronization step" +// from RFC 4880, 13.9 step 7 is performed. Different parts of OpenPGP vary on +// this point. +func NewOCFBEncrypter(block cipher.Block, randData []byte, resync OCFBResyncOption) (cipher.Stream, []byte) { + blockSize := block.BlockSize() + if len(randData) != blockSize { + return nil, nil + } + + x := &ocfbEncrypter{ + b: block, + fre: make([]byte, blockSize), + outUsed: 0, + } + prefix := make([]byte, blockSize+2) + + block.Encrypt(x.fre, x.fre) + for i := 0; i < blockSize; i++ { + prefix[i] = randData[i] ^ x.fre[i] + } + + block.Encrypt(x.fre, prefix[:blockSize]) + prefix[blockSize] = x.fre[0] ^ randData[blockSize-2] + prefix[blockSize+1] = x.fre[1] ^ randData[blockSize-1] + + if resync { + block.Encrypt(x.fre, prefix[2:]) + } else { + x.fre[0] = prefix[blockSize] + x.fre[1] = prefix[blockSize+1] + x.outUsed = 2 + } + return x, prefix +} + +func (x *ocfbEncrypter) XORKeyStream(dst, src []byte) { + for i := 0; i < len(src); i++ { + if x.outUsed == len(x.fre) { + x.b.Encrypt(x.fre, x.fre) + x.outUsed = 0 + } + + x.fre[x.outUsed] ^= src[i] + dst[i] = x.fre[x.outUsed] + x.outUsed++ + } +} + +type ocfbDecrypter struct { + b cipher.Block + fre []byte + outUsed int +} + +// NewOCFBDecrypter returns a cipher.Stream which decrypts data with OpenPGP's +// cipher feedback mode using the given cipher.Block. Prefix must be the first +// blockSize + 2 bytes of the ciphertext, where blockSize is the cipher.Block's +// block size. On successful exit, blockSize+2 bytes of decrypted data are written into +// prefix. Resync determines if the "resynchronization step" from RFC 4880, +// 13.9 step 7 is performed. Different parts of OpenPGP vary on this point. +func NewOCFBDecrypter(block cipher.Block, prefix []byte, resync OCFBResyncOption) cipher.Stream { + blockSize := block.BlockSize() + if len(prefix) != blockSize+2 { + return nil + } + + x := &ocfbDecrypter{ + b: block, + fre: make([]byte, blockSize), + outUsed: 0, + } + prefixCopy := make([]byte, len(prefix)) + copy(prefixCopy, prefix) + + block.Encrypt(x.fre, x.fre) + for i := 0; i < blockSize; i++ { + prefixCopy[i] ^= x.fre[i] + } + + block.Encrypt(x.fre, prefix[:blockSize]) + prefixCopy[blockSize] ^= x.fre[0] + prefixCopy[blockSize+1] ^= x.fre[1] + + if resync { + block.Encrypt(x.fre, prefix[2:]) + } else { + x.fre[0] = prefix[blockSize] + x.fre[1] = prefix[blockSize+1] + x.outUsed = 2 + } + copy(prefix, prefixCopy) + return x +} + +func (x *ocfbDecrypter) XORKeyStream(dst, src []byte) { + for i := 0; i < len(src); i++ { + if x.outUsed == len(x.fre) { + x.b.Encrypt(x.fre, x.fre) + x.outUsed = 0 + } + + c := src[i] + dst[i] = x.fre[x.outUsed] ^ src[i] + x.fre[x.outUsed] = c + x.outUsed++ + } +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/one_pass_signature.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/one_pass_signature.go new file mode 100644 index 0000000000000..41c35de219db2 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/one_pass_signature.go @@ -0,0 +1,73 @@ +// Copyright 2011 The 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 packet + +import ( + "crypto" + "encoding/binary" + "github.com/ProtonMail/go-crypto/openpgp/errors" + "github.com/ProtonMail/go-crypto/openpgp/s2k" + "io" + "strconv" +) + +// OnePassSignature represents a one-pass signature packet. See RFC 4880, +// section 5.4. +type OnePassSignature struct { + SigType SignatureType + Hash crypto.Hash + PubKeyAlgo PublicKeyAlgorithm + KeyId uint64 + IsLast bool +} + +const onePassSignatureVersion = 3 + +func (ops *OnePassSignature) parse(r io.Reader) (err error) { + var buf [13]byte + + _, err = readFull(r, buf[:]) + if err != nil { + return + } + if buf[0] != onePassSignatureVersion { + err = errors.UnsupportedError("one-pass-signature packet version " + strconv.Itoa(int(buf[0]))) + } + + var ok bool + ops.Hash, ok = s2k.HashIdToHash(buf[2]) + if !ok { + return errors.UnsupportedError("hash function: " + strconv.Itoa(int(buf[2]))) + } + + ops.SigType = SignatureType(buf[1]) + ops.PubKeyAlgo = PublicKeyAlgorithm(buf[3]) + ops.KeyId = binary.BigEndian.Uint64(buf[4:12]) + ops.IsLast = buf[12] != 0 + return +} + +// Serialize marshals the given OnePassSignature to w. +func (ops *OnePassSignature) Serialize(w io.Writer) error { + var buf [13]byte + buf[0] = onePassSignatureVersion + buf[1] = uint8(ops.SigType) + var ok bool + buf[2], ok = s2k.HashToHashId(ops.Hash) + if !ok { + return errors.UnsupportedError("hash type: " + strconv.Itoa(int(ops.Hash))) + } + buf[3] = uint8(ops.PubKeyAlgo) + binary.BigEndian.PutUint64(buf[4:12], ops.KeyId) + if ops.IsLast { + buf[12] = 1 + } + + if err := serializeHeader(w, packetTypeOnePassSignature, len(buf)); err != nil { + return err + } + _, err := w.Write(buf[:]) + return err +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/opaque.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/opaque.go new file mode 100644 index 0000000000000..e3879199e6bbe --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/opaque.go @@ -0,0 +1,162 @@ +// Copyright 2012 The 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 packet + +import ( + "bytes" + "io" + "io/ioutil" + + "github.com/ProtonMail/go-crypto/openpgp/errors" +) + +// OpaquePacket represents an OpenPGP packet as raw, unparsed data. This is +// useful for splitting and storing the original packet contents separately, +// handling unsupported packet types or accessing parts of the packet not yet +// implemented by this package. +type OpaquePacket struct { + // Packet type + Tag uint8 + // Reason why the packet was parsed opaquely + Reason error + // Binary contents of the packet data + Contents []byte +} + +func (op *OpaquePacket) parse(r io.Reader) (err error) { + op.Contents, err = ioutil.ReadAll(r) + return +} + +// Serialize marshals the packet to a writer in its original form, including +// the packet header. +func (op *OpaquePacket) Serialize(w io.Writer) (err error) { + err = serializeHeader(w, packetType(op.Tag), len(op.Contents)) + if err == nil { + _, err = w.Write(op.Contents) + } + return +} + +// Parse attempts to parse the opaque contents into a structure supported by +// this package. If the packet is not known then the result will be another +// OpaquePacket. +func (op *OpaquePacket) Parse() (p Packet, err error) { + hdr := bytes.NewBuffer(nil) + err = serializeHeader(hdr, packetType(op.Tag), len(op.Contents)) + if err != nil { + op.Reason = err + return op, err + } + p, err = Read(io.MultiReader(hdr, bytes.NewBuffer(op.Contents))) + if err != nil { + op.Reason = err + p = op + } + return +} + +// OpaqueReader reads OpaquePackets from an io.Reader. +type OpaqueReader struct { + r io.Reader +} + +func NewOpaqueReader(r io.Reader) *OpaqueReader { + return &OpaqueReader{r: r} +} + +// Read the next OpaquePacket. +func (or *OpaqueReader) Next() (op *OpaquePacket, err error) { + tag, _, contents, err := readHeader(or.r) + if err != nil { + return + } + op = &OpaquePacket{Tag: uint8(tag), Reason: err} + err = op.parse(contents) + if err != nil { + consumeAll(contents) + } + return +} + +// OpaqueSubpacket represents an unparsed OpenPGP subpacket, +// as found in signature and user attribute packets. +type OpaqueSubpacket struct { + SubType uint8 + Contents []byte +} + +// OpaqueSubpackets extracts opaque, unparsed OpenPGP subpackets from +// their byte representation. +func OpaqueSubpackets(contents []byte) (result []*OpaqueSubpacket, err error) { + var ( + subHeaderLen int + subPacket *OpaqueSubpacket + ) + for len(contents) > 0 { + subHeaderLen, subPacket, err = nextSubpacket(contents) + if err != nil { + break + } + result = append(result, subPacket) + contents = contents[subHeaderLen+len(subPacket.Contents):] + } + return +} + +func nextSubpacket(contents []byte) (subHeaderLen int, subPacket *OpaqueSubpacket, err error) { + // RFC 4880, section 5.2.3.1 + var subLen uint32 + if len(contents) < 1 { + goto Truncated + } + subPacket = &OpaqueSubpacket{} + switch { + case contents[0] < 192: + subHeaderLen = 2 // 1 length byte, 1 subtype byte + if len(contents) < subHeaderLen { + goto Truncated + } + subLen = uint32(contents[0]) + contents = contents[1:] + case contents[0] < 255: + subHeaderLen = 3 // 2 length bytes, 1 subtype + if len(contents) < subHeaderLen { + goto Truncated + } + subLen = uint32(contents[0]-192)<<8 + uint32(contents[1]) + 192 + contents = contents[2:] + default: + subHeaderLen = 6 // 5 length bytes, 1 subtype + if len(contents) < subHeaderLen { + goto Truncated + } + subLen = uint32(contents[1])<<24 | + uint32(contents[2])<<16 | + uint32(contents[3])<<8 | + uint32(contents[4]) + contents = contents[5:] + } + if subLen > uint32(len(contents)) || subLen == 0 { + goto Truncated + } + subPacket.SubType = contents[0] + subPacket.Contents = contents[1:subLen] + return +Truncated: + err = errors.StructuralError("subpacket truncated") + return +} + +func (osp *OpaqueSubpacket) Serialize(w io.Writer) (err error) { + buf := make([]byte, 6) + n := serializeSubpacketLength(buf, len(osp.Contents)+1) + buf[n] = osp.SubType + if _, err = w.Write(buf[:n+1]); err != nil { + return + } + _, err = w.Write(osp.Contents) + return +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet.go new file mode 100644 index 0000000000000..fe0da9d3685d9 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet.go @@ -0,0 +1,522 @@ +// Copyright 2011 The 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 packet implements parsing and serialization of OpenPGP packets, as +// specified in RFC 4880. +package packet // import "github.com/ProtonMail/go-crypto/openpgp/packet" + +import ( + "bytes" + "crypto/cipher" + "crypto/rsa" + "io" + + "github.com/ProtonMail/go-crypto/openpgp/errors" + "github.com/ProtonMail/go-crypto/openpgp/internal/algorithm" +) + +// readFull is the same as io.ReadFull except that reading zero bytes returns +// ErrUnexpectedEOF rather than EOF. +func readFull(r io.Reader, buf []byte) (n int, err error) { + n, err = io.ReadFull(r, buf) + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + return +} + +// readLength reads an OpenPGP length from r. See RFC 4880, section 4.2.2. +func readLength(r io.Reader) (length int64, isPartial bool, err error) { + var buf [4]byte + _, err = readFull(r, buf[:1]) + if err != nil { + return + } + switch { + case buf[0] < 192: + length = int64(buf[0]) + case buf[0] < 224: + length = int64(buf[0]-192) << 8 + _, err = readFull(r, buf[0:1]) + if err != nil { + return + } + length += int64(buf[0]) + 192 + case buf[0] < 255: + length = int64(1) << (buf[0] & 0x1f) + isPartial = true + default: + _, err = readFull(r, buf[0:4]) + if err != nil { + return + } + length = int64(buf[0])<<24 | + int64(buf[1])<<16 | + int64(buf[2])<<8 | + int64(buf[3]) + } + return +} + +// partialLengthReader wraps an io.Reader and handles OpenPGP partial lengths. +// The continuation lengths are parsed and removed from the stream and EOF is +// returned at the end of the packet. See RFC 4880, section 4.2.2.4. +type partialLengthReader struct { + r io.Reader + remaining int64 + isPartial bool +} + +func (r *partialLengthReader) Read(p []byte) (n int, err error) { + for r.remaining == 0 { + if !r.isPartial { + return 0, io.EOF + } + r.remaining, r.isPartial, err = readLength(r.r) + if err != nil { + return 0, err + } + } + + toRead := int64(len(p)) + if toRead > r.remaining { + toRead = r.remaining + } + + n, err = r.r.Read(p[:int(toRead)]) + r.remaining -= int64(n) + if n < int(toRead) && err == io.EOF { + err = io.ErrUnexpectedEOF + } + return +} + +// partialLengthWriter writes a stream of data using OpenPGP partial lengths. +// See RFC 4880, section 4.2.2.4. +type partialLengthWriter struct { + w io.WriteCloser + buf bytes.Buffer + lengthByte [1]byte +} + +func (w *partialLengthWriter) Write(p []byte) (n int, err error) { + bufLen := w.buf.Len() + if bufLen > 512 { + for power := uint(30); ; power-- { + l := 1 << power + if bufLen >= l { + w.lengthByte[0] = 224 + uint8(power) + _, err = w.w.Write(w.lengthByte[:]) + if err != nil { + return + } + var m int + m, err = w.w.Write(w.buf.Next(l)) + if err != nil { + return + } + if m != l { + return 0, io.ErrShortWrite + } + break + } + } + } + return w.buf.Write(p) +} + +func (w *partialLengthWriter) Close() (err error) { + len := w.buf.Len() + err = serializeLength(w.w, len) + if err != nil { + return err + } + _, err = w.buf.WriteTo(w.w) + if err != nil { + return err + } + return w.w.Close() +} + +// A spanReader is an io.LimitReader, but it returns ErrUnexpectedEOF if the +// underlying Reader returns EOF before the limit has been reached. +type spanReader struct { + r io.Reader + n int64 +} + +func (l *spanReader) Read(p []byte) (n int, err error) { + if l.n <= 0 { + return 0, io.EOF + } + if int64(len(p)) > l.n { + p = p[0:l.n] + } + n, err = l.r.Read(p) + l.n -= int64(n) + if l.n > 0 && err == io.EOF { + err = io.ErrUnexpectedEOF + } + return +} + +// readHeader parses a packet header and returns an io.Reader which will return +// the contents of the packet. See RFC 4880, section 4.2. +func readHeader(r io.Reader) (tag packetType, length int64, contents io.Reader, err error) { + var buf [4]byte + _, err = io.ReadFull(r, buf[:1]) + if err != nil { + return + } + if buf[0]&0x80 == 0 { + err = errors.StructuralError("tag byte does not have MSB set") + return + } + if buf[0]&0x40 == 0 { + // Old format packet + tag = packetType((buf[0] & 0x3f) >> 2) + lengthType := buf[0] & 3 + if lengthType == 3 { + length = -1 + contents = r + return + } + lengthBytes := 1 << lengthType + _, err = readFull(r, buf[0:lengthBytes]) + if err != nil { + return + } + for i := 0; i < lengthBytes; i++ { + length <<= 8 + length |= int64(buf[i]) + } + contents = &spanReader{r, length} + return + } + + // New format packet + tag = packetType(buf[0] & 0x3f) + length, isPartial, err := readLength(r) + if err != nil { + return + } + if isPartial { + contents = &partialLengthReader{ + remaining: length, + isPartial: true, + r: r, + } + length = -1 + } else { + contents = &spanReader{r, length} + } + return +} + +// serializeHeader writes an OpenPGP packet header to w. See RFC 4880, section +// 4.2. +func serializeHeader(w io.Writer, ptype packetType, length int) (err error) { + err = serializeType(w, ptype) + if err != nil { + return + } + return serializeLength(w, length) +} + +// serializeType writes an OpenPGP packet type to w. See RFC 4880, section +// 4.2. +func serializeType(w io.Writer, ptype packetType) (err error) { + var buf [1]byte + buf[0] = 0x80 | 0x40 | byte(ptype) + _, err = w.Write(buf[:]) + return +} + +// serializeLength writes an OpenPGP packet length to w. See RFC 4880, section +// 4.2.2. +func serializeLength(w io.Writer, length int) (err error) { + var buf [5]byte + var n int + + if length < 192 { + buf[0] = byte(length) + n = 1 + } else if length < 8384 { + length -= 192 + buf[0] = 192 + byte(length>>8) + buf[1] = byte(length) + n = 2 + } else { + buf[0] = 255 + buf[1] = byte(length >> 24) + buf[2] = byte(length >> 16) + buf[3] = byte(length >> 8) + buf[4] = byte(length) + n = 5 + } + + _, err = w.Write(buf[:n]) + return +} + +// serializeStreamHeader writes an OpenPGP packet header to w where the +// length of the packet is unknown. It returns a io.WriteCloser which can be +// used to write the contents of the packet. See RFC 4880, section 4.2. +func serializeStreamHeader(w io.WriteCloser, ptype packetType) (out io.WriteCloser, err error) { + err = serializeType(w, ptype) + if err != nil { + return + } + out = &partialLengthWriter{w: w} + return +} + +// Packet represents an OpenPGP packet. Users are expected to try casting +// instances of this interface to specific packet types. +type Packet interface { + parse(io.Reader) error +} + +// consumeAll reads from the given Reader until error, returning the number of +// bytes read. +func consumeAll(r io.Reader) (n int64, err error) { + var m int + var buf [1024]byte + + for { + m, err = r.Read(buf[:]) + n += int64(m) + if err == io.EOF { + err = nil + return + } + if err != nil { + return + } + } +} + +// packetType represents the numeric ids of the different OpenPGP packet types. See +// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-2 +type packetType uint8 + +const ( + packetTypeEncryptedKey packetType = 1 + packetTypeSignature packetType = 2 + packetTypeSymmetricKeyEncrypted packetType = 3 + packetTypeOnePassSignature packetType = 4 + packetTypePrivateKey packetType = 5 + packetTypePublicKey packetType = 6 + packetTypePrivateSubkey packetType = 7 + packetTypeCompressed packetType = 8 + packetTypeSymmetricallyEncrypted packetType = 9 + packetTypeLiteralData packetType = 11 + packetTypeUserId packetType = 13 + packetTypePublicSubkey packetType = 14 + packetTypeUserAttribute packetType = 17 + packetTypeSymmetricallyEncryptedMDC packetType = 18 + packetTypeAEADEncrypted packetType = 20 +) + +// EncryptedDataPacket holds encrypted data. It is currently implemented by +// SymmetricallyEncrypted and AEADEncrypted. +type EncryptedDataPacket interface { + Decrypt(CipherFunction, []byte) (io.ReadCloser, error) +} + +// Read reads a single OpenPGP packet from the given io.Reader. If there is an +// error parsing a packet, the whole packet is consumed from the input. +func Read(r io.Reader) (p Packet, err error) { + tag, _, contents, err := readHeader(r) + if err != nil { + return + } + + switch tag { + case packetTypeEncryptedKey: + p = new(EncryptedKey) + case packetTypeSignature: + p = new(Signature) + case packetTypeSymmetricKeyEncrypted: + p = new(SymmetricKeyEncrypted) + case packetTypeOnePassSignature: + p = new(OnePassSignature) + case packetTypePrivateKey, packetTypePrivateSubkey: + pk := new(PrivateKey) + if tag == packetTypePrivateSubkey { + pk.IsSubkey = true + } + p = pk + case packetTypePublicKey, packetTypePublicSubkey: + isSubkey := tag == packetTypePublicSubkey + p = &PublicKey{IsSubkey: isSubkey} + case packetTypeCompressed: + p = new(Compressed) + case packetTypeSymmetricallyEncrypted: + err = errors.UnsupportedError("Symmetrically encrypted packets without MDC are not supported") + case packetTypeLiteralData: + p = new(LiteralData) + case packetTypeUserId: + p = new(UserId) + case packetTypeUserAttribute: + p = new(UserAttribute) + case packetTypeSymmetricallyEncryptedMDC: + se := new(SymmetricallyEncrypted) + se.MDC = true + p = se + case packetTypeAEADEncrypted: + p = new(AEADEncrypted) + default: + err = errors.UnknownPacketTypeError(tag) + } + if p != nil { + err = p.parse(contents) + } + if err != nil { + consumeAll(contents) + } + return +} + +// SignatureType represents the different semantic meanings of an OpenPGP +// signature. See RFC 4880, section 5.2.1. +type SignatureType uint8 + +const ( + SigTypeBinary SignatureType = 0x00 + SigTypeText = 0x01 + SigTypeGenericCert = 0x10 + SigTypePersonaCert = 0x11 + SigTypeCasualCert = 0x12 + SigTypePositiveCert = 0x13 + SigTypeSubkeyBinding = 0x18 + SigTypePrimaryKeyBinding = 0x19 + SigTypeDirectSignature = 0x1F + SigTypeKeyRevocation = 0x20 + SigTypeSubkeyRevocation = 0x28 +) + +// PublicKeyAlgorithm represents the different public key system specified for +// OpenPGP. See +// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-12 +type PublicKeyAlgorithm uint8 + +const ( + PubKeyAlgoRSA PublicKeyAlgorithm = 1 + PubKeyAlgoElGamal PublicKeyAlgorithm = 16 + PubKeyAlgoDSA PublicKeyAlgorithm = 17 + // RFC 6637, Section 5. + PubKeyAlgoECDH PublicKeyAlgorithm = 18 + PubKeyAlgoECDSA PublicKeyAlgorithm = 19 + // https://www.ietf.org/archive/id/draft-koch-eddsa-for-openpgp-04.txt + PubKeyAlgoEdDSA PublicKeyAlgorithm = 22 + + // Deprecated in RFC 4880, Section 13.5. Use key flags instead. + PubKeyAlgoRSAEncryptOnly PublicKeyAlgorithm = 2 + PubKeyAlgoRSASignOnly PublicKeyAlgorithm = 3 +) + +// CanEncrypt returns true if it's possible to encrypt a message to a public +// key of the given type. +func (pka PublicKeyAlgorithm) CanEncrypt() bool { + switch pka { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElGamal, PubKeyAlgoECDH: + return true + } + return false +} + +// CanSign returns true if it's possible for a public key of the given type to +// sign a message. +func (pka PublicKeyAlgorithm) CanSign() bool { + switch pka { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA, PubKeyAlgoEdDSA: + return true + } + return false +} + +// CipherFunction represents the different block ciphers specified for OpenPGP. See +// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-13 +type CipherFunction algorithm.CipherFunction + +const ( + Cipher3DES CipherFunction = 2 + CipherCAST5 CipherFunction = 3 + CipherAES128 CipherFunction = 7 + CipherAES192 CipherFunction = 8 + CipherAES256 CipherFunction = 9 +) + +// KeySize returns the key size, in bytes, of cipher. +func (cipher CipherFunction) KeySize() int { + return algorithm.CipherFunction(cipher).KeySize() +} + +// blockSize returns the block size, in bytes, of cipher. +func (cipher CipherFunction) blockSize() int { + return algorithm.CipherFunction(cipher).BlockSize() +} + +// new returns a fresh instance of the given cipher. +func (cipher CipherFunction) new(key []byte) (block cipher.Block) { + return algorithm.CipherFunction(cipher).New(key) +} + +// padToKeySize left-pads a MPI with zeroes to match the length of the +// specified RSA public. +func padToKeySize(pub *rsa.PublicKey, b []byte) []byte { + k := (pub.N.BitLen() + 7) / 8 + if len(b) >= k { + return b + } + bb := make([]byte, k) + copy(bb[len(bb)-len(b):], b) + return bb +} + +// CompressionAlgo Represents the different compression algorithms +// supported by OpenPGP (except for BZIP2, which is not currently +// supported). See Section 9.3 of RFC 4880. +type CompressionAlgo uint8 + +const ( + CompressionNone CompressionAlgo = 0 + CompressionZIP CompressionAlgo = 1 + CompressionZLIB CompressionAlgo = 2 +) + +// AEADMode represents the different Authenticated Encryption with Associated +// Data specified for OpenPGP. +type AEADMode algorithm.AEADMode + +const ( + AEADModeEAX AEADMode = 1 + AEADModeOCB AEADMode = 2 + AEADModeExperimentalGCM AEADMode = 100 +) + +func (mode AEADMode) NonceLength() int { + return algorithm.AEADMode(mode).NonceLength() +} + +func (mode AEADMode) TagLength() int { + return algorithm.AEADMode(mode).TagLength() +} + +// new returns a fresh instance of the given mode. +func (mode AEADMode) new(block cipher.Block) cipher.AEAD { + return algorithm.AEADMode(mode).New(block) +} + +// ReasonForRevocation represents a revocation reason code as per RFC4880 +// section 5.2.3.23. +type ReasonForRevocation uint8 + +const ( + NoReason ReasonForRevocation = 0 + KeySuperseded ReasonForRevocation = 1 + KeyCompromised ReasonForRevocation = 2 + KeyRetired ReasonForRevocation = 3 +) diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/private_key.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/private_key.go new file mode 100644 index 0000000000000..854f9c4bbbed4 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/private_key.go @@ -0,0 +1,780 @@ +// Copyright 2011 The 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 packet + +import ( + "bytes" + "crypto" + "crypto/cipher" + "crypto/dsa" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "crypto/sha1" + "fmt" + "io" + "io/ioutil" + "math/big" + "strconv" + "time" + + "github.com/ProtonMail/go-crypto/openpgp/internal/ecc" + "golang.org/x/crypto/curve25519" + + "github.com/ProtonMail/go-crypto/openpgp/ecdh" + "github.com/ProtonMail/go-crypto/openpgp/elgamal" + "github.com/ProtonMail/go-crypto/openpgp/errors" + "github.com/ProtonMail/go-crypto/openpgp/internal/encoding" + "github.com/ProtonMail/go-crypto/openpgp/s2k" + "golang.org/x/crypto/ed25519" +) + +// PrivateKey represents a possibly encrypted private key. See RFC 4880, +// section 5.5.3. +type PrivateKey struct { + PublicKey + Encrypted bool // if true then the private key is unavailable until Decrypt has been called. + encryptedData []byte + cipher CipherFunction + s2k func(out, in []byte) + // An *{rsa|dsa|elgamal|ecdh|ecdsa|ed25519}.PrivateKey or + // crypto.Signer/crypto.Decrypter (Decryptor RSA only). + PrivateKey interface{} + sha1Checksum bool + iv []byte + + // Type of encryption of the S2K packet + // Allowed values are 0 (Not encrypted), 254 (SHA1), or + // 255 (2-byte checksum) + s2kType S2KType + // Full parameters of the S2K packet + s2kParams *s2k.Params +} + +//S2KType s2k packet type +type S2KType uint8 + +const ( + // S2KNON unencrypt + S2KNON S2KType = 0 + // S2KSHA1 sha1 sum check + S2KSHA1 S2KType = 254 + // S2KCHECKSUM sum check + S2KCHECKSUM S2KType = 255 +) + +func NewRSAPrivateKey(creationTime time.Time, priv *rsa.PrivateKey) *PrivateKey { + pk := new(PrivateKey) + pk.PublicKey = *NewRSAPublicKey(creationTime, &priv.PublicKey) + pk.PrivateKey = priv + return pk +} + +func NewDSAPrivateKey(creationTime time.Time, priv *dsa.PrivateKey) *PrivateKey { + pk := new(PrivateKey) + pk.PublicKey = *NewDSAPublicKey(creationTime, &priv.PublicKey) + pk.PrivateKey = priv + return pk +} + +func NewElGamalPrivateKey(creationTime time.Time, priv *elgamal.PrivateKey) *PrivateKey { + pk := new(PrivateKey) + pk.PublicKey = *NewElGamalPublicKey(creationTime, &priv.PublicKey) + pk.PrivateKey = priv + return pk +} + +func NewECDSAPrivateKey(creationTime time.Time, priv *ecdsa.PrivateKey) *PrivateKey { + pk := new(PrivateKey) + pk.PublicKey = *NewECDSAPublicKey(creationTime, &priv.PublicKey) + pk.PrivateKey = priv + return pk +} + +func NewEdDSAPrivateKey(creationTime time.Time, priv *ed25519.PrivateKey) *PrivateKey { + pk := new(PrivateKey) + pub := priv.Public().(ed25519.PublicKey) + pk.PublicKey = *NewEdDSAPublicKey(creationTime, &pub) + pk.PrivateKey = priv + return pk +} + +func NewECDHPrivateKey(creationTime time.Time, priv *ecdh.PrivateKey) *PrivateKey { + pk := new(PrivateKey) + pk.PublicKey = *NewECDHPublicKey(creationTime, &priv.PublicKey) + pk.PrivateKey = priv + return pk +} + +// NewSignerPrivateKey creates a PrivateKey from a crypto.Signer that +// implements RSA, ECDSA or EdDSA. +func NewSignerPrivateKey(creationTime time.Time, signer crypto.Signer) *PrivateKey { + pk := new(PrivateKey) + // In general, the public Keys should be used as pointers. We still + // type-switch on the values, for backwards-compatibility. + switch pubkey := signer.Public().(type) { + case *rsa.PublicKey: + pk.PublicKey = *NewRSAPublicKey(creationTime, pubkey) + case rsa.PublicKey: + pk.PublicKey = *NewRSAPublicKey(creationTime, &pubkey) + case *ecdsa.PublicKey: + pk.PublicKey = *NewECDSAPublicKey(creationTime, pubkey) + case ecdsa.PublicKey: + pk.PublicKey = *NewECDSAPublicKey(creationTime, &pubkey) + case *ed25519.PublicKey: + pk.PublicKey = *NewEdDSAPublicKey(creationTime, pubkey) + case ed25519.PublicKey: + pk.PublicKey = *NewEdDSAPublicKey(creationTime, &pubkey) + default: + panic("openpgp: unknown crypto.Signer type in NewSignerPrivateKey") + } + pk.PrivateKey = signer + return pk +} + +// NewDecrypterPrivateKey creates a PrivateKey from a *{rsa|elgamal|ecdh}.PrivateKey. +func NewDecrypterPrivateKey(creationTime time.Time, decrypter interface{}) *PrivateKey { + pk := new(PrivateKey) + switch priv := decrypter.(type) { + case *rsa.PrivateKey: + pk.PublicKey = *NewRSAPublicKey(creationTime, &priv.PublicKey) + case *elgamal.PrivateKey: + pk.PublicKey = *NewElGamalPublicKey(creationTime, &priv.PublicKey) + case *ecdh.PrivateKey: + pk.PublicKey = *NewECDHPublicKey(creationTime, &priv.PublicKey) + default: + panic("openpgp: unknown decrypter type in NewDecrypterPrivateKey") + } + pk.PrivateKey = decrypter + return pk +} + +func (pk *PrivateKey) parse(r io.Reader) (err error) { + err = (&pk.PublicKey).parse(r) + if err != nil { + return + } + v5 := pk.PublicKey.Version == 5 + + var buf [1]byte + _, err = readFull(r, buf[:]) + if err != nil { + return + } + pk.s2kType = S2KType(buf[0]) + var optCount [1]byte + if v5 { + if _, err = readFull(r, optCount[:]); err != nil { + return + } + } + + switch pk.s2kType { + case S2KNON: + pk.s2k = nil + pk.Encrypted = false + case S2KSHA1, S2KCHECKSUM: + if v5 && pk.s2kType == S2KCHECKSUM { + return errors.StructuralError("wrong s2k identifier for version 5") + } + _, err = readFull(r, buf[:]) + if err != nil { + return + } + pk.cipher = CipherFunction(buf[0]) + pk.s2kParams, err = s2k.ParseIntoParams(r) + if err != nil { + return + } + if pk.s2kParams.Dummy() { + return + } + pk.s2k, err = pk.s2kParams.Function() + if err != nil { + return + } + pk.Encrypted = true + if pk.s2kType == S2KSHA1 { + pk.sha1Checksum = true + } + default: + return errors.UnsupportedError("deprecated s2k function in private key") + } + + if pk.Encrypted { + blockSize := pk.cipher.blockSize() + if blockSize == 0 { + return errors.UnsupportedError("unsupported cipher in private key: " + strconv.Itoa(int(pk.cipher))) + } + pk.iv = make([]byte, blockSize) + _, err = readFull(r, pk.iv) + if err != nil { + return + } + } + + var privateKeyData []byte + if v5 { + var n [4]byte /* secret material four octet count */ + _, err = readFull(r, n[:]) + if err != nil { + return + } + count := uint32(uint32(n[0])<<24 | uint32(n[1])<<16 | uint32(n[2])<<8 | uint32(n[3])) + if !pk.Encrypted { + count = count + 2 /* two octet checksum */ + } + privateKeyData = make([]byte, count) + _, err = readFull(r, privateKeyData) + if err != nil { + return + } + } else { + privateKeyData, err = ioutil.ReadAll(r) + if err != nil { + return + } + } + if !pk.Encrypted { + return pk.parsePrivateKey(privateKeyData) + } + + pk.encryptedData = privateKeyData + return +} + +// Dummy returns true if the private key is a dummy key. This is a GNU extension. +func (pk *PrivateKey) Dummy() bool { + return pk.s2kParams.Dummy() +} + +func mod64kHash(d []byte) uint16 { + var h uint16 + for _, b := range d { + h += uint16(b) + } + return h +} + +func (pk *PrivateKey) Serialize(w io.Writer) (err error) { + contents := bytes.NewBuffer(nil) + err = pk.PublicKey.serializeWithoutHeaders(contents) + if err != nil { + return + } + if _, err = contents.Write([]byte{uint8(pk.s2kType)}); err != nil { + return + } + + optional := bytes.NewBuffer(nil) + if pk.Encrypted || pk.Dummy() { + optional.Write([]byte{uint8(pk.cipher)}) + if err := pk.s2kParams.Serialize(optional); err != nil { + return err + } + if pk.Encrypted { + optional.Write(pk.iv) + } + } + if pk.Version == 5 { + contents.Write([]byte{uint8(optional.Len())}) + } + io.Copy(contents, optional) + + if !pk.Dummy() { + l := 0 + var priv []byte + if !pk.Encrypted { + buf := bytes.NewBuffer(nil) + err = pk.serializePrivateKey(buf) + if err != nil { + return err + } + l = buf.Len() + if pk.sha1Checksum { + h := sha1.New() + h.Write(buf.Bytes()) + buf.Write(h.Sum(nil)) + } else { + checksum := mod64kHash(buf.Bytes()) + buf.Write([]byte{byte(checksum >> 8), byte(checksum)}) + } + priv = buf.Bytes() + } else { + priv, l = pk.encryptedData, len(pk.encryptedData) + } + + if pk.Version == 5 { + contents.Write([]byte{byte(l >> 24), byte(l >> 16), byte(l >> 8), byte(l)}) + } + contents.Write(priv) + } + + ptype := packetTypePrivateKey + if pk.IsSubkey { + ptype = packetTypePrivateSubkey + } + err = serializeHeader(w, ptype, contents.Len()) + if err != nil { + return + } + _, err = io.Copy(w, contents) + if err != nil { + return + } + return +} + +func serializeRSAPrivateKey(w io.Writer, priv *rsa.PrivateKey) error { + if _, err := w.Write(new(encoding.MPI).SetBig(priv.D).EncodedBytes()); err != nil { + return err + } + if _, err := w.Write(new(encoding.MPI).SetBig(priv.Primes[1]).EncodedBytes()); err != nil { + return err + } + if _, err := w.Write(new(encoding.MPI).SetBig(priv.Primes[0]).EncodedBytes()); err != nil { + return err + } + _, err := w.Write(new(encoding.MPI).SetBig(priv.Precomputed.Qinv).EncodedBytes()) + return err +} + +func serializeDSAPrivateKey(w io.Writer, priv *dsa.PrivateKey) error { + _, err := w.Write(new(encoding.MPI).SetBig(priv.X).EncodedBytes()) + return err +} + +func serializeElGamalPrivateKey(w io.Writer, priv *elgamal.PrivateKey) error { + _, err := w.Write(new(encoding.MPI).SetBig(priv.X).EncodedBytes()) + return err +} + +func serializeECDSAPrivateKey(w io.Writer, priv *ecdsa.PrivateKey) error { + _, err := w.Write(new(encoding.MPI).SetBig(priv.D).EncodedBytes()) + return err +} + +func serializeEdDSAPrivateKey(w io.Writer, priv *ed25519.PrivateKey) error { + keySize := ed25519.PrivateKeySize - ed25519.PublicKeySize + _, err := w.Write(encoding.NewMPI((*priv)[:keySize]).EncodedBytes()) + return err +} + +func serializeECDHPrivateKey(w io.Writer, priv *ecdh.PrivateKey) error { + _, err := w.Write(encoding.NewMPI(priv.D).EncodedBytes()) + return err +} + +// Decrypt decrypts an encrypted private key using a passphrase. +func (pk *PrivateKey) Decrypt(passphrase []byte) error { + if pk.Dummy() { + return errors.ErrDummyPrivateKey("dummy key found") + } + if !pk.Encrypted { + return nil + } + + key := make([]byte, pk.cipher.KeySize()) + pk.s2k(key, passphrase) + block := pk.cipher.new(key) + cfb := cipher.NewCFBDecrypter(block, pk.iv) + + data := make([]byte, len(pk.encryptedData)) + cfb.XORKeyStream(data, pk.encryptedData) + + if pk.sha1Checksum { + if len(data) < sha1.Size { + return errors.StructuralError("truncated private key data") + } + h := sha1.New() + h.Write(data[:len(data)-sha1.Size]) + sum := h.Sum(nil) + if !bytes.Equal(sum, data[len(data)-sha1.Size:]) { + return errors.StructuralError("private key checksum failure") + } + data = data[:len(data)-sha1.Size] + } else { + if len(data) < 2 { + return errors.StructuralError("truncated private key data") + } + var sum uint16 + for i := 0; i < len(data)-2; i++ { + sum += uint16(data[i]) + } + if data[len(data)-2] != uint8(sum>>8) || + data[len(data)-1] != uint8(sum) { + return errors.StructuralError("private key checksum failure") + } + data = data[:len(data)-2] + } + + err := pk.parsePrivateKey(data) + if _, ok := err.(errors.KeyInvalidError); ok { + return errors.KeyInvalidError("invalid key parameters") + } + if err != nil { + return err + } + + // Mark key as unencrypted + pk.s2kType = S2KNON + pk.s2k = nil + pk.Encrypted = false + pk.encryptedData = nil + + return nil +} + +// Encrypt encrypts an unencrypted private key using a passphrase. +func (pk *PrivateKey) Encrypt(passphrase []byte) error { + priv := bytes.NewBuffer(nil) + err := pk.serializePrivateKey(priv) + if err != nil { + return err + } + + //Default config of private key encryption + pk.cipher = CipherAES256 + s2kConfig := &s2k.Config{ + S2KMode: 3, //Iterated + S2KCount: 65536, + Hash: crypto.SHA256, + } + + pk.s2kParams, err = s2k.Generate(rand.Reader, s2kConfig) + if err != nil { + return err + } + privateKeyBytes := priv.Bytes() + key := make([]byte, pk.cipher.KeySize()) + + pk.sha1Checksum = true + pk.s2k, err = pk.s2kParams.Function() + if err != nil { + return err + } + pk.s2k(key, passphrase) + block := pk.cipher.new(key) + pk.iv = make([]byte, pk.cipher.blockSize()) + _, err = rand.Read(pk.iv) + if err != nil { + return err + } + cfb := cipher.NewCFBEncrypter(block, pk.iv) + + if pk.sha1Checksum { + pk.s2kType = S2KSHA1 + h := sha1.New() + h.Write(privateKeyBytes) + sum := h.Sum(nil) + privateKeyBytes = append(privateKeyBytes, sum...) + } else { + pk.s2kType = S2KCHECKSUM + var sum uint16 + for _, b := range privateKeyBytes { + sum += uint16(b) + } + priv.Write([]byte{uint8(sum >> 8), uint8(sum)}) + } + + pk.encryptedData = make([]byte, len(privateKeyBytes)) + cfb.XORKeyStream(pk.encryptedData, privateKeyBytes) + pk.Encrypted = true + pk.PrivateKey = nil + return err +} + +func (pk *PrivateKey) serializePrivateKey(w io.Writer) (err error) { + switch priv := pk.PrivateKey.(type) { + case *rsa.PrivateKey: + err = serializeRSAPrivateKey(w, priv) + case *dsa.PrivateKey: + err = serializeDSAPrivateKey(w, priv) + case *elgamal.PrivateKey: + err = serializeElGamalPrivateKey(w, priv) + case *ecdsa.PrivateKey: + err = serializeECDSAPrivateKey(w, priv) + case *ed25519.PrivateKey: + err = serializeEdDSAPrivateKey(w, priv) + case *ecdh.PrivateKey: + err = serializeECDHPrivateKey(w, priv) + default: + err = errors.InvalidArgumentError("unknown private key type") + } + return +} + +func (pk *PrivateKey) parsePrivateKey(data []byte) (err error) { + switch pk.PublicKey.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoRSAEncryptOnly: + return pk.parseRSAPrivateKey(data) + case PubKeyAlgoDSA: + return pk.parseDSAPrivateKey(data) + case PubKeyAlgoElGamal: + return pk.parseElGamalPrivateKey(data) + case PubKeyAlgoECDSA: + return pk.parseECDSAPrivateKey(data) + case PubKeyAlgoECDH: + return pk.parseECDHPrivateKey(data) + case PubKeyAlgoEdDSA: + return pk.parseEdDSAPrivateKey(data) + } + panic("impossible") +} + +func (pk *PrivateKey) parseRSAPrivateKey(data []byte) (err error) { + rsaPub := pk.PublicKey.PublicKey.(*rsa.PublicKey) + rsaPriv := new(rsa.PrivateKey) + rsaPriv.PublicKey = *rsaPub + + buf := bytes.NewBuffer(data) + d := new(encoding.MPI) + if _, err := d.ReadFrom(buf); err != nil { + return err + } + + p := new(encoding.MPI) + if _, err := p.ReadFrom(buf); err != nil { + return err + } + + q := new(encoding.MPI) + if _, err := q.ReadFrom(buf); err != nil { + return err + } + + rsaPriv.D = new(big.Int).SetBytes(d.Bytes()) + rsaPriv.Primes = make([]*big.Int, 2) + rsaPriv.Primes[0] = new(big.Int).SetBytes(p.Bytes()) + rsaPriv.Primes[1] = new(big.Int).SetBytes(q.Bytes()) + if err := rsaPriv.Validate(); err != nil { + return errors.KeyInvalidError(err.Error()) + } + rsaPriv.Precompute() + pk.PrivateKey = rsaPriv + + return nil +} + +func (pk *PrivateKey) parseDSAPrivateKey(data []byte) (err error) { + dsaPub := pk.PublicKey.PublicKey.(*dsa.PublicKey) + dsaPriv := new(dsa.PrivateKey) + dsaPriv.PublicKey = *dsaPub + + buf := bytes.NewBuffer(data) + x := new(encoding.MPI) + if _, err := x.ReadFrom(buf); err != nil { + return err + } + + dsaPriv.X = new(big.Int).SetBytes(x.Bytes()) + if err := validateDSAParameters(dsaPriv); err != nil { + return err + } + pk.PrivateKey = dsaPriv + + return nil +} + +func (pk *PrivateKey) parseElGamalPrivateKey(data []byte) (err error) { + pub := pk.PublicKey.PublicKey.(*elgamal.PublicKey) + priv := new(elgamal.PrivateKey) + priv.PublicKey = *pub + + buf := bytes.NewBuffer(data) + x := new(encoding.MPI) + if _, err := x.ReadFrom(buf); err != nil { + return err + } + + priv.X = new(big.Int).SetBytes(x.Bytes()) + if err := validateElGamalParameters(priv); err != nil { + return err + } + pk.PrivateKey = priv + + return nil +} + +func (pk *PrivateKey) parseECDSAPrivateKey(data []byte) (err error) { + ecdsaPub := pk.PublicKey.PublicKey.(*ecdsa.PublicKey) + ecdsaPriv := new(ecdsa.PrivateKey) + ecdsaPriv.PublicKey = *ecdsaPub + + buf := bytes.NewBuffer(data) + d := new(encoding.MPI) + if _, err := d.ReadFrom(buf); err != nil { + return err + } + + ecdsaPriv.D = new(big.Int).SetBytes(d.Bytes()) + if err := validateECDSAParameters(ecdsaPriv); err != nil { + return err + } + pk.PrivateKey = ecdsaPriv + + return nil +} + +func (pk *PrivateKey) parseECDHPrivateKey(data []byte) (err error) { + ecdhPub := pk.PublicKey.PublicKey.(*ecdh.PublicKey) + ecdhPriv := new(ecdh.PrivateKey) + ecdhPriv.PublicKey = *ecdhPub + + buf := bytes.NewBuffer(data) + d := new(encoding.MPI) + if _, err := d.ReadFrom(buf); err != nil { + return err + } + + ecdhPriv.D = d.Bytes() + if err := validateECDHParameters(ecdhPriv); err != nil { + return err + } + pk.PrivateKey = ecdhPriv + + return nil +} + +func (pk *PrivateKey) parseEdDSAPrivateKey(data []byte) (err error) { + eddsaPub := pk.PublicKey.PublicKey.(*ed25519.PublicKey) + eddsaPriv := make(ed25519.PrivateKey, ed25519.PrivateKeySize) + + buf := bytes.NewBuffer(data) + d := new(encoding.MPI) + if _, err := d.ReadFrom(buf); err != nil { + return err + } + + priv := d.Bytes() + copy(eddsaPriv[32-len(priv):32], priv) + copy(eddsaPriv[32:], (*eddsaPub)[:]) + if err := validateEdDSAParameters(&eddsaPriv); err != nil { + return err + } + pk.PrivateKey = &eddsaPriv + + return nil +} + +func validateECDSAParameters(priv *ecdsa.PrivateKey) error { + return validateCommonECC(priv.Curve, priv.D.Bytes(), priv.X, priv.Y) +} + +func validateECDHParameters(priv *ecdh.PrivateKey) error { + if priv.CurveType != ecc.Curve25519 { + return validateCommonECC(priv.Curve, priv.D, priv.X, priv.Y) + } + // Handle Curve25519 + Q := priv.X.Bytes()[1:] + var d [32]byte + // Copy reversed d + l := len(priv.D) + for i := 0; i < l; i++ { + d[i] = priv.D[l-i-1] + } + var expectedQ [32]byte + curve25519.ScalarBaseMult(&expectedQ, &d) + if !bytes.Equal(Q, expectedQ[:]) { + return errors.KeyInvalidError("ECDH curve25519: invalid point") + } + return nil +} + +func validateCommonECC(curve elliptic.Curve, d []byte, X, Y *big.Int) error { + // the public point should not be at infinity (0,0) + zero := new(big.Int) + if X.Cmp(zero) == 0 && Y.Cmp(zero) == 0 { + return errors.KeyInvalidError(fmt.Sprintf("ecc (%s): infinity point", curve.Params().Name)) + } + // re-derive the public point Q' = (X,Y) = dG + // to compare to declared Q in public key + expectedX, expectedY := curve.ScalarBaseMult(d) + if X.Cmp(expectedX) != 0 || Y.Cmp(expectedY) != 0 { + return errors.KeyInvalidError(fmt.Sprintf("ecc (%s): invalid point", curve.Params().Name)) + } + return nil +} + +func validateEdDSAParameters(priv *ed25519.PrivateKey) error { + // In EdDSA, the serialized public point is stored as part of private key (together with the seed), + // hence we can re-derive the key from the seed + seed := priv.Seed() + expectedPriv := ed25519.NewKeyFromSeed(seed) + if !bytes.Equal(*priv, expectedPriv) { + return errors.KeyInvalidError("eddsa: invalid point") + } + return nil +} + +func validateDSAParameters(priv *dsa.PrivateKey) error { + p := priv.P // group prime + q := priv.Q // subgroup order + g := priv.G // g has order q mod p + x := priv.X // secret + y := priv.Y // y == g**x mod p + one := big.NewInt(1) + // expect g, y >= 2 and g < p + if g.Cmp(one) <= 0 || y.Cmp(one) <= 0 || g.Cmp(p) > 0 { + return errors.KeyInvalidError("dsa: invalid group") + } + // expect p > q + if p.Cmp(q) <= 0 { + return errors.KeyInvalidError("dsa: invalid group prime") + } + // q should be large enough and divide p-1 + pSub1 := new(big.Int).Sub(p, one) + if q.BitLen() < 150 || new(big.Int).Mod(pSub1, q).Cmp(big.NewInt(0)) != 0 { + return errors.KeyInvalidError("dsa: invalid order") + } + // confirm that g has order q mod p + if !q.ProbablyPrime(32) || new(big.Int).Exp(g, q, p).Cmp(one) != 0 { + return errors.KeyInvalidError("dsa: invalid order") + } + // check y + if new(big.Int).Exp(g, x, p).Cmp(y) != 0 { + return errors.KeyInvalidError("dsa: mismatching values") + } + + return nil +} + +func validateElGamalParameters(priv *elgamal.PrivateKey) error { + p := priv.P // group prime + g := priv.G // g has order p-1 mod p + x := priv.X // secret + y := priv.Y // y == g**x mod p + one := big.NewInt(1) + // Expect g, y >= 2 and g < p + if g.Cmp(one) <= 0 || y.Cmp(one) <= 0 || g.Cmp(p) > 0 { + return errors.KeyInvalidError("elgamal: invalid group") + } + if p.BitLen() < 1024 { + return errors.KeyInvalidError("elgamal: group order too small") + } + pSub1 := new(big.Int).Sub(p, one) + if new(big.Int).Exp(g, pSub1, p).Cmp(one) != 0 { + return errors.KeyInvalidError("elgamal: invalid group") + } + // Since p-1 is not prime, g might have a smaller order that divides p-1. + // We cannot confirm the exact order of g, but we make sure it is not too small. + gExpI := new(big.Int).Set(g) + i := 1 + threshold := 2 << 17 // we want order > threshold + for i < threshold { + i++ // we check every order to make sure key validation is not easily bypassed by guessing y' + gExpI.Mod(new(big.Int).Mul(gExpI, g), p) + if gExpI.Cmp(one) == 0 { + return errors.KeyInvalidError("elgamal: order too small") + } + } + // Check y + if new(big.Int).Exp(g, x, p).Cmp(y) != 0 { + return errors.KeyInvalidError("elgamal: mismatching values") + } + + return nil +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/private_key_test_data.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/private_key_test_data.go new file mode 100644 index 0000000000000..029b8f1aab2ee --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/private_key_test_data.go @@ -0,0 +1,12 @@ +package packet + +// Generated with `gpg --export-secret-keys "Test Key 2"` +const privKeyRSAHex = "9501fe044cc349a8010400b70ca0010e98c090008d45d1ee8f9113bd5861fd57b88bacb7c68658747663f1e1a3b5a98f32fda6472373c024b97359cd2efc88ff60f77751adfbf6af5e615e6a1408cfad8bf0cea30b0d5f53aa27ad59089ba9b15b7ebc2777a25d7b436144027e3bcd203909f147d0e332b240cf63d3395f5dfe0df0a6c04e8655af7eacdf0011010001fe0303024a252e7d475fd445607de39a265472aa74a9320ba2dac395faa687e9e0336aeb7e9a7397e511b5afd9dc84557c80ac0f3d4d7bfec5ae16f20d41c8c84a04552a33870b930420e230e179564f6d19bb153145e76c33ae993886c388832b0fa042ddda7f133924f3854481533e0ede31d51278c0519b29abc3bf53da673e13e3e1214b52413d179d7f66deee35cac8eacb060f78379d70ef4af8607e68131ff529439668fc39c9ce6dfef8a5ac234d234802cbfb749a26107db26406213ae5c06d4673253a3cbee1fcbae58d6ab77e38d6e2c0e7c6317c48e054edadb5a40d0d48acb44643d998139a8a66bb820be1f3f80185bc777d14b5954b60effe2448a036d565c6bc0b915fcea518acdd20ab07bc1529f561c58cd044f723109b93f6fd99f876ff891d64306b5d08f48bab59f38695e9109c4dec34013ba3153488ce070268381ba923ee1eb77125b36afcb4347ec3478c8f2735b06ef17351d872e577fa95d0c397c88c71b59629a36aec" + +// Generated by `gpg --export-secret-keys` followed by a manual extraction of +// the ElGamal subkey from the packets. +const privKeyElGamalHex = "9d0157044df9ee1a100400eb8e136a58ec39b582629cdadf830bc64e0a94ed8103ca8bb247b27b11b46d1d25297ef4bcc3071785ba0c0bedfe89eabc5287fcc0edf81ab5896c1c8e4b20d27d79813c7aede75320b33eaeeaa586edc00fd1036c10133e6ba0ff277245d0d59d04b2b3421b7244aca5f4a8d870c6f1c1fbff9e1c26699a860b9504f35ca1d700030503fd1ededd3b840795be6d9ccbe3c51ee42e2f39233c432b831ddd9c4e72b7025a819317e47bf94f9ee316d7273b05d5fcf2999c3a681f519b1234bbfa6d359b4752bd9c3f77d6b6456cde152464763414ca130f4e91d91041432f90620fec0e6d6b5116076c2985d5aeaae13be492b9b329efcaf7ee25120159a0a30cd976b42d7afe030302dae7eb80db744d4960c4df930d57e87fe81412eaace9f900e6c839817a614ddb75ba6603b9417c33ea7b6c93967dfa2bcff3fa3c74a5ce2c962db65b03aece14c96cbd0038fc" + +// pkcs1PrivKeyHex is a PKCS#1, RSA private key. +// Generated by `openssl genrsa 1024 | openssl rsa -outform DER | xxd -p` +const pkcs1PrivKeyHex = "3082025d02010002818100e98edfa1c3b35884a54d0b36a6a603b0290fa85e49e30fa23fc94fef9c6790bc4849928607aa48d809da326fb42a969d06ad756b98b9c1a90f5d4a2b6d0ac05953c97f4da3120164a21a679793ce181c906dc01d235cc085ddcdf6ea06c389b6ab8885dfd685959e693138856a68a7e5db263337ff82a088d583a897cf2d59e9020301000102818100b6d5c9eb70b02d5369b3ee5b520a14490b5bde8a317d36f7e4c74b7460141311d1e5067735f8f01d6f5908b2b96fbd881f7a1ab9a84d82753e39e19e2d36856be960d05ac9ef8e8782ea1b6d65aee28fdfe1d61451e8cff0adfe84322f12cf455028b581cf60eb9e0e140ba5d21aeba6c2634d7c65318b9a665fc01c3191ca21024100fa5e818da3705b0fa33278bb28d4b6f6050388af2d4b75ec9375dd91ccf2e7d7068086a8b82a8f6282e4fbbdb8a7f2622eb97295249d87acea7f5f816f54d347024100eecf9406d7dc49cdfb95ab1eff4064de84c7a30f64b2798936a0d2018ba9eb52e4b636f82e96c49cc63b80b675e91e40d1b2e4017d4b9adaf33ab3d9cf1c214f024100c173704ace742c082323066226a4655226819a85304c542b9dacbeacbf5d1881ee863485fcf6f59f3a604f9b42289282067447f2b13dfeed3eab7851fc81e0550240741fc41f3fc002b382eed8730e33c5d8de40256e4accee846667f536832f711ab1d4590e7db91a8a116ac5bff3be13d3f9243ff2e976662aa9b395d907f8e9c9024046a5696c9ef882363e06c9fa4e2f5b580906452befba03f4a99d0f873697ef1f851d2226ca7934b30b7c3e80cb634a67172bbbf4781735fe3e09263e2dd723e7" diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key.go new file mode 100644 index 0000000000000..28971c1ada208 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key.go @@ -0,0 +1,825 @@ +// Copyright 2011 The 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 packet + +import ( + "crypto" + "crypto/dsa" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rsa" + "crypto/sha1" + "crypto/sha256" + _ "crypto/sha512" + "encoding/binary" + "fmt" + "hash" + "io" + "math/big" + "strconv" + "time" + + "github.com/ProtonMail/go-crypto/openpgp/ecdh" + "github.com/ProtonMail/go-crypto/openpgp/elgamal" + "github.com/ProtonMail/go-crypto/openpgp/errors" + "github.com/ProtonMail/go-crypto/openpgp/internal/algorithm" + "github.com/ProtonMail/go-crypto/openpgp/internal/ecc" + "github.com/ProtonMail/go-crypto/openpgp/internal/encoding" + "golang.org/x/crypto/ed25519" +) + +type kdfHashFunction byte +type kdfAlgorithm byte + +// PublicKey represents an OpenPGP public key. See RFC 4880, section 5.5.2. +type PublicKey struct { + Version int + CreationTime time.Time + PubKeyAlgo PublicKeyAlgorithm + PublicKey interface{} // *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey or *eddsa.PublicKey + Fingerprint []byte + KeyId uint64 + IsSubkey bool + + // RFC 4880 fields + n, e, p, q, g, y encoding.Field + + // RFC 6637 fields + // oid contains the OID byte sequence identifying the elliptic curve used + oid encoding.Field + + // kdf stores key derivation function parameters + // used for ECDH encryption. See RFC 6637, Section 9. + kdf encoding.Field +} + +// UpgradeToV5 updates the version of the key to v5, and updates all necessary +// fields. +func (pk *PublicKey) UpgradeToV5() { + pk.Version = 5 + pk.setFingerprintAndKeyId() +} + +// signingKey provides a convenient abstraction over signature verification +// for v3 and v4 public keys. +type signingKey interface { + SerializeForHash(io.Writer) error + SerializeSignaturePrefix(io.Writer) + serializeWithoutHeaders(io.Writer) error +} + +// NewRSAPublicKey returns a PublicKey that wraps the given rsa.PublicKey. +func NewRSAPublicKey(creationTime time.Time, pub *rsa.PublicKey) *PublicKey { + pk := &PublicKey{ + Version: 4, + CreationTime: creationTime, + PubKeyAlgo: PubKeyAlgoRSA, + PublicKey: pub, + n: new(encoding.MPI).SetBig(pub.N), + e: new(encoding.MPI).SetBig(big.NewInt(int64(pub.E))), + } + + pk.setFingerprintAndKeyId() + return pk +} + +// NewDSAPublicKey returns a PublicKey that wraps the given dsa.PublicKey. +func NewDSAPublicKey(creationTime time.Time, pub *dsa.PublicKey) *PublicKey { + pk := &PublicKey{ + Version: 4, + CreationTime: creationTime, + PubKeyAlgo: PubKeyAlgoDSA, + PublicKey: pub, + p: new(encoding.MPI).SetBig(pub.P), + q: new(encoding.MPI).SetBig(pub.Q), + g: new(encoding.MPI).SetBig(pub.G), + y: new(encoding.MPI).SetBig(pub.Y), + } + + pk.setFingerprintAndKeyId() + return pk +} + +// NewElGamalPublicKey returns a PublicKey that wraps the given elgamal.PublicKey. +func NewElGamalPublicKey(creationTime time.Time, pub *elgamal.PublicKey) *PublicKey { + pk := &PublicKey{ + Version: 4, + CreationTime: creationTime, + PubKeyAlgo: PubKeyAlgoElGamal, + PublicKey: pub, + p: new(encoding.MPI).SetBig(pub.P), + g: new(encoding.MPI).SetBig(pub.G), + y: new(encoding.MPI).SetBig(pub.Y), + } + + pk.setFingerprintAndKeyId() + return pk +} + +func NewECDSAPublicKey(creationTime time.Time, pub *ecdsa.PublicKey) *PublicKey { + pk := &PublicKey{ + Version: 4, + CreationTime: creationTime, + PubKeyAlgo: PubKeyAlgoECDSA, + PublicKey: pub, + p: encoding.NewMPI(elliptic.Marshal(pub.Curve, pub.X, pub.Y)), + } + + curveInfo := ecc.FindByCurve(pub.Curve) + if curveInfo == nil { + panic("unknown elliptic curve") + } + pk.oid = curveInfo.Oid + pk.setFingerprintAndKeyId() + return pk +} + +func NewECDHPublicKey(creationTime time.Time, pub *ecdh.PublicKey) *PublicKey { + var pk *PublicKey + var curveInfo *ecc.CurveInfo + var kdf = encoding.NewOID([]byte{0x1, pub.Hash.Id(), pub.Cipher.Id()}) + if pub.CurveType == ecc.Curve25519 { + pk = &PublicKey{ + Version: 4, + CreationTime: creationTime, + PubKeyAlgo: PubKeyAlgoECDH, + PublicKey: pub, + p: encoding.NewMPI(pub.X.Bytes()), + kdf: kdf, + } + curveInfo = ecc.FindByName("Curve25519") + } else { + pk = &PublicKey{ + Version: 4, + CreationTime: creationTime, + PubKeyAlgo: PubKeyAlgoECDH, + PublicKey: pub, + p: encoding.NewMPI(elliptic.Marshal(pub.Curve, pub.X, pub.Y)), + kdf: kdf, + } + curveInfo = ecc.FindByCurve(pub.Curve) + } + if curveInfo == nil { + panic("unknown elliptic curve") + } + pk.oid = curveInfo.Oid + pk.setFingerprintAndKeyId() + return pk +} + +func NewEdDSAPublicKey(creationTime time.Time, pub *ed25519.PublicKey) *PublicKey { + curveInfo := ecc.FindByName("Ed25519") + pk := &PublicKey{ + Version: 4, + CreationTime: creationTime, + PubKeyAlgo: PubKeyAlgoEdDSA, + PublicKey: pub, + oid: curveInfo.Oid, + // Native point format, see draft-koch-eddsa-for-openpgp-04, Appendix B + p: encoding.NewMPI(append([]byte{0x40}, *pub...)), + } + + pk.setFingerprintAndKeyId() + return pk +} + +func (pk *PublicKey) parse(r io.Reader) (err error) { + // RFC 4880, section 5.5.2 + var buf [6]byte + _, err = readFull(r, buf[:]) + if err != nil { + return + } + if buf[0] != 4 && buf[0] != 5 { + return errors.UnsupportedError("public key version " + strconv.Itoa(int(buf[0]))) + } + + pk.Version = int(buf[0]) + if pk.Version == 5 { + var n [4]byte + _, err = readFull(r, n[:]) + if err != nil { + return + } + } + pk.CreationTime = time.Unix(int64(uint32(buf[1])<<24|uint32(buf[2])<<16|uint32(buf[3])<<8|uint32(buf[4])), 0) + pk.PubKeyAlgo = PublicKeyAlgorithm(buf[5]) + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + err = pk.parseRSA(r) + case PubKeyAlgoDSA: + err = pk.parseDSA(r) + case PubKeyAlgoElGamal: + err = pk.parseElGamal(r) + case PubKeyAlgoECDSA: + err = pk.parseECDSA(r) + case PubKeyAlgoECDH: + err = pk.parseECDH(r) + case PubKeyAlgoEdDSA: + err = pk.parseEdDSA(r) + default: + err = errors.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo))) + } + if err != nil { + return + } + + pk.setFingerprintAndKeyId() + return +} + +func (pk *PublicKey) setFingerprintAndKeyId() { + // RFC 4880, section 12.2 + if pk.Version == 5 { + fingerprint := sha256.New() + pk.SerializeForHash(fingerprint) + pk.Fingerprint = make([]byte, 32) + copy(pk.Fingerprint, fingerprint.Sum(nil)) + pk.KeyId = binary.BigEndian.Uint64(pk.Fingerprint[:8]) + } else { + fingerprint := sha1.New() + pk.SerializeForHash(fingerprint) + pk.Fingerprint = make([]byte, 20) + copy(pk.Fingerprint, fingerprint.Sum(nil)) + pk.KeyId = binary.BigEndian.Uint64(pk.Fingerprint[12:20]) + } +} + +// parseRSA parses RSA public key material from the given Reader. See RFC 4880, +// section 5.5.2. +func (pk *PublicKey) parseRSA(r io.Reader) (err error) { + pk.n = new(encoding.MPI) + if _, err = pk.n.ReadFrom(r); err != nil { + return + } + pk.e = new(encoding.MPI) + if _, err = pk.e.ReadFrom(r); err != nil { + return + } + + if len(pk.e.Bytes()) > 3 { + err = errors.UnsupportedError("large public exponent") + return + } + rsa := &rsa.PublicKey{ + N: new(big.Int).SetBytes(pk.n.Bytes()), + E: 0, + } + for i := 0; i < len(pk.e.Bytes()); i++ { + rsa.E <<= 8 + rsa.E |= int(pk.e.Bytes()[i]) + } + pk.PublicKey = rsa + return +} + +// parseDSA parses DSA public key material from the given Reader. See RFC 4880, +// section 5.5.2. +func (pk *PublicKey) parseDSA(r io.Reader) (err error) { + pk.p = new(encoding.MPI) + if _, err = pk.p.ReadFrom(r); err != nil { + return + } + pk.q = new(encoding.MPI) + if _, err = pk.q.ReadFrom(r); err != nil { + return + } + pk.g = new(encoding.MPI) + if _, err = pk.g.ReadFrom(r); err != nil { + return + } + pk.y = new(encoding.MPI) + if _, err = pk.y.ReadFrom(r); err != nil { + return + } + + dsa := new(dsa.PublicKey) + dsa.P = new(big.Int).SetBytes(pk.p.Bytes()) + dsa.Q = new(big.Int).SetBytes(pk.q.Bytes()) + dsa.G = new(big.Int).SetBytes(pk.g.Bytes()) + dsa.Y = new(big.Int).SetBytes(pk.y.Bytes()) + pk.PublicKey = dsa + return +} + +// parseElGamal parses ElGamal public key material from the given Reader. See +// RFC 4880, section 5.5.2. +func (pk *PublicKey) parseElGamal(r io.Reader) (err error) { + pk.p = new(encoding.MPI) + if _, err = pk.p.ReadFrom(r); err != nil { + return + } + pk.g = new(encoding.MPI) + if _, err = pk.g.ReadFrom(r); err != nil { + return + } + pk.y = new(encoding.MPI) + if _, err = pk.y.ReadFrom(r); err != nil { + return + } + + elgamal := new(elgamal.PublicKey) + elgamal.P = new(big.Int).SetBytes(pk.p.Bytes()) + elgamal.G = new(big.Int).SetBytes(pk.g.Bytes()) + elgamal.Y = new(big.Int).SetBytes(pk.y.Bytes()) + pk.PublicKey = elgamal + return +} + +// parseECDSA parses ECDSA public key material from the given Reader. See +// RFC 6637, Section 9. +func (pk *PublicKey) parseECDSA(r io.Reader) (err error) { + pk.oid = new(encoding.OID) + if _, err = pk.oid.ReadFrom(r); err != nil { + return + } + pk.p = new(encoding.MPI) + if _, err = pk.p.ReadFrom(r); err != nil { + return + } + + var c elliptic.Curve + curveInfo := ecc.FindByOid(pk.oid) + if curveInfo == nil || curveInfo.SigAlgorithm != ecc.ECDSA { + return errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", pk.oid)) + } + c = curveInfo.Curve + x, y := elliptic.Unmarshal(c, pk.p.Bytes()) + if x == nil { + return errors.UnsupportedError("failed to parse EC point") + } + pk.PublicKey = &ecdsa.PublicKey{Curve: c, X: x, Y: y} + return +} + +// parseECDH parses ECDH public key material from the given Reader. See +// RFC 6637, Section 9. +func (pk *PublicKey) parseECDH(r io.Reader) (err error) { + pk.oid = new(encoding.OID) + if _, err = pk.oid.ReadFrom(r); err != nil { + return + } + pk.p = new(encoding.MPI) + if _, err = pk.p.ReadFrom(r); err != nil { + return + } + pk.kdf = new(encoding.OID) + if _, err = pk.kdf.ReadFrom(r); err != nil { + return + } + + curveInfo := ecc.FindByOid(pk.oid) + if curveInfo == nil { + return errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", pk.oid)) + } + + c := curveInfo.Curve + cType := curveInfo.CurveType + + var x, y *big.Int + if cType == ecc.Curve25519 { + x = new(big.Int) + x.SetBytes(pk.p.Bytes()) + } else { + x, y = elliptic.Unmarshal(c, pk.p.Bytes()) + } + if x == nil { + return errors.UnsupportedError("failed to parse EC point") + } + + if kdfLen := len(pk.kdf.Bytes()); kdfLen < 3 { + return errors.UnsupportedError("unsupported ECDH KDF length: " + strconv.Itoa(kdfLen)) + } + if reserved := pk.kdf.Bytes()[0]; reserved != 0x01 { + return errors.UnsupportedError("unsupported KDF reserved field: " + strconv.Itoa(int(reserved))) + } + kdfHash, ok := algorithm.HashById[pk.kdf.Bytes()[1]] + if !ok { + return errors.UnsupportedError("unsupported ECDH KDF hash: " + strconv.Itoa(int(pk.kdf.Bytes()[1]))) + } + kdfCipher, ok := algorithm.CipherById[pk.kdf.Bytes()[2]] + if !ok { + return errors.UnsupportedError("unsupported ECDH KDF cipher: " + strconv.Itoa(int(pk.kdf.Bytes()[2]))) + } + + pk.PublicKey = &ecdh.PublicKey{ + CurveType: cType, + Curve: c, + X: x, + Y: y, + KDF: ecdh.KDF{ + Hash: kdfHash, + Cipher: kdfCipher, + }, + } + return +} + +func (pk *PublicKey) parseEdDSA(r io.Reader) (err error) { + pk.oid = new(encoding.OID) + if _, err = pk.oid.ReadFrom(r); err != nil { + return + } + curveInfo := ecc.FindByOid(pk.oid) + if curveInfo == nil || curveInfo.SigAlgorithm != ecc.EdDSA { + return errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", pk.oid)) + } + pk.p = new(encoding.MPI) + if _, err = pk.p.ReadFrom(r); err != nil { + return + } + + eddsa := make(ed25519.PublicKey, ed25519.PublicKeySize) + switch flag := pk.p.Bytes()[0]; flag { + case 0x04: + // TODO: see _grcy_ecc_eddsa_ensure_compact in grcypt + return errors.UnsupportedError("unsupported EdDSA compression: " + strconv.Itoa(int(flag))) + case 0x40: + copy(eddsa[:], pk.p.Bytes()[1:]) + default: + return errors.UnsupportedError("unsupported EdDSA compression: " + strconv.Itoa(int(flag))) + } + + pk.PublicKey = &eddsa + return +} + +// SerializeForHash serializes the PublicKey to w with the special packet +// header format needed for hashing. +func (pk *PublicKey) SerializeForHash(w io.Writer) error { + pk.SerializeSignaturePrefix(w) + return pk.serializeWithoutHeaders(w) +} + +// SerializeSignaturePrefix writes the prefix for this public key to the given Writer. +// The prefix is used when calculating a signature over this public key. See +// RFC 4880, section 5.2.4. +func (pk *PublicKey) SerializeSignaturePrefix(w io.Writer) { + var pLength = pk.algorithmSpecificByteCount() + if pk.Version == 5 { + pLength += 10 // version, timestamp (4), algorithm, key octet count (4). + w.Write([]byte{ + 0x9A, + byte(pLength >> 24), + byte(pLength >> 16), + byte(pLength >> 8), + byte(pLength), + }) + return + } + pLength += 6 + w.Write([]byte{0x99, byte(pLength >> 8), byte(pLength)}) +} + +func (pk *PublicKey) Serialize(w io.Writer) (err error) { + length := 6 // 6 byte header + length += pk.algorithmSpecificByteCount() + if pk.Version == 5 { + length += 4 // octet key count + } + packetType := packetTypePublicKey + if pk.IsSubkey { + packetType = packetTypePublicSubkey + } + err = serializeHeader(w, packetType, length) + if err != nil { + return + } + return pk.serializeWithoutHeaders(w) +} + +func (pk *PublicKey) algorithmSpecificByteCount() int { + length := 0 + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + length += int(pk.n.EncodedLength()) + length += int(pk.e.EncodedLength()) + case PubKeyAlgoDSA: + length += int(pk.p.EncodedLength()) + length += int(pk.q.EncodedLength()) + length += int(pk.g.EncodedLength()) + length += int(pk.y.EncodedLength()) + case PubKeyAlgoElGamal: + length += int(pk.p.EncodedLength()) + length += int(pk.g.EncodedLength()) + length += int(pk.y.EncodedLength()) + case PubKeyAlgoECDSA: + length += int(pk.oid.EncodedLength()) + length += int(pk.p.EncodedLength()) + case PubKeyAlgoECDH: + length += int(pk.oid.EncodedLength()) + length += int(pk.p.EncodedLength()) + length += int(pk.kdf.EncodedLength()) + case PubKeyAlgoEdDSA: + length += int(pk.oid.EncodedLength()) + length += int(pk.p.EncodedLength()) + default: + panic("unknown public key algorithm") + } + return length +} + +// serializeWithoutHeaders marshals the PublicKey to w in the form of an +// OpenPGP public key packet, not including the packet header. +func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err error) { + t := uint32(pk.CreationTime.Unix()) + if _, err = w.Write([]byte{ + byte(pk.Version), + byte(t >> 24), byte(t >> 16), byte(t >> 8), byte(t), + byte(pk.PubKeyAlgo), + }); err != nil { + return + } + + if pk.Version == 5 { + n := pk.algorithmSpecificByteCount() + if _, err = w.Write([]byte{ + byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n), + }); err != nil { + return + } + } + + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + if _, err = w.Write(pk.n.EncodedBytes()); err != nil { + return + } + _, err = w.Write(pk.e.EncodedBytes()) + return + case PubKeyAlgoDSA: + if _, err = w.Write(pk.p.EncodedBytes()); err != nil { + return + } + if _, err = w.Write(pk.q.EncodedBytes()); err != nil { + return + } + if _, err = w.Write(pk.g.EncodedBytes()); err != nil { + return + } + _, err = w.Write(pk.y.EncodedBytes()) + return + case PubKeyAlgoElGamal: + if _, err = w.Write(pk.p.EncodedBytes()); err != nil { + return + } + if _, err = w.Write(pk.g.EncodedBytes()); err != nil { + return + } + _, err = w.Write(pk.y.EncodedBytes()) + return + case PubKeyAlgoECDSA: + if _, err = w.Write(pk.oid.EncodedBytes()); err != nil { + return + } + _, err = w.Write(pk.p.EncodedBytes()) + return + case PubKeyAlgoECDH: + if _, err = w.Write(pk.oid.EncodedBytes()); err != nil { + return + } + if _, err = w.Write(pk.p.EncodedBytes()); err != nil { + return + } + _, err = w.Write(pk.kdf.EncodedBytes()) + return + case PubKeyAlgoEdDSA: + if _, err = w.Write(pk.oid.EncodedBytes()); err != nil { + return + } + _, err = w.Write(pk.p.EncodedBytes()) + return + } + return errors.InvalidArgumentError("bad public-key algorithm") +} + +// CanSign returns true iff this public key can generate signatures +func (pk *PublicKey) CanSign() bool { + return pk.PubKeyAlgo != PubKeyAlgoRSAEncryptOnly && pk.PubKeyAlgo != PubKeyAlgoElGamal && pk.PubKeyAlgo != PubKeyAlgoECDH +} + +// VerifySignature returns nil iff sig is a valid signature, made by this +// public key, of the data hashed into signed. signed is mutated by this call. +func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err error) { + if !pk.CanSign() { + return errors.InvalidArgumentError("public key cannot generate signatures") + } + if sig.Version == 5 && (sig.SigType == 0x00 || sig.SigType == 0x01) { + sig.AddMetadataToHashSuffix() + } + signed.Write(sig.HashSuffix) + hashBytes := signed.Sum(nil) + if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] { + return errors.SignatureError("hash tag doesn't match") + } + + if pk.PubKeyAlgo != sig.PubKeyAlgo { + return errors.InvalidArgumentError("public key and signature use different algorithms") + } + + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: + rsaPublicKey, _ := pk.PublicKey.(*rsa.PublicKey) + err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, padToKeySize(rsaPublicKey, sig.RSASignature.Bytes())) + if err != nil { + return errors.SignatureError("RSA verification failure") + } + return nil + case PubKeyAlgoDSA: + dsaPublicKey, _ := pk.PublicKey.(*dsa.PublicKey) + // Need to truncate hashBytes to match FIPS 186-3 section 4.6. + subgroupSize := (dsaPublicKey.Q.BitLen() + 7) / 8 + if len(hashBytes) > subgroupSize { + hashBytes = hashBytes[:subgroupSize] + } + if !dsa.Verify(dsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.DSASigR.Bytes()), new(big.Int).SetBytes(sig.DSASigS.Bytes())) { + return errors.SignatureError("DSA verification failure") + } + return nil + case PubKeyAlgoECDSA: + ecdsaPublicKey := pk.PublicKey.(*ecdsa.PublicKey) + if !ecdsa.Verify(ecdsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.ECDSASigR.Bytes()), new(big.Int).SetBytes(sig.ECDSASigS.Bytes())) { + return errors.SignatureError("ECDSA verification failure") + } + return nil + case PubKeyAlgoEdDSA: + eddsaPublicKey := pk.PublicKey.(*ed25519.PublicKey) + + sigR := sig.EdDSASigR.Bytes() + sigS := sig.EdDSASigS.Bytes() + + eddsaSig := make([]byte, ed25519.SignatureSize) + copy(eddsaSig[32-len(sigR):32], sigR) + copy(eddsaSig[64-len(sigS):], sigS) + + if !ed25519.Verify(*eddsaPublicKey, hashBytes, eddsaSig) { + return errors.SignatureError("EdDSA verification failure") + } + return nil + default: + return errors.SignatureError("Unsupported public key algorithm used in signature") + } +} + +// keySignatureHash returns a Hash of the message that needs to be signed for +// pk to assert a subkey relationship to signed. +func keySignatureHash(pk, signed signingKey, hashFunc crypto.Hash) (h hash.Hash, err error) { + if !hashFunc.Available() { + return nil, errors.UnsupportedError("hash function") + } + h = hashFunc.New() + + // RFC 4880, section 5.2.4 + err = pk.SerializeForHash(h) + if err != nil { + return nil, err + } + + err = signed.SerializeForHash(h) + return +} + +// VerifyKeySignature returns nil iff sig is a valid signature, made by this +// public key, of signed. +func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) error { + h, err := keySignatureHash(pk, signed, sig.Hash) + if err != nil { + return err + } + if err = pk.VerifySignature(h, sig); err != nil { + return err + } + + if sig.FlagSign { + // Signing subkeys must be cross-signed. See + // https://www.gnupg.org/faq/subkey-cross-certify.html. + if sig.EmbeddedSignature == nil { + return errors.StructuralError("signing subkey is missing cross-signature") + } + // Verify the cross-signature. This is calculated over the same + // data as the main signature, so we cannot just recursively + // call signed.VerifyKeySignature(...) + if h, err = keySignatureHash(pk, signed, sig.EmbeddedSignature.Hash); err != nil { + return errors.StructuralError("error while hashing for cross-signature: " + err.Error()) + } + if err := signed.VerifySignature(h, sig.EmbeddedSignature); err != nil { + return errors.StructuralError("error while verifying cross-signature: " + err.Error()) + } + } + + return nil +} + +func keyRevocationHash(pk signingKey, hashFunc crypto.Hash) (h hash.Hash, err error) { + if !hashFunc.Available() { + return nil, errors.UnsupportedError("hash function") + } + h = hashFunc.New() + + // RFC 4880, section 5.2.4 + err = pk.SerializeForHash(h) + + return +} + +// VerifyRevocationSignature returns nil iff sig is a valid signature, made by this +// public key. +func (pk *PublicKey) VerifyRevocationSignature(sig *Signature) (err error) { + h, err := keyRevocationHash(pk, sig.Hash) + if err != nil { + return err + } + return pk.VerifySignature(h, sig) +} + +// VerifySubkeyRevocationSignature returns nil iff sig is a valid subkey revocation signature, +// made by the passed in signingKey. +func (pk *PublicKey) VerifySubkeyRevocationSignature(sig *Signature, signingKey *PublicKey) (err error) { + h, err := keyRevocationHash(pk, sig.Hash) + if err != nil { + return err + } + return signingKey.VerifySignature(h, sig) +} + +// userIdSignatureHash returns a Hash of the message that needs to be signed +// to assert that pk is a valid key for id. +func userIdSignatureHash(id string, pk *PublicKey, hashFunc crypto.Hash) (h hash.Hash, err error) { + if !hashFunc.Available() { + return nil, errors.UnsupportedError("hash function") + } + h = hashFunc.New() + + // RFC 4880, section 5.2.4 + pk.SerializeSignaturePrefix(h) + pk.serializeWithoutHeaders(h) + + var buf [5]byte + buf[0] = 0xb4 + buf[1] = byte(len(id) >> 24) + buf[2] = byte(len(id) >> 16) + buf[3] = byte(len(id) >> 8) + buf[4] = byte(len(id)) + h.Write(buf[:]) + h.Write([]byte(id)) + + return +} + +// VerifyUserIdSignature returns nil iff sig is a valid signature, made by this +// public key, that id is the identity of pub. +func (pk *PublicKey) VerifyUserIdSignature(id string, pub *PublicKey, sig *Signature) (err error) { + h, err := userIdSignatureHash(id, pub, sig.Hash) + if err != nil { + return err + } + return pk.VerifySignature(h, sig) +} + +// KeyIdString returns the public key's fingerprint in capital hex +// (e.g. "6C7EE1B8621CC013"). +func (pk *PublicKey) KeyIdString() string { + return fmt.Sprintf("%X", pk.Fingerprint[12:20]) +} + +// KeyIdShortString returns the short form of public key's fingerprint +// in capital hex, as shown by gpg --list-keys (e.g. "621CC013"). +func (pk *PublicKey) KeyIdShortString() string { + return fmt.Sprintf("%X", pk.Fingerprint[16:20]) +} + +// BitLength returns the bit length for the given public key. +func (pk *PublicKey) BitLength() (bitLength uint16, err error) { + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + bitLength = pk.n.BitLength() + case PubKeyAlgoDSA: + bitLength = pk.p.BitLength() + case PubKeyAlgoElGamal: + bitLength = pk.p.BitLength() + case PubKeyAlgoECDSA: + bitLength = pk.p.BitLength() + case PubKeyAlgoECDH: + bitLength = pk.p.BitLength() + case PubKeyAlgoEdDSA: + bitLength = pk.p.BitLength() + default: + err = errors.InvalidArgumentError("bad public-key algorithm") + } + return +} + +// KeyExpired returns whether sig is a self-signature of a key that has +// expired or is created in the future. +func (pk *PublicKey) KeyExpired(sig *Signature, currentTime time.Time) bool { + if pk.CreationTime.After(currentTime) { + return true + } + if sig.KeyLifetimeSecs == nil || *sig.KeyLifetimeSecs == 0 { + return false + } + expiry := pk.CreationTime.Add(time.Duration(*sig.KeyLifetimeSecs) * time.Second) + return currentTime.After(expiry) +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key_test_data.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key_test_data.go new file mode 100644 index 0000000000000..b255f1f6f8f5f --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key_test_data.go @@ -0,0 +1,24 @@ +package packet + +const rsaFingerprintHex = "5fb74b1d03b1e3cb31bc2f8aa34d7e18c20c31bb" + +const rsaPkDataHex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd0011010001" + +const dsaFingerprintHex = "eece4c094db002103714c63c8e8fbe54062f19ed" + +const dsaPkDataHex = "9901a2044d432f89110400cd581334f0d7a1e1bdc8b9d6d8c0baf68793632735d2bb0903224cbaa1dfbf35a60ee7a13b92643421e1eb41aa8d79bea19a115a677f6b8ba3c7818ce53a6c2a24a1608bd8b8d6e55c5090cbde09dd26e356267465ae25e69ec8bdd57c7bbb2623e4d73336f73a0a9098f7f16da2e25252130fd694c0e8070c55a812a423ae7f00a0ebf50e70c2f19c3520a551bd4b08d30f23530d3d03ff7d0bf4a53a64a09dc5e6e6e35854b7d70c882b0c60293401958b1bd9e40abec3ea05ba87cf64899299d4bd6aa7f459c201d3fbbd6c82004bdc5e8a9eb8082d12054cc90fa9d4ec251a843236a588bf49552441817436c4f43326966fe85447d4e6d0acf8fa1ef0f014730770603ad7634c3088dc52501c237328417c31c89ed70400b2f1a98b0bf42f11fefc430704bebbaa41d9f355600c3facee1e490f64208e0e094ea55e3a598a219a58500bf78ac677b670a14f4e47e9cf8eab4f368cc1ddcaa18cc59309d4cc62dd4f680e73e6cc3e1ce87a84d0925efbcb26c575c093fc42eecf45135fabf6403a25c2016e1774c0484e440a18319072c617cc97ac0a3bb0" + +const ecdsaFingerprintHex = "9892270b38b8980b05c8d56d43fe956c542ca00b" + +const ecdsaPkDataHex = "9893045071c29413052b8104002304230401f4867769cedfa52c325018896245443968e52e51d0c2df8d939949cb5b330f2921711fbee1c9b9dddb95d15cb0255e99badeddda7cc23d9ddcaacbc290969b9f24019375d61c2e4e3b36953a28d8b2bc95f78c3f1d592fb24499be348656a7b17e3963187b4361afe497bc5f9f81213f04069f8e1fb9e6a6290ae295ca1a92b894396cb4" + +const ecdhFingerprintHex = "722354df2475a42164d1d49faa8b938f9a201946" + +const ecdhPkDataHex = "b90073044d53059212052b810400220303042faa84024a20b6735c4897efa5bfb41bf85b7eefeab5ca0cb9ffc8ea04a46acb25534a577694f9e25340a4ab5223a9dd1eda530c8aa2e6718db10d7e672558c7736fe09369ea5739a2a3554bf16d41faa50562f11c6d39bbd5dffb6b9a9ec91803010909" + +const eddsaFingerprintHex = "b2d5e5ec0e6deca6bc8eeeb00907e75e1dd99ad8" + +const eddsaPkDataHex = "98330456e2132b16092b06010401da470f01010740bbda39266affa511a8c2d02edf690fb784b0499c4406185811a163539ef11dc1b41d74657374696e67203c74657374696e674074657374696e672e636f6d3e8879041316080021050256e2132b021b03050b09080702061508090a0b020416020301021e01021780000a09100907e75e1dd99ad86d0c00fe39d2008359352782bc9b61ac382584cd8eff3f57a18c2287e3afeeb05d1f04ba00fe2d0bc1ddf3ff8adb9afa3e7d9287244b4ec567f3db4d60b74a9b5465ed528203" + +// Source: https://sites.google.com/site/brainhub/pgpecckeys#TOC-ECC-NIST-P-384-key +const ecc384PubHex = `99006f044d53059213052b81040022030304f6b8c5aced5b84ef9f4a209db2e4a9dfb70d28cb8c10ecd57674a9fa5a67389942b62d5e51367df4c7bfd3f8e500feecf07ed265a621a8ebbbe53e947ec78c677eba143bd1533c2b350e1c29f82313e1e1108eba063be1e64b10e6950e799c2db42465635f6473615f64685f333834203c6f70656e70677040627261696e6875622e6f72673e8900cb04101309005305024d530592301480000000002000077072656665727265642d656d61696c2d656e636f64696e67407067702e636f6d7067706d696d65040b090807021901051b03000000021602051e010000000415090a08000a0910098033880f54719fca2b0180aa37350968bd5f115afd8ce7bc7b103822152dbff06d0afcda835329510905b98cb469ba208faab87c7412b799e7b633017f58364ea480e8a1a3f253a0c5f22c446e8be9a9fce6210136ee30811abbd49139de28b5bdf8dc36d06ae748579e9ff503b90073044d53059212052b810400220303042faa84024a20b6735c4897efa5bfb41bf85b7eefeab5ca0cb9ffc8ea04a46acb25534a577694f9e25340a4ab5223a9dd1eda530c8aa2e6718db10d7e672558c7736fe09369ea5739a2a3554bf16d41faa50562f11c6d39bbd5dffb6b9a9ec9180301090989008404181309000c05024d530592051b0c000000000a0910098033880f54719f80970180eee7a6d8fcee41ee4f9289df17f9bcf9d955dca25c583b94336f3a2b2d4986dc5cf417b8d2dc86f741a9e1a6d236c0e3017d1c76575458a0cfb93ae8a2b274fcc65ceecd7a91eec83656ba13219969f06945b48c56bd04152c3a0553c5f2f4bd1267` diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/reader.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/reader.go new file mode 100644 index 0000000000000..94ae9ad9aca38 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/reader.go @@ -0,0 +1,78 @@ +// Copyright 2011 The 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 packet + +import ( + "io" + + "github.com/ProtonMail/go-crypto/openpgp/errors" +) + +// Reader reads packets from an io.Reader and allows packets to be 'unread' so +// that they result from the next call to Next. +type Reader struct { + q []Packet + readers []io.Reader +} + +// New io.Readers are pushed when a compressed or encrypted packet is processed +// and recursively treated as a new source of packets. However, a carefully +// crafted packet can trigger an infinite recursive sequence of packets. See +// http://mumble.net/~campbell/misc/pgp-quine +// https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2013-4402 +// This constant limits the number of recursive packets that may be pushed. +const maxReaders = 32 + +// Next returns the most recently unread Packet, or reads another packet from +// the top-most io.Reader. Unknown packet types are skipped. +func (r *Reader) Next() (p Packet, err error) { + if len(r.q) > 0 { + p = r.q[len(r.q)-1] + r.q = r.q[:len(r.q)-1] + return + } + + for len(r.readers) > 0 { + p, err = Read(r.readers[len(r.readers)-1]) + if err == nil { + return + } + if err == io.EOF { + r.readers = r.readers[:len(r.readers)-1] + continue + } + // TODO: Add strict mode that rejects unknown packets, instead of ignoring them. + if _, ok := err.(errors.UnknownPacketTypeError); !ok { + return nil, err + } + } + + return nil, io.EOF +} + +// Push causes the Reader to start reading from a new io.Reader. When an EOF +// error is seen from the new io.Reader, it is popped and the Reader continues +// to read from the next most recent io.Reader. Push returns a StructuralError +// if pushing the reader would exceed the maximum recursion level, otherwise it +// returns nil. +func (r *Reader) Push(reader io.Reader) (err error) { + if len(r.readers) >= maxReaders { + return errors.StructuralError("too many layers of packets") + } + r.readers = append(r.readers, reader) + return nil +} + +// Unread causes the given Packet to be returned from the next call to Next. +func (r *Reader) Unread(p Packet) { + r.q = append(r.q, p) +} + +func NewReader(r io.Reader) *Reader { + return &Reader{ + q: nil, + readers: []io.Reader{r}, + } +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/signature.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/signature.go new file mode 100644 index 0000000000000..6b1704e4292f9 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/signature.go @@ -0,0 +1,964 @@ +// Copyright 2011 The 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 packet + +import ( + "bytes" + "crypto" + "crypto/dsa" + "crypto/ecdsa" + "encoding/asn1" + "encoding/binary" + "hash" + "io" + "math/big" + "strconv" + "time" + + "github.com/ProtonMail/go-crypto/openpgp/errors" + "github.com/ProtonMail/go-crypto/openpgp/internal/encoding" + "github.com/ProtonMail/go-crypto/openpgp/s2k" +) + +const ( + // See RFC 4880, section 5.2.3.21 for details. + KeyFlagCertify = 1 << iota + KeyFlagSign + KeyFlagEncryptCommunications + KeyFlagEncryptStorage +) + +// Signature represents a signature. See RFC 4880, section 5.2. +type Signature struct { + Version int + SigType SignatureType + PubKeyAlgo PublicKeyAlgorithm + Hash crypto.Hash + + // HashSuffix is extra data that is hashed in after the signed data. + HashSuffix []byte + // HashTag contains the first two bytes of the hash for fast rejection + // of bad signed data. + HashTag [2]byte + + // Metadata includes format, filename and time, and is protected by v5 + // signatures of type 0x00 or 0x01. This metadata is included into the hash + // computation; if nil, six 0x00 bytes are used instead. See section 5.2.4. + Metadata *LiteralData + + CreationTime time.Time + + RSASignature encoding.Field + DSASigR, DSASigS encoding.Field + ECDSASigR, ECDSASigS encoding.Field + EdDSASigR, EdDSASigS encoding.Field + + // rawSubpackets contains the unparsed subpackets, in order. + rawSubpackets []outputSubpacket + + // The following are optional so are nil when not included in the + // signature. + + SigLifetimeSecs, KeyLifetimeSecs *uint32 + PreferredSymmetric, PreferredHash, PreferredCompression []uint8 + PreferredAEAD []uint8 + IssuerKeyId *uint64 + IssuerFingerprint []byte + IsPrimaryId *bool + + // FlagsValid is set if any flags were given. See RFC 4880, section + // 5.2.3.21 for details. + FlagsValid bool + FlagCertify, FlagSign, FlagEncryptCommunications, FlagEncryptStorage bool + + // RevocationReason is set if this signature has been revoked. + // See RFC 4880, section 5.2.3.23 for details. + RevocationReason *uint8 + RevocationReasonText string + + // In a self-signature, these flags are set there is a features subpacket + // indicating that the issuer implementation supports these features + // (section 5.2.5.25). + MDC, AEAD, V5Keys bool + + // EmbeddedSignature, if non-nil, is a signature of the parent key, by + // this key. This prevents an attacker from claiming another's signing + // subkey as their own. + EmbeddedSignature *Signature + + outSubpackets []outputSubpacket +} + +func (sig *Signature) parse(r io.Reader) (err error) { + // RFC 4880, section 5.2.3 + var buf [5]byte + _, err = readFull(r, buf[:1]) + if err != nil { + return + } + if buf[0] != 4 && buf[0] != 5 { + err = errors.UnsupportedError("signature packet version " + strconv.Itoa(int(buf[0]))) + return + } + sig.Version = int(buf[0]) + _, err = readFull(r, buf[:5]) + if err != nil { + return + } + sig.SigType = SignatureType(buf[0]) + sig.PubKeyAlgo = PublicKeyAlgorithm(buf[1]) + switch sig.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA, PubKeyAlgoEdDSA: + default: + err = errors.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo))) + return + } + + var ok bool + sig.Hash, ok = s2k.HashIdToHash(buf[2]) + if !ok { + return errors.UnsupportedError("hash function " + strconv.Itoa(int(buf[2]))) + } + + hashedSubpacketsLength := int(buf[3])<<8 | int(buf[4]) + hashedSubpackets := make([]byte, hashedSubpacketsLength) + _, err = readFull(r, hashedSubpackets) + if err != nil { + return + } + sig.buildHashSuffix(hashedSubpackets) + err = parseSignatureSubpackets(sig, hashedSubpackets, true) + if err != nil { + return + } + + _, err = readFull(r, buf[:2]) + if err != nil { + return + } + unhashedSubpacketsLength := int(buf[0])<<8 | int(buf[1]) + unhashedSubpackets := make([]byte, unhashedSubpacketsLength) + _, err = readFull(r, unhashedSubpackets) + if err != nil { + return + } + err = parseSignatureSubpackets(sig, unhashedSubpackets, false) + if err != nil { + return + } + + _, err = readFull(r, sig.HashTag[:2]) + if err != nil { + return + } + + switch sig.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: + sig.RSASignature = new(encoding.MPI) + _, err = sig.RSASignature.ReadFrom(r) + case PubKeyAlgoDSA: + sig.DSASigR = new(encoding.MPI) + if _, err = sig.DSASigR.ReadFrom(r); err != nil { + return + } + + sig.DSASigS = new(encoding.MPI) + _, err = sig.DSASigS.ReadFrom(r) + case PubKeyAlgoECDSA: + sig.ECDSASigR = new(encoding.MPI) + if _, err = sig.ECDSASigR.ReadFrom(r); err != nil { + return + } + + sig.ECDSASigS = new(encoding.MPI) + _, err = sig.ECDSASigS.ReadFrom(r) + case PubKeyAlgoEdDSA: + sig.EdDSASigR = new(encoding.MPI) + if _, err = sig.EdDSASigR.ReadFrom(r); err != nil { + return + } + + sig.EdDSASigS = new(encoding.MPI) + if _, err = sig.EdDSASigS.ReadFrom(r); err != nil { + return + } + default: + panic("unreachable") + } + return +} + +// parseSignatureSubpackets parses subpackets of the main signature packet. See +// RFC 4880, section 5.2.3.1. +func parseSignatureSubpackets(sig *Signature, subpackets []byte, isHashed bool) (err error) { + for len(subpackets) > 0 { + subpackets, err = parseSignatureSubpacket(sig, subpackets, isHashed) + if err != nil { + return + } + } + + if sig.CreationTime.IsZero() { + err = errors.StructuralError("no creation time in signature") + } + + return +} + +type signatureSubpacketType uint8 + +const ( + creationTimeSubpacket signatureSubpacketType = 2 + signatureExpirationSubpacket signatureSubpacketType = 3 + keyExpirationSubpacket signatureSubpacketType = 9 + prefSymmetricAlgosSubpacket signatureSubpacketType = 11 + issuerSubpacket signatureSubpacketType = 16 + prefHashAlgosSubpacket signatureSubpacketType = 21 + prefCompressionSubpacket signatureSubpacketType = 22 + primaryUserIdSubpacket signatureSubpacketType = 25 + keyFlagsSubpacket signatureSubpacketType = 27 + reasonForRevocationSubpacket signatureSubpacketType = 29 + featuresSubpacket signatureSubpacketType = 30 + embeddedSignatureSubpacket signatureSubpacketType = 32 + issuerFingerprintSubpacket signatureSubpacketType = 33 + prefAeadAlgosSubpacket signatureSubpacketType = 34 +) + +// parseSignatureSubpacket parses a single subpacket. len(subpacket) is >= 1. +func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (rest []byte, err error) { + // RFC 4880, section 5.2.3.1 + var ( + length uint32 + packetType signatureSubpacketType + isCritical bool + ) + switch { + case subpacket[0] < 192: + length = uint32(subpacket[0]) + subpacket = subpacket[1:] + case subpacket[0] < 255: + if len(subpacket) < 2 { + goto Truncated + } + length = uint32(subpacket[0]-192)<<8 + uint32(subpacket[1]) + 192 + subpacket = subpacket[2:] + default: + if len(subpacket) < 5 { + goto Truncated + } + length = uint32(subpacket[1])<<24 | + uint32(subpacket[2])<<16 | + uint32(subpacket[3])<<8 | + uint32(subpacket[4]) + subpacket = subpacket[5:] + } + if length > uint32(len(subpacket)) { + goto Truncated + } + rest = subpacket[length:] + subpacket = subpacket[:length] + if len(subpacket) == 0 { + err = errors.StructuralError("zero length signature subpacket") + return + } + packetType = signatureSubpacketType(subpacket[0] & 0x7f) + isCritical = subpacket[0]&0x80 == 0x80 + subpacket = subpacket[1:] + sig.rawSubpackets = append(sig.rawSubpackets, outputSubpacket{isHashed, packetType, isCritical, subpacket}) + switch packetType { + case creationTimeSubpacket: + if !isHashed { + err = errors.StructuralError("signature creation time in non-hashed area") + return + } + if len(subpacket) != 4 { + err = errors.StructuralError("signature creation time not four bytes") + return + } + t := binary.BigEndian.Uint32(subpacket) + sig.CreationTime = time.Unix(int64(t), 0) + case signatureExpirationSubpacket: + // Signature expiration time, section 5.2.3.10 + if !isHashed { + return + } + if len(subpacket) != 4 { + err = errors.StructuralError("expiration subpacket with bad length") + return + } + sig.SigLifetimeSecs = new(uint32) + *sig.SigLifetimeSecs = binary.BigEndian.Uint32(subpacket) + case keyExpirationSubpacket: + // Key expiration time, section 5.2.3.6 + if !isHashed { + return + } + if len(subpacket) != 4 { + err = errors.StructuralError("key expiration subpacket with bad length") + return + } + sig.KeyLifetimeSecs = new(uint32) + *sig.KeyLifetimeSecs = binary.BigEndian.Uint32(subpacket) + case prefSymmetricAlgosSubpacket: + // Preferred symmetric algorithms, section 5.2.3.7 + if !isHashed { + return + } + sig.PreferredSymmetric = make([]byte, len(subpacket)) + copy(sig.PreferredSymmetric, subpacket) + case issuerSubpacket: + if sig.Version > 4 { + err = errors.StructuralError("issuer subpacket found in v5 key") + } + // Issuer, section 5.2.3.5 + if len(subpacket) != 8 { + err = errors.StructuralError("issuer subpacket with bad length") + return + } + sig.IssuerKeyId = new(uint64) + *sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket) + case prefHashAlgosSubpacket: + // Preferred hash algorithms, section 5.2.3.8 + if !isHashed { + return + } + sig.PreferredHash = make([]byte, len(subpacket)) + copy(sig.PreferredHash, subpacket) + case prefCompressionSubpacket: + // Preferred compression algorithms, section 5.2.3.9 + if !isHashed { + return + } + sig.PreferredCompression = make([]byte, len(subpacket)) + copy(sig.PreferredCompression, subpacket) + case primaryUserIdSubpacket: + // Primary User ID, section 5.2.3.19 + if !isHashed { + return + } + if len(subpacket) != 1 { + err = errors.StructuralError("primary user id subpacket with bad length") + return + } + sig.IsPrimaryId = new(bool) + if subpacket[0] > 0 { + *sig.IsPrimaryId = true + } + case keyFlagsSubpacket: + // Key flags, section 5.2.3.21 + if !isHashed { + return + } + if len(subpacket) == 0 { + err = errors.StructuralError("empty key flags subpacket") + return + } + sig.FlagsValid = true + if subpacket[0]&KeyFlagCertify != 0 { + sig.FlagCertify = true + } + if subpacket[0]&KeyFlagSign != 0 { + sig.FlagSign = true + } + if subpacket[0]&KeyFlagEncryptCommunications != 0 { + sig.FlagEncryptCommunications = true + } + if subpacket[0]&KeyFlagEncryptStorage != 0 { + sig.FlagEncryptStorage = true + } + case reasonForRevocationSubpacket: + // Reason For Revocation, section 5.2.3.23 + if !isHashed { + return + } + if len(subpacket) == 0 { + err = errors.StructuralError("empty revocation reason subpacket") + return + } + sig.RevocationReason = new(uint8) + *sig.RevocationReason = subpacket[0] + sig.RevocationReasonText = string(subpacket[1:]) + case featuresSubpacket: + // Features subpacket, section 5.2.3.24 specifies a very general + // mechanism for OpenPGP implementations to signal support for new + // features. + if !isHashed { + return + } + if len(subpacket) > 0 { + if subpacket[0]&0x01 != 0 { + sig.MDC = true + } + if subpacket[0]&0x02 != 0 { + sig.AEAD = true + } + if subpacket[0]&0x04 != 0 { + sig.V5Keys = true + } + } + case embeddedSignatureSubpacket: + // Only usage is in signatures that cross-certify + // signing subkeys. section 5.2.3.26 describes the + // format, with its usage described in section 11.1 + if sig.EmbeddedSignature != nil { + err = errors.StructuralError("Cannot have multiple embedded signatures") + return + } + sig.EmbeddedSignature = new(Signature) + // Embedded signatures are required to be v4 signatures see + // section 12.1. However, we only parse v4 signatures in this + // file anyway. + if err := sig.EmbeddedSignature.parse(bytes.NewBuffer(subpacket)); err != nil { + return nil, err + } + if sigType := sig.EmbeddedSignature.SigType; sigType != SigTypePrimaryKeyBinding { + return nil, errors.StructuralError("cross-signature has unexpected type " + strconv.Itoa(int(sigType))) + } + case issuerFingerprintSubpacket: + v, l := subpacket[0], len(subpacket[1:]) + if v == 5 && l != 32 || v != 5 && l != 20 { + return nil, errors.StructuralError("bad fingerprint length") + } + sig.IssuerFingerprint = make([]byte, l) + copy(sig.IssuerFingerprint, subpacket[1:]) + sig.IssuerKeyId = new(uint64) + if v == 5 { + *sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket[1:9]) + } else { + *sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket[13:21]) + } + case prefAeadAlgosSubpacket: + // Preferred symmetric algorithms, section 5.2.3.8 + if !isHashed { + return + } + sig.PreferredAEAD = make([]byte, len(subpacket)) + copy(sig.PreferredAEAD, subpacket) + default: + if isCritical { + err = errors.UnsupportedError("unknown critical signature subpacket type " + strconv.Itoa(int(packetType))) + return + } + } + return + +Truncated: + err = errors.StructuralError("signature subpacket truncated") + return +} + +// subpacketLengthLength returns the length, in bytes, of an encoded length value. +func subpacketLengthLength(length int) int { + if length < 192 { + return 1 + } + if length < 16320 { + return 2 + } + return 5 +} + +func (sig *Signature) CheckKeyIdOrFingerprint(pk *PublicKey) bool { + if sig.IssuerFingerprint != nil && len(sig.IssuerFingerprint) >= 20 { + return bytes.Equal(sig.IssuerFingerprint, pk.Fingerprint) + } + return sig.IssuerKeyId != nil && *sig.IssuerKeyId == pk.KeyId +} + +// serializeSubpacketLength marshals the given length into to. +func serializeSubpacketLength(to []byte, length int) int { + // RFC 4880, Section 4.2.2. + if length < 192 { + to[0] = byte(length) + return 1 + } + if length < 16320 { + length -= 192 + to[0] = byte((length >> 8) + 192) + to[1] = byte(length) + return 2 + } + to[0] = 255 + to[1] = byte(length >> 24) + to[2] = byte(length >> 16) + to[3] = byte(length >> 8) + to[4] = byte(length) + return 5 +} + +// subpacketsLength returns the serialized length, in bytes, of the given +// subpackets. +func subpacketsLength(subpackets []outputSubpacket, hashed bool) (length int) { + for _, subpacket := range subpackets { + if subpacket.hashed == hashed { + length += subpacketLengthLength(len(subpacket.contents) + 1) + length += 1 // type byte + length += len(subpacket.contents) + } + } + return +} + +// serializeSubpackets marshals the given subpackets into to. +func serializeSubpackets(to []byte, subpackets []outputSubpacket, hashed bool) { + for _, subpacket := range subpackets { + if subpacket.hashed == hashed { + n := serializeSubpacketLength(to, len(subpacket.contents)+1) + to[n] = byte(subpacket.subpacketType) + to = to[1+n:] + n = copy(to, subpacket.contents) + to = to[n:] + } + } + return +} + +// SigExpired returns whether sig is a signature that has expired or is created +// in the future. +func (sig *Signature) SigExpired(currentTime time.Time) bool { + if sig.CreationTime.After(currentTime) { + return true + } + if sig.SigLifetimeSecs == nil || *sig.SigLifetimeSecs == 0 { + return false + } + expiry := sig.CreationTime.Add(time.Duration(*sig.SigLifetimeSecs) * time.Second) + return currentTime.After(expiry) +} + +// buildHashSuffix constructs the HashSuffix member of sig in preparation for signing. +func (sig *Signature) buildHashSuffix(hashedSubpackets []byte) (err error) { + hash, ok := s2k.HashToHashId(sig.Hash) + if !ok { + sig.HashSuffix = nil + return errors.InvalidArgumentError("hash cannot be represented in OpenPGP: " + strconv.Itoa(int(sig.Hash))) + } + + hashedFields := bytes.NewBuffer([]byte{ + uint8(sig.Version), + uint8(sig.SigType), + uint8(sig.PubKeyAlgo), + uint8(hash), + uint8(len(hashedSubpackets) >> 8), + uint8(len(hashedSubpackets)), + }) + hashedFields.Write(hashedSubpackets) + + var l uint64 = uint64(6 + len(hashedSubpackets)) + if sig.Version == 5 { + hashedFields.Write([]byte{0x05, 0xff}) + hashedFields.Write([]byte{ + uint8(l >> 56), uint8(l >> 48), uint8(l >> 40), uint8(l >> 32), + uint8(l >> 24), uint8(l >> 16), uint8(l >> 8), uint8(l), + }) + } else { + hashedFields.Write([]byte{0x04, 0xff}) + hashedFields.Write([]byte{ + uint8(l >> 24), uint8(l >> 16), uint8(l >> 8), uint8(l), + }) + } + sig.HashSuffix = make([]byte, hashedFields.Len()) + copy(sig.HashSuffix, hashedFields.Bytes()) + return +} + +func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err error) { + hashedSubpacketsLen := subpacketsLength(sig.outSubpackets, true) + hashedSubpackets := make([]byte, hashedSubpacketsLen) + serializeSubpackets(hashedSubpackets, sig.outSubpackets, true) + err = sig.buildHashSuffix(hashedSubpackets) + if err != nil { + return + } + if sig.Version == 5 && (sig.SigType == 0x00 || sig.SigType == 0x01) { + sig.AddMetadataToHashSuffix() + } + + h.Write(sig.HashSuffix) + digest = h.Sum(nil) + copy(sig.HashTag[:], digest) + return +} + +// Sign signs a message with a private key. The hash, h, must contain +// the hash of the message to be signed and will be mutated by this function. +// On success, the signature is stored in sig. Call Serialize to write it out. +// If config is nil, sensible defaults will be used. +func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err error) { + if priv.Dummy() { + return errors.ErrDummyPrivateKey("dummy key found") + } + sig.Version = priv.PublicKey.Version + sig.IssuerFingerprint = priv.PublicKey.Fingerprint + sig.outSubpackets, err = sig.buildSubpackets(priv.PublicKey) + if err != nil { + return err + } + digest, err := sig.signPrepareHash(h) + if err != nil { + return + } + switch priv.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: + // supports both *rsa.PrivateKey and crypto.Signer + sigdata, err := priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, sig.Hash) + if err == nil { + sig.RSASignature = encoding.NewMPI(sigdata) + } + case PubKeyAlgoDSA: + dsaPriv := priv.PrivateKey.(*dsa.PrivateKey) + + // Need to truncate hashBytes to match FIPS 186-3 section 4.6. + subgroupSize := (dsaPriv.Q.BitLen() + 7) / 8 + if len(digest) > subgroupSize { + digest = digest[:subgroupSize] + } + r, s, err := dsa.Sign(config.Random(), dsaPriv, digest) + if err == nil { + sig.DSASigR = new(encoding.MPI).SetBig(r) + sig.DSASigS = new(encoding.MPI).SetBig(s) + } + case PubKeyAlgoECDSA: + var r, s *big.Int + if pk, ok := priv.PrivateKey.(*ecdsa.PrivateKey); ok { + // direct support, avoid asn1 wrapping/unwrapping + r, s, err = ecdsa.Sign(config.Random(), pk, digest) + } else { + var b []byte + b, err = priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, sig.Hash) + if err == nil { + r, s, err = unwrapECDSASig(b) + } + } + if err == nil { + sig.ECDSASigR = new(encoding.MPI).SetBig(r) + sig.ECDSASigS = new(encoding.MPI).SetBig(s) + } + case PubKeyAlgoEdDSA: + sigdata, err := priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, crypto.Hash(0)) + if err == nil { + sig.EdDSASigR = encoding.NewMPI(sigdata[:32]) + sig.EdDSASigS = encoding.NewMPI(sigdata[32:]) + } + default: + err = errors.UnsupportedError("public key algorithm: " + strconv.Itoa(int(sig.PubKeyAlgo))) + } + + return +} + +// unwrapECDSASig parses the two integer components of an ASN.1-encoded ECDSA +// signature. +func unwrapECDSASig(b []byte) (r, s *big.Int, err error) { + var ecsdaSig struct { + R, S *big.Int + } + _, err = asn1.Unmarshal(b, &ecsdaSig) + if err != nil { + return + } + return ecsdaSig.R, ecsdaSig.S, nil +} + +// SignUserId computes a signature from priv, asserting that pub is a valid +// key for the identity id. On success, the signature is stored in sig. Call +// Serialize to write it out. +// If config is nil, sensible defaults will be used. +func (sig *Signature) SignUserId(id string, pub *PublicKey, priv *PrivateKey, config *Config) error { + if priv.Dummy() { + return errors.ErrDummyPrivateKey("dummy key found") + } + h, err := userIdSignatureHash(id, pub, sig.Hash) + if err != nil { + return err + } + return sig.Sign(h, priv, config) +} + +// CrossSignKey computes a signature from signingKey on pub hashed using hashKey. On success, +// the signature is stored in sig. Call Serialize to write it out. +// If config is nil, sensible defaults will be used. +func (sig *Signature) CrossSignKey(pub *PublicKey, hashKey *PublicKey, signingKey *PrivateKey, + config *Config) error { + h, err := keySignatureHash(hashKey, pub, sig.Hash) + if err != nil { + return err + } + return sig.Sign(h, signingKey, config) +} + +// SignKey computes a signature from priv, asserting that pub is a subkey. On +// success, the signature is stored in sig. Call Serialize to write it out. +// If config is nil, sensible defaults will be used. +func (sig *Signature) SignKey(pub *PublicKey, priv *PrivateKey, config *Config) error { + if priv.Dummy() { + return errors.ErrDummyPrivateKey("dummy key found") + } + h, err := keySignatureHash(&priv.PublicKey, pub, sig.Hash) + if err != nil { + return err + } + return sig.Sign(h, priv, config) +} + +// RevokeKey computes a revocation signature of pub using priv. On success, the signature is +// stored in sig. Call Serialize to write it out. +// If config is nil, sensible defaults will be used. +func (sig *Signature) RevokeKey(pub *PublicKey, priv *PrivateKey, config *Config) error { + h, err := keyRevocationHash(pub, sig.Hash) + if err != nil { + return err + } + return sig.Sign(h, priv, config) +} + +// Serialize marshals sig to w. Sign, SignUserId or SignKey must have been +// called first. +func (sig *Signature) Serialize(w io.Writer) (err error) { + if len(sig.outSubpackets) == 0 { + sig.outSubpackets = sig.rawSubpackets + } + if sig.RSASignature == nil && sig.DSASigR == nil && sig.ECDSASigR == nil && sig.EdDSASigR == nil { + return errors.InvalidArgumentError("Signature: need to call Sign, SignUserId or SignKey before Serialize") + } + + sigLength := 0 + switch sig.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: + sigLength = int(sig.RSASignature.EncodedLength()) + case PubKeyAlgoDSA: + sigLength = int(sig.DSASigR.EncodedLength()) + sigLength += int(sig.DSASigS.EncodedLength()) + case PubKeyAlgoECDSA: + sigLength = int(sig.ECDSASigR.EncodedLength()) + sigLength += int(sig.ECDSASigS.EncodedLength()) + case PubKeyAlgoEdDSA: + sigLength = int(sig.EdDSASigR.EncodedLength()) + sigLength += int(sig.EdDSASigS.EncodedLength()) + default: + panic("impossible") + } + + unhashedSubpacketsLen := subpacketsLength(sig.outSubpackets, false) + length := len(sig.HashSuffix) - 6 /* trailer not included */ + + 2 /* length of unhashed subpackets */ + unhashedSubpacketsLen + + 2 /* hash tag */ + sigLength + if sig.Version == 5 { + length -= 4 // eight-octet instead of four-octet big endian + } + err = serializeHeader(w, packetTypeSignature, length) + if err != nil { + return + } + err = sig.serializeBody(w) + if err != nil { + return err + } + return +} + +func (sig *Signature) serializeBody(w io.Writer) (err error) { + hashedSubpacketsLen := uint16(uint16(sig.HashSuffix[4])<<8) | uint16(sig.HashSuffix[5]) + fields := sig.HashSuffix[:6+hashedSubpacketsLen] + _, err = w.Write(fields) + if err != nil { + return + } + + unhashedSubpacketsLen := subpacketsLength(sig.outSubpackets, false) + unhashedSubpackets := make([]byte, 2+unhashedSubpacketsLen) + unhashedSubpackets[0] = byte(unhashedSubpacketsLen >> 8) + unhashedSubpackets[1] = byte(unhashedSubpacketsLen) + serializeSubpackets(unhashedSubpackets[2:], sig.outSubpackets, false) + + _, err = w.Write(unhashedSubpackets) + if err != nil { + return + } + _, err = w.Write(sig.HashTag[:]) + if err != nil { + return + } + + switch sig.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: + _, err = w.Write(sig.RSASignature.EncodedBytes()) + case PubKeyAlgoDSA: + if _, err = w.Write(sig.DSASigR.EncodedBytes()); err != nil { + return + } + _, err = w.Write(sig.DSASigS.EncodedBytes()) + case PubKeyAlgoECDSA: + if _, err = w.Write(sig.ECDSASigR.EncodedBytes()); err != nil { + return + } + _, err = w.Write(sig.ECDSASigS.EncodedBytes()) + case PubKeyAlgoEdDSA: + if _, err = w.Write(sig.EdDSASigR.EncodedBytes()); err != nil { + return + } + _, err = w.Write(sig.EdDSASigS.EncodedBytes()) + default: + panic("impossible") + } + return +} + +// outputSubpacket represents a subpacket to be marshaled. +type outputSubpacket struct { + hashed bool // true if this subpacket is in the hashed area. + subpacketType signatureSubpacketType + isCritical bool + contents []byte +} + +func (sig *Signature) buildSubpackets(issuer PublicKey) (subpackets []outputSubpacket, err error) { + creationTime := make([]byte, 4) + binary.BigEndian.PutUint32(creationTime, uint32(sig.CreationTime.Unix())) + subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, false, creationTime}) + + if sig.IssuerKeyId != nil && sig.Version == 4 { + keyId := make([]byte, 8) + binary.BigEndian.PutUint64(keyId, *sig.IssuerKeyId) + subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, true, keyId}) + } + if sig.IssuerFingerprint != nil { + contents := append([]uint8{uint8(issuer.Version)}, sig.IssuerFingerprint...) + subpackets = append(subpackets, outputSubpacket{true, issuerFingerprintSubpacket, true, contents}) + } + if sig.SigLifetimeSecs != nil && *sig.SigLifetimeSecs != 0 { + sigLifetime := make([]byte, 4) + binary.BigEndian.PutUint32(sigLifetime, *sig.SigLifetimeSecs) + subpackets = append(subpackets, outputSubpacket{true, signatureExpirationSubpacket, true, sigLifetime}) + } + + // Key flags may only appear in self-signatures or certification signatures. + + if sig.FlagsValid { + var flags byte + if sig.FlagCertify { + flags |= KeyFlagCertify + } + if sig.FlagSign { + flags |= KeyFlagSign + } + if sig.FlagEncryptCommunications { + flags |= KeyFlagEncryptCommunications + } + if sig.FlagEncryptStorage { + flags |= KeyFlagEncryptStorage + } + subpackets = append(subpackets, outputSubpacket{true, keyFlagsSubpacket, false, []byte{flags}}) + } + + // The following subpackets may only appear in self-signatures. + + var features = byte(0x00) + if sig.MDC { + features |= 0x01 + } + if sig.AEAD { + features |= 0x02 + } + if sig.V5Keys { + features |= 0x04 + } + + if features != 0x00 { + subpackets = append(subpackets, outputSubpacket{true, featuresSubpacket, false, []byte{features}}) + } + + if sig.KeyLifetimeSecs != nil && *sig.KeyLifetimeSecs != 0 { + keyLifetime := make([]byte, 4) + binary.BigEndian.PutUint32(keyLifetime, *sig.KeyLifetimeSecs) + subpackets = append(subpackets, outputSubpacket{true, keyExpirationSubpacket, true, keyLifetime}) + } + + if sig.IsPrimaryId != nil && *sig.IsPrimaryId { + subpackets = append(subpackets, outputSubpacket{true, primaryUserIdSubpacket, false, []byte{1}}) + } + + if len(sig.PreferredSymmetric) > 0 { + subpackets = append(subpackets, outputSubpacket{true, prefSymmetricAlgosSubpacket, false, sig.PreferredSymmetric}) + } + + if len(sig.PreferredHash) > 0 { + subpackets = append(subpackets, outputSubpacket{true, prefHashAlgosSubpacket, false, sig.PreferredHash}) + } + + if len(sig.PreferredCompression) > 0 { + subpackets = append(subpackets, outputSubpacket{true, prefCompressionSubpacket, false, sig.PreferredCompression}) + } + + if len(sig.PreferredAEAD) > 0 { + subpackets = append(subpackets, outputSubpacket{true, prefAeadAlgosSubpacket, false, sig.PreferredAEAD}) + } + + // Revocation reason appears only in revocation signatures and is serialized as per section 5.2.3.23. + if sig.RevocationReason != nil { + subpackets = append(subpackets, outputSubpacket{true, reasonForRevocationSubpacket, true, + append([]uint8{*sig.RevocationReason}, []uint8(sig.RevocationReasonText)...)}) + } + + // EmbeddedSignature appears only in subkeys capable of signing and is serialized as per section 5.2.3.26. + if sig.EmbeddedSignature != nil { + var buf bytes.Buffer + err = sig.EmbeddedSignature.serializeBody(&buf) + if err != nil { + return + } + subpackets = append(subpackets, outputSubpacket{true, embeddedSignatureSubpacket, true, buf.Bytes()}) + } + + return +} + +// AddMetadataToHashSuffix modifies the current hash suffix to include metadata +// (format, filename, and time). Version 5 keys protect this data including it +// in the hash computation. See section 5.2.4. +func (sig *Signature) AddMetadataToHashSuffix() { + if sig == nil || sig.Version != 5 { + return + } + if sig.SigType != 0x00 && sig.SigType != 0x01 { + return + } + lit := sig.Metadata + if lit == nil { + // This will translate into six 0x00 bytes. + lit = &LiteralData{} + } + + // Extract the current byte count + n := sig.HashSuffix[len(sig.HashSuffix)-8:] + l := uint64( + uint64(n[0])<<56 | uint64(n[1])<<48 | uint64(n[2])<<40 | uint64(n[3])<<32 | + uint64(n[4])<<24 | uint64(n[5])<<16 | uint64(n[6])<<8 | uint64(n[7])) + + suffix := bytes.NewBuffer(nil) + suffix.Write(sig.HashSuffix[:l]) + + // Add the metadata + var buf [4]byte + buf[0] = lit.Format + fileName := lit.FileName + if len(lit.FileName) > 255 { + fileName = fileName[:255] + } + buf[1] = byte(len(fileName)) + suffix.Write(buf[:2]) + suffix.Write([]byte(lit.FileName)) + binary.BigEndian.PutUint32(buf[:], lit.Time) + suffix.Write(buf[:]) + + // Update the counter and restore trailing bytes + l = uint64(suffix.Len()) + suffix.Write([]byte{0x05, 0xff}) + suffix.Write([]byte{ + uint8(l >> 56), uint8(l >> 48), uint8(l >> 40), uint8(l >> 32), + uint8(l >> 24), uint8(l >> 16), uint8(l >> 8), uint8(l), + }) + sig.HashSuffix = suffix.Bytes() +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetric_key_encrypted.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetric_key_encrypted.go new file mode 100644 index 0000000000000..0e9f51d51b39b --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetric_key_encrypted.go @@ -0,0 +1,267 @@ +// Copyright 2011 The 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 packet + +import ( + "bytes" + "crypto/cipher" + "io" + "strconv" + + "github.com/ProtonMail/go-crypto/openpgp/errors" + "github.com/ProtonMail/go-crypto/openpgp/s2k" +) + +// This is the largest session key that we'll support. Since no 512-bit cipher +// has even been seriously used, this is comfortably large. +const maxSessionKeySizeInBytes = 64 + +// SymmetricKeyEncrypted represents a passphrase protected session key. See RFC +// 4880, section 5.3. +type SymmetricKeyEncrypted struct { + Version int + CipherFunc CipherFunction + Mode AEADMode + s2k func(out, in []byte) + aeadNonce []byte + encryptedKey []byte +} + +func (ske *SymmetricKeyEncrypted) parse(r io.Reader) error { + // RFC 4880, section 5.3. + var buf [2]byte + if _, err := readFull(r, buf[:]); err != nil { + return err + } + ske.Version = int(buf[0]) + if ske.Version != 4 && ske.Version != 5 { + return errors.UnsupportedError("unknown SymmetricKeyEncrypted version") + } + ske.CipherFunc = CipherFunction(buf[1]) + if ske.CipherFunc.KeySize() == 0 { + return errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(buf[1]))) + } + + if ske.Version == 5 { + mode := make([]byte, 1) + if _, err := r.Read(mode); err != nil { + return errors.StructuralError("cannot read AEAD octect from packet") + } + ske.Mode = AEADMode(mode[0]) + } + + var err error + if ske.s2k, err = s2k.Parse(r); err != nil { + if _, ok := err.(errors.ErrDummyPrivateKey); ok { + return errors.UnsupportedError("missing key GNU extension in session key") + } + return err + } + + if ske.Version == 5 { + // AEAD nonce + nonce := make([]byte, ske.Mode.NonceLength()) + _, err := readFull(r, nonce) + if err != nil && err != io.ErrUnexpectedEOF { + return err + } + ske.aeadNonce = nonce + } + + encryptedKey := make([]byte, maxSessionKeySizeInBytes) + // The session key may follow. We just have to try and read to find + // out. If it exists then we limit it to maxSessionKeySizeInBytes. + n, err := readFull(r, encryptedKey) + if err != nil && err != io.ErrUnexpectedEOF { + return err + } + + if n != 0 { + if n == maxSessionKeySizeInBytes { + return errors.UnsupportedError("oversized encrypted session key") + } + ske.encryptedKey = encryptedKey[:n] + } + return nil +} + +// Decrypt attempts to decrypt an encrypted session key and returns the key and +// the cipher to use when decrypting a subsequent Symmetrically Encrypted Data +// packet. +func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) ([]byte, CipherFunction, error) { + key := make([]byte, ske.CipherFunc.KeySize()) + ske.s2k(key, passphrase) + if len(ske.encryptedKey) == 0 { + return key, ske.CipherFunc, nil + } + switch ske.Version { + case 4: + plaintextKey, cipherFunc, err := ske.decryptV4(key) + return plaintextKey, cipherFunc, err + case 5: + plaintextKey, err := ske.decryptV5(key) + return plaintextKey, CipherFunction(0), err + } + err := errors.UnsupportedError("unknown SymmetricKeyEncrypted version") + return nil, CipherFunction(0), err +} + +func (ske *SymmetricKeyEncrypted) decryptV4(key []byte) ([]byte, CipherFunction, error) { + // the IV is all zeros + iv := make([]byte, ske.CipherFunc.blockSize()) + c := cipher.NewCFBDecrypter(ske.CipherFunc.new(key), iv) + plaintextKey := make([]byte, len(ske.encryptedKey)) + c.XORKeyStream(plaintextKey, ske.encryptedKey) + cipherFunc := CipherFunction(plaintextKey[0]) + if cipherFunc.blockSize() == 0 { + return nil, ske.CipherFunc, errors.UnsupportedError( + "unknown cipher: " + strconv.Itoa(int(cipherFunc))) + } + plaintextKey = plaintextKey[1:] + if len(plaintextKey) != cipherFunc.KeySize() { + return nil, cipherFunc, errors.StructuralError( + "length of decrypted key not equal to cipher keysize") + } + return plaintextKey, cipherFunc, nil +} + +func (ske *SymmetricKeyEncrypted) decryptV5(key []byte) ([]byte, error) { + blockCipher := CipherFunction(ske.CipherFunc).new(key) + aead := ske.Mode.new(blockCipher) + + adata := []byte{0xc3, byte(5), byte(ske.CipherFunc), byte(ske.Mode)} + plaintextKey, err := aead.Open(nil, ske.aeadNonce, ske.encryptedKey, adata) + if err != nil { + return nil, err + } + return plaintextKey, nil +} + +// SerializeSymmetricKeyEncrypted serializes a symmetric key packet to w. +// The packet contains a random session key, encrypted by a key derived from +// the given passphrase. The session key is returned and must be passed to +// SerializeSymmetricallyEncrypted or SerializeAEADEncrypted, depending on +// whether config.AEADConfig != nil. +// If config is nil, sensible defaults will be used. +func SerializeSymmetricKeyEncrypted(w io.Writer, passphrase []byte, config *Config) (key []byte, err error) { + cipherFunc := config.Cipher() + keySize := cipherFunc.KeySize() + if keySize == 0 { + return nil, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc))) + } + + sessionKey := make([]byte, keySize) + _, err = io.ReadFull(config.Random(), sessionKey) + if err != nil { + return + } + + err = SerializeSymmetricKeyEncryptedReuseKey(w, sessionKey, passphrase, config) + if err != nil { + return + } + + key = sessionKey + return +} + +// SerializeSymmetricKeyEncryptedReuseKey serializes a symmetric key packet to w. +// The packet contains the given session key, encrypted by a key derived from +// the given passphrase. The session key must be passed to +// SerializeSymmetricallyEncrypted or SerializeAEADEncrypted, depending on +// whether config.AEADConfig != nil. +// If config is nil, sensible defaults will be used. +func SerializeSymmetricKeyEncryptedReuseKey(w io.Writer, sessionKey []byte, passphrase []byte, config *Config) (err error) { + var version int + if config.AEAD() != nil { + version = 5 + } else { + version = 4 + } + cipherFunc := config.Cipher() + keySize := cipherFunc.KeySize() + if keySize == 0 { + return errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc))) + } + + s2kBuf := new(bytes.Buffer) + keyEncryptingKey := make([]byte, keySize) + // s2k.Serialize salts and stretches the passphrase, and writes the + // resulting key to keyEncryptingKey and the s2k descriptor to s2kBuf. + err = s2k.Serialize(s2kBuf, keyEncryptingKey, config.Random(), passphrase, &s2k.Config{Hash: config.Hash(), S2KCount: config.PasswordHashIterations()}) + if err != nil { + return + } + s2kBytes := s2kBuf.Bytes() + + var packetLength int + switch version { + case 4: + packetLength = 2 /* header */ + len(s2kBytes) + 1 /* cipher type */ + keySize + case 5: + nonceLen := config.AEAD().Mode().NonceLength() + tagLen := config.AEAD().Mode().TagLength() + packetLength = 3 + len(s2kBytes) + nonceLen + keySize + tagLen + } + err = serializeHeader(w, packetTypeSymmetricKeyEncrypted, packetLength) + if err != nil { + return + } + + buf := make([]byte, 2) + // Symmetric Key Encrypted Version + buf[0] = byte(version) + // Cipher function + buf[1] = byte(cipherFunc) + + if version == 5 { + // AEAD mode + buf = append(buf, byte(config.AEAD().Mode())) + } + _, err = w.Write(buf) + if err != nil { + return + } + _, err = w.Write(s2kBytes) + if err != nil { + return + } + + switch version { + case 4: + iv := make([]byte, cipherFunc.blockSize()) + c := cipher.NewCFBEncrypter(cipherFunc.new(keyEncryptingKey), iv) + encryptedCipherAndKey := make([]byte, keySize+1) + c.XORKeyStream(encryptedCipherAndKey, buf[1:]) + c.XORKeyStream(encryptedCipherAndKey[1:], sessionKey) + _, err = w.Write(encryptedCipherAndKey) + if err != nil { + return + } + case 5: + blockCipher := cipherFunc.new(keyEncryptingKey) + mode := config.AEAD().Mode() + aead := mode.new(blockCipher) + // Sample nonce using random reader + nonce := make([]byte, config.AEAD().Mode().NonceLength()) + _, err = io.ReadFull(config.Random(), nonce) + if err != nil { + return + } + // Seal and write (encryptedData includes auth. tag) + adata := []byte{0xc3, byte(5), byte(cipherFunc), byte(mode)} + encryptedData := aead.Seal(nil, nonce, sessionKey, adata) + _, err = w.Write(nonce) + if err != nil { + return + } + _, err = w.Write(encryptedData) + if err != nil { + return + } + } + + return +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted.go new file mode 100644 index 0000000000000..8b84de177f99d --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted.go @@ -0,0 +1,290 @@ +// Copyright 2011 The 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 packet + +import ( + "crypto/cipher" + "crypto/sha1" + "crypto/subtle" + "hash" + "io" + "strconv" + + "github.com/ProtonMail/go-crypto/openpgp/errors" +) + +// SymmetricallyEncrypted represents a symmetrically encrypted byte string. The +// encrypted Contents will consist of more OpenPGP packets. See RFC 4880, +// sections 5.7 and 5.13. +type SymmetricallyEncrypted struct { + MDC bool // true iff this is a type 18 packet and thus has an embedded MAC. + Contents io.Reader + prefix []byte +} + +const symmetricallyEncryptedVersion = 1 + +func (se *SymmetricallyEncrypted) parse(r io.Reader) error { + if se.MDC { + // See RFC 4880, section 5.13. + var buf [1]byte + _, err := readFull(r, buf[:]) + if err != nil { + return err + } + if buf[0] != symmetricallyEncryptedVersion { + return errors.UnsupportedError("unknown SymmetricallyEncrypted version") + } + } + se.Contents = r + return nil +} + +// Decrypt returns a ReadCloser, from which the decrypted Contents of the +// packet can be read. An incorrect key will only be detected after trying +// to decrypt the entire data. +func (se *SymmetricallyEncrypted) Decrypt(c CipherFunction, key []byte) (io.ReadCloser, error) { + keySize := c.KeySize() + if keySize == 0 { + return nil, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(c))) + } + if len(key) != keySize { + return nil, errors.InvalidArgumentError("SymmetricallyEncrypted: incorrect key length") + } + + if se.prefix == nil { + se.prefix = make([]byte, c.blockSize()+2) + _, err := readFull(se.Contents, se.prefix) + if err != nil { + return nil, err + } + } else if len(se.prefix) != c.blockSize()+2 { + return nil, errors.InvalidArgumentError("can't try ciphers with different block lengths") + } + + ocfbResync := OCFBResync + if se.MDC { + // MDC packets use a different form of OCFB mode. + ocfbResync = OCFBNoResync + } + + s := NewOCFBDecrypter(c.new(key), se.prefix, ocfbResync) + + plaintext := cipher.StreamReader{S: s, R: se.Contents} + + if se.MDC { + // MDC packets have an embedded hash that we need to check. + h := sha1.New() + h.Write(se.prefix) + return &seMDCReader{in: plaintext, h: h}, nil + } + + // Otherwise, we just need to wrap plaintext so that it's a valid ReadCloser. + return seReader{plaintext}, nil +} + +// seReader wraps an io.Reader with a no-op Close method. +type seReader struct { + in io.Reader +} + +func (ser seReader) Read(buf []byte) (int, error) { + return ser.in.Read(buf) +} + +func (ser seReader) Close() error { + return nil +} + +const mdcTrailerSize = 1 /* tag byte */ + 1 /* length byte */ + sha1.Size + +// An seMDCReader wraps an io.Reader, maintains a running hash and keeps hold +// of the most recent 22 bytes (mdcTrailerSize). Upon EOF, those bytes form an +// MDC packet containing a hash of the previous Contents which is checked +// against the running hash. See RFC 4880, section 5.13. +type seMDCReader struct { + in io.Reader + h hash.Hash + trailer [mdcTrailerSize]byte + scratch [mdcTrailerSize]byte + trailerUsed int + error bool + eof bool +} + +func (ser *seMDCReader) Read(buf []byte) (n int, err error) { + if ser.error { + err = io.ErrUnexpectedEOF + return + } + if ser.eof { + err = io.EOF + return + } + + // If we haven't yet filled the trailer buffer then we must do that + // first. + for ser.trailerUsed < mdcTrailerSize { + n, err = ser.in.Read(ser.trailer[ser.trailerUsed:]) + ser.trailerUsed += n + if err == io.EOF { + if ser.trailerUsed != mdcTrailerSize { + n = 0 + err = io.ErrUnexpectedEOF + ser.error = true + return + } + ser.eof = true + n = 0 + return + } + + if err != nil { + n = 0 + return + } + } + + // If it's a short read then we read into a temporary buffer and shift + // the data into the caller's buffer. + if len(buf) <= mdcTrailerSize { + n, err = readFull(ser.in, ser.scratch[:len(buf)]) + copy(buf, ser.trailer[:n]) + ser.h.Write(buf[:n]) + copy(ser.trailer[:], ser.trailer[n:]) + copy(ser.trailer[mdcTrailerSize-n:], ser.scratch[:]) + if n < len(buf) { + ser.eof = true + err = io.EOF + } + return + } + + n, err = ser.in.Read(buf[mdcTrailerSize:]) + copy(buf, ser.trailer[:]) + ser.h.Write(buf[:n]) + copy(ser.trailer[:], buf[n:]) + + if err == io.EOF { + ser.eof = true + } + return +} + +// This is a new-format packet tag byte for a type 19 (MDC) packet. +const mdcPacketTagByte = byte(0x80) | 0x40 | 19 + +func (ser *seMDCReader) Close() error { + if ser.error { + return errors.ErrMDCMissing + } + + for !ser.eof { + // We haven't seen EOF so we need to read to the end + var buf [1024]byte + _, err := ser.Read(buf[:]) + if err == io.EOF { + break + } + if err != nil { + return errors.ErrMDCMissing + } + } + + ser.h.Write(ser.trailer[:2]) + + final := ser.h.Sum(nil) + if subtle.ConstantTimeCompare(final, ser.trailer[2:]) != 1 { + return errors.ErrMDCHashMismatch + } + // The hash already includes the MDC header, but we still check its value + // to confirm encryption correctness + if ser.trailer[0] != mdcPacketTagByte || ser.trailer[1] != sha1.Size { + return errors.ErrMDCMissing + } + return nil +} + +// An seMDCWriter writes through to an io.WriteCloser while maintains a running +// hash of the data written. On close, it emits an MDC packet containing the +// running hash. +type seMDCWriter struct { + w io.WriteCloser + h hash.Hash +} + +func (w *seMDCWriter) Write(buf []byte) (n int, err error) { + w.h.Write(buf) + return w.w.Write(buf) +} + +func (w *seMDCWriter) Close() (err error) { + var buf [mdcTrailerSize]byte + + buf[0] = mdcPacketTagByte + buf[1] = sha1.Size + w.h.Write(buf[:2]) + digest := w.h.Sum(nil) + copy(buf[2:], digest) + + _, err = w.w.Write(buf[:]) + if err != nil { + return + } + return w.w.Close() +} + +// noOpCloser is like an ioutil.NopCloser, but for an io.Writer. +type noOpCloser struct { + w io.Writer +} + +func (c noOpCloser) Write(data []byte) (n int, err error) { + return c.w.Write(data) +} + +func (c noOpCloser) Close() error { + return nil +} + +// SerializeSymmetricallyEncrypted serializes a symmetrically encrypted packet +// to w and returns a WriteCloser to which the to-be-encrypted packets can be +// written. +// If config is nil, sensible defaults will be used. +func SerializeSymmetricallyEncrypted(w io.Writer, c CipherFunction, key []byte, config *Config) (Contents io.WriteCloser, err error) { + if c.KeySize() != len(key) { + return nil, errors.InvalidArgumentError("SymmetricallyEncrypted.Serialize: bad key length") + } + writeCloser := noOpCloser{w} + ciphertext, err := serializeStreamHeader(writeCloser, packetTypeSymmetricallyEncryptedMDC) + if err != nil { + return + } + + _, err = ciphertext.Write([]byte{symmetricallyEncryptedVersion}) + if err != nil { + return + } + + block := c.new(key) + blockSize := block.BlockSize() + iv := make([]byte, blockSize) + _, err = config.Random().Read(iv) + if err != nil { + return + } + s, prefix := NewOCFBEncrypter(block, iv, OCFBNoResync) + _, err = ciphertext.Write(prefix) + if err != nil { + return + } + plaintext := cipher.StreamWriter{S: s, W: ciphertext} + + h := sha1.New() + h.Write(iv) + h.Write(iv[blockSize-2:]) + Contents = &seMDCWriter{w: plaintext, h: h} + return +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userattribute.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userattribute.go new file mode 100644 index 0000000000000..0f760ade2cff8 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userattribute.go @@ -0,0 +1,94 @@ +// Copyright 2013 The 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 packet + +import ( + "bytes" + "image" + "image/jpeg" + "io" + "io/ioutil" +) + +const UserAttrImageSubpacket = 1 + +// UserAttribute is capable of storing other types of data about a user +// beyond name, email and a text comment. In practice, user attributes are typically used +// to store a signed thumbnail photo JPEG image of the user. +// See RFC 4880, section 5.12. +type UserAttribute struct { + Contents []*OpaqueSubpacket +} + +// NewUserAttributePhoto creates a user attribute packet +// containing the given images. +func NewUserAttributePhoto(photos ...image.Image) (uat *UserAttribute, err error) { + uat = new(UserAttribute) + for _, photo := range photos { + var buf bytes.Buffer + // RFC 4880, Section 5.12.1. + data := []byte{ + 0x10, 0x00, // Little-endian image header length (16 bytes) + 0x01, // Image header version 1 + 0x01, // JPEG + 0, 0, 0, 0, // 12 reserved octets, must be all zero. + 0, 0, 0, 0, + 0, 0, 0, 0} + if _, err = buf.Write(data); err != nil { + return + } + if err = jpeg.Encode(&buf, photo, nil); err != nil { + return + } + uat.Contents = append(uat.Contents, &OpaqueSubpacket{ + SubType: UserAttrImageSubpacket, + Contents: buf.Bytes()}) + } + return +} + +// NewUserAttribute creates a new user attribute packet containing the given subpackets. +func NewUserAttribute(contents ...*OpaqueSubpacket) *UserAttribute { + return &UserAttribute{Contents: contents} +} + +func (uat *UserAttribute) parse(r io.Reader) (err error) { + // RFC 4880, section 5.13 + b, err := ioutil.ReadAll(r) + if err != nil { + return + } + uat.Contents, err = OpaqueSubpackets(b) + return +} + +// Serialize marshals the user attribute to w in the form of an OpenPGP packet, including +// header. +func (uat *UserAttribute) Serialize(w io.Writer) (err error) { + var buf bytes.Buffer + for _, sp := range uat.Contents { + err = sp.Serialize(&buf) + if err != nil { + return err + } + } + if err = serializeHeader(w, packetTypeUserAttribute, buf.Len()); err != nil { + return err + } + _, err = w.Write(buf.Bytes()) + return +} + +// ImageData returns zero or more byte slices, each containing +// JPEG File Interchange Format (JFIF), for each photo in the +// user attribute packet. +func (uat *UserAttribute) ImageData() (imageData [][]byte) { + for _, sp := range uat.Contents { + if sp.SubType == UserAttrImageSubpacket && len(sp.Contents) > 16 { + imageData = append(imageData, sp.Contents[16:]) + } + } + return +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userid.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userid.go new file mode 100644 index 0000000000000..d6bea7d4acc07 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userid.go @@ -0,0 +1,160 @@ +// Copyright 2011 The 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 packet + +import ( + "io" + "io/ioutil" + "strings" +) + +// UserId contains text that is intended to represent the name and email +// address of the key holder. See RFC 4880, section 5.11. By convention, this +// takes the form "Full Name (Comment) " +type UserId struct { + Id string // By convention, this takes the form "Full Name (Comment) " which is split out in the fields below. + + Name, Comment, Email string +} + +func hasInvalidCharacters(s string) bool { + for _, c := range s { + switch c { + case '(', ')', '<', '>', 0: + return true + } + } + return false +} + +// NewUserId returns a UserId or nil if any of the arguments contain invalid +// characters. The invalid characters are '\x00', '(', ')', '<' and '>' +func NewUserId(name, comment, email string) *UserId { + // RFC 4880 doesn't deal with the structure of userid strings; the + // name, comment and email form is just a convention. However, there's + // no convention about escaping the metacharacters and GPG just refuses + // to create user ids where, say, the name contains a '('. We mirror + // this behaviour. + + if hasInvalidCharacters(name) || hasInvalidCharacters(comment) || hasInvalidCharacters(email) { + return nil + } + + uid := new(UserId) + uid.Name, uid.Comment, uid.Email = name, comment, email + uid.Id = name + if len(comment) > 0 { + if len(uid.Id) > 0 { + uid.Id += " " + } + uid.Id += "(" + uid.Id += comment + uid.Id += ")" + } + if len(email) > 0 { + if len(uid.Id) > 0 { + uid.Id += " " + } + uid.Id += "<" + uid.Id += email + uid.Id += ">" + } + return uid +} + +func (uid *UserId) parse(r io.Reader) (err error) { + // RFC 4880, section 5.11 + b, err := ioutil.ReadAll(r) + if err != nil { + return + } + uid.Id = string(b) + uid.Name, uid.Comment, uid.Email = parseUserId(uid.Id) + return +} + +// Serialize marshals uid to w in the form of an OpenPGP packet, including +// header. +func (uid *UserId) Serialize(w io.Writer) error { + err := serializeHeader(w, packetTypeUserId, len(uid.Id)) + if err != nil { + return err + } + _, err = w.Write([]byte(uid.Id)) + return err +} + +// parseUserId extracts the name, comment and email from a user id string that +// is formatted as "Full Name (Comment) ". +func parseUserId(id string) (name, comment, email string) { + var n, c, e struct { + start, end int + } + var state int + + for offset, rune := range id { + switch state { + case 0: + // Entering name + n.start = offset + state = 1 + fallthrough + case 1: + // In name + if rune == '(' { + state = 2 + n.end = offset + } else if rune == '<' { + state = 5 + n.end = offset + } + case 2: + // Entering comment + c.start = offset + state = 3 + fallthrough + case 3: + // In comment + if rune == ')' { + state = 4 + c.end = offset + } + case 4: + // Between comment and email + if rune == '<' { + state = 5 + } + case 5: + // Entering email + e.start = offset + state = 6 + fallthrough + case 6: + // In email + if rune == '>' { + state = 7 + e.end = offset + } + default: + // After email + } + } + switch state { + case 1: + // ended in the name + n.end = len(id) + case 3: + // ended in comment + c.end = len(id) + case 6: + // ended in email + e.end = len(id) + } + + name = strings.TrimSpace(id[n.start:n.end]) + comment = strings.TrimSpace(id[c.start:c.end]) + email = strings.TrimSpace(id[e.start:e.end]) + return +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/read.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/read.go new file mode 100644 index 0000000000000..a649ebbab397a --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/read.go @@ -0,0 +1,508 @@ +// Copyright 2011 The 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 openpgp implements high level operations on OpenPGP messages. +package openpgp // import "github.com/ProtonMail/go-crypto/openpgp" + +import ( + "crypto" + _ "crypto/sha256" + "hash" + "io" + "strconv" + + "github.com/ProtonMail/go-crypto/openpgp/armor" + "github.com/ProtonMail/go-crypto/openpgp/errors" + "github.com/ProtonMail/go-crypto/openpgp/packet" +) + +// SignatureType is the armor type for a PGP signature. +var SignatureType = "PGP SIGNATURE" + +// readArmored reads an armored block with the given type. +func readArmored(r io.Reader, expectedType string) (body io.Reader, err error) { + block, err := armor.Decode(r) + if err != nil { + return + } + + if block.Type != expectedType { + return nil, errors.InvalidArgumentError("expected '" + expectedType + "', got: " + block.Type) + } + + return block.Body, nil +} + +// MessageDetails contains the result of parsing an OpenPGP encrypted and/or +// signed message. +type MessageDetails struct { + IsEncrypted bool // true if the message was encrypted. + EncryptedToKeyIds []uint64 // the list of recipient key ids. + IsSymmetricallyEncrypted bool // true if a passphrase could have decrypted the message. + DecryptedWith Key // the private key used to decrypt the message, if any. + IsSigned bool // true if the message is signed. + SignedByKeyId uint64 // the key id of the signer, if any. + SignedBy *Key // the key of the signer, if available. + LiteralData *packet.LiteralData // the metadata of the contents + UnverifiedBody io.Reader // the contents of the message. + + // If IsSigned is true and SignedBy is non-zero then the signature will + // be verified as UnverifiedBody is read. The signature cannot be + // checked until the whole of UnverifiedBody is read so UnverifiedBody + // must be consumed until EOF before the data can be trusted. Even if a + // message isn't signed (or the signer is unknown) the data may contain + // an authentication code that is only checked once UnverifiedBody has + // been consumed. Once EOF has been seen, the following fields are + // valid. (An authentication code failure is reported as a + // SignatureError error when reading from UnverifiedBody.) + Signature *packet.Signature // the signature packet itself. + SignatureError error // nil if the signature is good. + UnverifiedSignatures []*packet.Signature // all other unverified signature packets. + + decrypted io.ReadCloser +} + +// A PromptFunction is used as a callback by functions that may need to decrypt +// a private key, or prompt for a passphrase. It is called with a list of +// acceptable, encrypted private keys and a boolean that indicates whether a +// passphrase is usable. It should either decrypt a private key or return a +// passphrase to try. If the decrypted private key or given passphrase isn't +// correct, the function will be called again, forever. Any error returned will +// be passed up. +type PromptFunction func(keys []Key, symmetric bool) ([]byte, error) + +// A keyEnvelopePair is used to store a private key with the envelope that +// contains a symmetric key, encrypted with that key. +type keyEnvelopePair struct { + key Key + encryptedKey *packet.EncryptedKey +} + +// ReadMessage parses an OpenPGP message that may be signed and/or encrypted. +// The given KeyRing should contain both public keys (for signature +// verification) and, possibly encrypted, private keys for decrypting. +// If config is nil, sensible defaults will be used. +func ReadMessage(r io.Reader, keyring KeyRing, prompt PromptFunction, config *packet.Config) (md *MessageDetails, err error) { + var p packet.Packet + + var symKeys []*packet.SymmetricKeyEncrypted + var pubKeys []keyEnvelopePair + // Integrity protected encrypted packet: SymmetricallyEncrypted or AEADEncrypted + var edp packet.EncryptedDataPacket + + packets := packet.NewReader(r) + md = new(MessageDetails) + md.IsEncrypted = true + + // The message, if encrypted, starts with a number of packets + // containing an encrypted decryption key. The decryption key is either + // encrypted to a public key, or with a passphrase. This loop + // collects these packets. +ParsePackets: + for { + p, err = packets.Next() + if err != nil { + return nil, err + } + switch p := p.(type) { + case *packet.SymmetricKeyEncrypted: + // This packet contains the decryption key encrypted with a passphrase. + md.IsSymmetricallyEncrypted = true + symKeys = append(symKeys, p) + case *packet.EncryptedKey: + // This packet contains the decryption key encrypted to a public key. + md.EncryptedToKeyIds = append(md.EncryptedToKeyIds, p.KeyId) + switch p.Algo { + case packet.PubKeyAlgoRSA, packet.PubKeyAlgoRSAEncryptOnly, packet.PubKeyAlgoElGamal, packet.PubKeyAlgoECDH: + break + default: + continue + } + var keys []Key + if p.KeyId == 0 { + keys = keyring.DecryptionKeys() + } else { + keys = keyring.KeysById(p.KeyId) + } + for _, k := range keys { + pubKeys = append(pubKeys, keyEnvelopePair{k, p}) + } + case *packet.SymmetricallyEncrypted, *packet.AEADEncrypted: + edp = p.(packet.EncryptedDataPacket) + break ParsePackets + case *packet.Compressed, *packet.LiteralData, *packet.OnePassSignature: + // This message isn't encrypted. + if len(symKeys) != 0 || len(pubKeys) != 0 { + return nil, errors.StructuralError("key material not followed by encrypted message") + } + packets.Unread(p) + return readSignedMessage(packets, nil, keyring, config) + } + } + + var candidates []Key + var decrypted io.ReadCloser + + // Now that we have the list of encrypted keys we need to decrypt at + // least one of them or, if we cannot, we need to call the prompt + // function so that it can decrypt a key or give us a passphrase. +FindKey: + for { + // See if any of the keys already have a private key available + candidates = candidates[:0] + candidateFingerprints := make(map[string]bool) + + for _, pk := range pubKeys { + if pk.key.PrivateKey == nil { + continue + } + if !pk.key.PrivateKey.Encrypted { + if len(pk.encryptedKey.Key) == 0 { + errDec := pk.encryptedKey.Decrypt(pk.key.PrivateKey, config) + if errDec != nil { + continue + } + } + // Try to decrypt symmetrically encrypted + decrypted, err = edp.Decrypt(pk.encryptedKey.CipherFunc, pk.encryptedKey.Key) + if err != nil && err != errors.ErrKeyIncorrect { + return nil, err + } + if decrypted != nil { + md.DecryptedWith = pk.key + break FindKey + } + } else { + fpr := string(pk.key.PublicKey.Fingerprint[:]) + if v := candidateFingerprints[fpr]; v { + continue + } + candidates = append(candidates, pk.key) + candidateFingerprints[fpr] = true + } + } + + if len(candidates) == 0 && len(symKeys) == 0 { + return nil, errors.ErrKeyIncorrect + } + + if prompt == nil { + return nil, errors.ErrKeyIncorrect + } + + passphrase, err := prompt(candidates, len(symKeys) != 0) + if err != nil { + return nil, err + } + + // Try the symmetric passphrase first + if len(symKeys) != 0 && passphrase != nil { + for _, s := range symKeys { + key, cipherFunc, err := s.Decrypt(passphrase) + // On wrong passphrase, session key decryption is very likely to result in an invalid cipherFunc: + // only for < 5% of cases we will proceed to decrypt the data + if err == nil { + decrypted, err = edp.Decrypt(cipherFunc, key) + // TODO: ErrKeyIncorrect is no longer thrown on SEIP decryption, + // but it might still be relevant for when we implement AEAD decryption (otherwise, remove?) + if err != nil && err != errors.ErrKeyIncorrect { + return nil, err + } + if decrypted != nil { + break FindKey + } + } + } + } + } + + md.decrypted = decrypted + if err := packets.Push(decrypted); err != nil { + return nil, err + } + mdFinal, sensitiveParsingErr := readSignedMessage(packets, md, keyring, config) + if sensitiveParsingErr != nil { + return nil, errors.StructuralError("parsing error") + } + return mdFinal, nil +} + +// readSignedMessage reads a possibly signed message if mdin is non-zero then +// that structure is updated and returned. Otherwise a fresh MessageDetails is +// used. +func readSignedMessage(packets *packet.Reader, mdin *MessageDetails, keyring KeyRing, config *packet.Config) (md *MessageDetails, err error) { + if mdin == nil { + mdin = new(MessageDetails) + } + md = mdin + + var p packet.Packet + var h hash.Hash + var wrappedHash hash.Hash + var prevLast bool +FindLiteralData: + for { + p, err = packets.Next() + if err != nil { + return nil, err + } + switch p := p.(type) { + case *packet.Compressed: + if err := packets.Push(p.Body); err != nil { + return nil, err + } + case *packet.OnePassSignature: + if prevLast { + return nil, errors.UnsupportedError("nested signature packets") + } + + if p.IsLast { + prevLast = true + } + + h, wrappedHash, err = hashForSignature(p.Hash, p.SigType) + if err != nil { + md.SignatureError = err + } + + md.IsSigned = true + md.SignedByKeyId = p.KeyId + keys := keyring.KeysByIdUsage(p.KeyId, packet.KeyFlagSign) + if len(keys) > 0 { + md.SignedBy = &keys[0] + } + case *packet.LiteralData: + md.LiteralData = p + break FindLiteralData + } + } + + if md.IsSigned && md.SignatureError == nil { + md.UnverifiedBody = &signatureCheckReader{packets, h, wrappedHash, md, config} + } else if md.decrypted != nil { + md.UnverifiedBody = checkReader{md} + } else { + md.UnverifiedBody = md.LiteralData.Body + } + + return md, nil +} + +// hashForSignature returns a pair of hashes that can be used to verify a +// signature. The signature may specify that the contents of the signed message +// should be preprocessed (i.e. to normalize line endings). Thus this function +// returns two hashes. The second should be used to hash the message itself and +// performs any needed preprocessing. +func hashForSignature(hashId crypto.Hash, sigType packet.SignatureType) (hash.Hash, hash.Hash, error) { + if hashId == crypto.MD5 { + return nil, nil, errors.UnsupportedError("insecure hash algorithm: MD5") + } + if !hashId.Available() { + return nil, nil, errors.UnsupportedError("hash not available: " + strconv.Itoa(int(hashId))) + } + h := hashId.New() + + switch sigType { + case packet.SigTypeBinary: + return h, h, nil + case packet.SigTypeText: + return h, NewCanonicalTextHash(h), nil + } + + return nil, nil, errors.UnsupportedError("unsupported signature type: " + strconv.Itoa(int(sigType))) +} + +// checkReader wraps an io.Reader from a LiteralData packet. When it sees EOF +// it closes the ReadCloser from any SymmetricallyEncrypted packet to trigger +// MDC checks. +type checkReader struct { + md *MessageDetails +} + +func (cr checkReader) Read(buf []byte) (int, error) { + n, sensitiveParsingError := cr.md.LiteralData.Body.Read(buf) + if sensitiveParsingError == io.EOF { + mdcErr := cr.md.decrypted.Close() + if mdcErr != nil { + return n, mdcErr + } + return n, io.EOF + } + + if sensitiveParsingError != nil { + return n, errors.StructuralError("parsing error") + } + + return n, nil +} + +// signatureCheckReader wraps an io.Reader from a LiteralData packet and hashes +// the data as it is read. When it sees an EOF from the underlying io.Reader +// it parses and checks a trailing Signature packet and triggers any MDC checks. +type signatureCheckReader struct { + packets *packet.Reader + h, wrappedHash hash.Hash + md *MessageDetails + config *packet.Config +} + +func (scr *signatureCheckReader) Read(buf []byte) (int, error) { + n, sensitiveParsingError := scr.md.LiteralData.Body.Read(buf) + + // Hash only if required + if scr.md.SignedBy != nil { + scr.wrappedHash.Write(buf[:n]) + } + + if sensitiveParsingError == io.EOF { + var p packet.Packet + var readError error + var sig *packet.Signature + + p, readError = scr.packets.Next() + for readError == nil { + var ok bool + if sig, ok = p.(*packet.Signature); ok { + if sig.Version == 5 && (sig.SigType == 0x00 || sig.SigType == 0x01) { + sig.Metadata = scr.md.LiteralData + } + + // If signature KeyID matches + if scr.md.SignedBy != nil && *sig.IssuerKeyId == scr.md.SignedByKeyId { + scr.md.Signature = sig + scr.md.SignatureError = scr.md.SignedBy.PublicKey.VerifySignature(scr.h, scr.md.Signature) + if scr.md.SignatureError == nil && scr.md.Signature.SigExpired(scr.config.Now()) { + scr.md.SignatureError = errors.ErrSignatureExpired + } + } else { + scr.md.UnverifiedSignatures = append(scr.md.UnverifiedSignatures, sig) + } + } + + p, readError = scr.packets.Next() + } + + if scr.md.SignedBy != nil && scr.md.Signature == nil { + if scr.md.UnverifiedSignatures == nil { + scr.md.SignatureError = errors.StructuralError("LiteralData not followed by signature") + } else { + scr.md.SignatureError = errors.StructuralError("No matching signature found") + } + } + + // The SymmetricallyEncrypted packet, if any, might have an + // unsigned hash of its own. In order to check this we need to + // close that Reader. + if scr.md.decrypted != nil { + mdcErr := scr.md.decrypted.Close() + if mdcErr != nil { + return n, mdcErr + } + } + return n, io.EOF + } + + if sensitiveParsingError != nil { + return n, errors.StructuralError("parsing error") + } + + return n, nil +} + +// CheckDetachedSignature takes a signed file and a detached signature and +// returns the signer if the signature is valid. If the signer isn't known, +// ErrUnknownIssuer is returned. +func CheckDetachedSignature(keyring KeyRing, signed, signature io.Reader, config *packet.Config) (signer *Entity, err error) { + var expectedHashes []crypto.Hash + return CheckDetachedSignatureAndHash(keyring, signed, signature, expectedHashes, config) +} + +// CheckDetachedSignatureAndHash performs the same actions as +// CheckDetachedSignature and checks that the expected hash functions were used. +func CheckDetachedSignatureAndHash(keyring KeyRing, signed, signature io.Reader, expectedHashes []crypto.Hash, config *packet.Config) (signer *Entity, err error) { + var issuerKeyId uint64 + var hashFunc crypto.Hash + var sigType packet.SignatureType + var keys []Key + var p packet.Packet + + expectedHashesLen := len(expectedHashes) + packets := packet.NewReader(signature) + var sig *packet.Signature + for { + p, err = packets.Next() + if err == io.EOF { + return nil, errors.ErrUnknownIssuer + } + if err != nil { + return nil, err + } + + var ok bool + sig, ok = p.(*packet.Signature) + if !ok { + return nil, errors.StructuralError("non signature packet found") + } + if sig.IssuerKeyId == nil { + return nil, errors.StructuralError("signature doesn't have an issuer") + } + issuerKeyId = *sig.IssuerKeyId + hashFunc = sig.Hash + sigType = sig.SigType + + for i, expectedHash := range expectedHashes { + if hashFunc == expectedHash { + break + } + if i+1 == expectedHashesLen { + return nil, errors.StructuralError("hash algorithm mismatch with cleartext message headers") + } + } + + keys = keyring.KeysByIdUsage(issuerKeyId, packet.KeyFlagSign) + if len(keys) > 0 { + break + } + } + + if len(keys) == 0 { + panic("unreachable") + } + + h, wrappedHash, err := hashForSignature(hashFunc, sigType) + if err != nil { + return nil, err + } + + if _, err := io.Copy(wrappedHash, signed); err != nil && err != io.EOF { + return nil, err + } + + for _, key := range keys { + err = key.PublicKey.VerifySignature(h, sig) + if err == nil { + now := config.Now() + if sig.SigExpired(now) { + return key.Entity, errors.ErrSignatureExpired + } + if key.PublicKey.KeyExpired(key.SelfSignature, now) { + return key.Entity, errors.ErrKeyExpired + } + return key.Entity, nil + } + } + + return nil, err +} + +// CheckArmoredDetachedSignature performs the same actions as +// CheckDetachedSignature but expects the signature to be armored. +func CheckArmoredDetachedSignature(keyring KeyRing, signed, signature io.Reader, config *packet.Config) (signer *Entity, err error) { + body, err := readArmored(signature, SignatureType) + if err != nil { + return + } + + return CheckDetachedSignature(keyring, signed, body, config) +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/read_write_test_data.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/read_write_test_data.go new file mode 100644 index 0000000000000..8caed36e3e762 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/read_write_test_data.go @@ -0,0 +1,173 @@ +package openpgp + +const testKey1KeyId = 0xA34D7E18C20C31BB +const testKey3KeyId = 0x338934250CCC0360 +const testKeyP256KeyId = 0xd44a2c495918513e + +const signedInput = "Signed message\nline 2\nline 3\n" +const signedTextInput = "Signed message\r\nline 2\r\nline 3\r\n" + +const recipientUnspecifiedHex = "848c0300000000000000000103ff62d4d578d03cf40c3da998dfe216c074fa6ddec5e31c197c9666ba292830d91d18716a80f699f9d897389a90e6d62d0238f5f07a5248073c0f24920e4bc4a30c2d17ee4e0cae7c3d4aaa4e8dced50e3010a80ee692175fa0385f62ecca4b56ee6e9980aa3ec51b61b077096ac9e800edaf161268593eedb6cc7027ff5cb32745d250010d407a6221ae22ef18469b444f2822478c4d190b24d36371a95cb40087cdd42d9399c3d06a53c0673349bfb607927f20d1e122bde1e2bf3aa6cae6edf489629bcaa0689539ae3b718914d88ededc3b" + +const detachedSignatureHex = "889c04000102000605024d449cd1000a0910a34d7e18c20c31bb167603ff57718d09f28a519fdc7b5a68b6a3336da04df85e38c5cd5d5bd2092fa4629848a33d85b1729402a2aab39c3ac19f9d573f773cc62c264dc924c067a79dfd8a863ae06c7c8686120760749f5fd9b1e03a64d20a7df3446ddc8f0aeadeaeba7cbaee5c1e366d65b6a0c6cc749bcb912d2f15013f812795c2e29eb7f7b77f39ce77" + +const detachedSignatureTextHex = "889c04010102000605024d449d21000a0910a34d7e18c20c31bbc8c60400a24fbef7342603a41cb1165767bd18985d015fb72fe05db42db36cfb2f1d455967f1e491194fbf6cf88146222b23bf6ffbd50d17598d976a0417d3192ff9cc0034fd00f287b02e90418bbefe609484b09231e4e7a5f3562e199bf39909ab5276c4d37382fe088f6b5c3426fc1052865da8b3ab158672d58b6264b10823dc4b39" + +const detachedSignatureDSAHex = "884604001102000605024d6c4eac000a0910338934250ccc0360f18d00a087d743d6405ed7b87755476629600b8b694a39e900a0abff8126f46faf1547c1743c37b21b4ea15b8f83" + +const detachedSignatureP256Hex = "885e0400130a0006050256e5bb00000a0910d44a2c495918513edef001009841a4f792beb0befccb35c8838a6a87d9b936beaa86db6745ddc7b045eee0cf00fd1ac1f78306b17e965935dd3f8bae4587a76587e4af231efe19cc4011a8434817" + +// The plaintext is https://www.gutenberg.org/cache/epub/1080/pg1080.txt +const modestProposalSha512 = "lbbrB1+WP3T9AaC9OQqBdOcCjgeEQadlulXsNPgVx0tyqPzDHwUugZ2gE7V0ESKAw6kAVfgkcuvfgxAAGaeHtw==" + +const testKeys1And2Hex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd0011010001b41054657374204b6579203120285253412988b804130102002205024d3c5c10021b03060b090807030206150802090a0b0416020301021e01021780000a0910a34d7e18c20c31bbb5b304009cc45fe610b641a2c146331be94dade0a396e73ca725e1b25c21708d9cab46ecca5ccebc23055879df8f99eea39b377962a400f2ebdc36a7c99c333d74aeba346315137c3ff9d0a09b0273299090343048afb8107cf94cbd1400e3026f0ccac7ecebbc4d78588eb3e478fe2754d3ca664bcf3eac96ca4a6b0c8d7df5102f60f6b0020003b88d044d3c5c10010400b201df61d67487301f11879d514f4248ade90c8f68c7af1284c161098de4c28c2850f1ec7b8e30f959793e571542ffc6532189409cb51c3d30dad78c4ad5165eda18b20d9826d8707d0f742e2ab492103a85bbd9ddf4f5720f6de7064feb0d39ee002219765bb07bcfb8b877f47abe270ddeda4f676108cecb6b9bb2ad484a4f0011010001889f04180102000905024d3c5c10021b0c000a0910a34d7e18c20c31bb1a03040085c8d62e16d05dc4e9dad64953c8a2eed8b6c12f92b1575eeaa6dcf7be9473dd5b24b37b6dffbb4e7c99ed1bd3cb11634be19b3e6e207bed7505c7ca111ccf47cb323bf1f8851eb6360e8034cbff8dd149993c959de89f8f77f38e7e98b8e3076323aa719328e2b408db5ec0d03936efd57422ba04f925cdc7b4c1af7590e40ab0020003988d044d3c5c33010400b488c3e5f83f4d561f317817538d9d0397981e9aef1321ca68ebfae1cf8b7d388e19f4b5a24a82e2fbbf1c6c26557a6c5845307a03d815756f564ac7325b02bc83e87d5480a8fae848f07cb891f2d51ce7df83dcafdc12324517c86d472cc0ee10d47a68fd1d9ae49a6c19bbd36d82af597a0d88cc9c49de9df4e696fc1f0b5d0011010001b42754657374204b6579203220285253412c20656e637279707465642070726976617465206b65792988b804130102002205024d3c5c33021b03060b090807030206150802090a0b0416020301021e01021780000a0910d4984f961e35246b98940400908a73b6a6169f700434f076c6c79015a49bee37130eaf23aaa3cfa9ce60bfe4acaa7bc95f1146ada5867e0079babb38804891f4f0b8ebca57a86b249dee786161a755b7a342e68ccf3f78ed6440a93a6626beb9a37aa66afcd4f888790cb4bb46d94a4ae3eb3d7d3e6b00f6bfec940303e89ec5b32a1eaaacce66497d539328b0020003b88d044d3c5c33010400a4e913f9442abcc7f1804ccab27d2f787ffa592077ca935a8bb23165bd8d57576acac647cc596b2c3f814518cc8c82953c7a4478f32e0cf645630a5ba38d9618ef2bc3add69d459ae3dece5cab778938d988239f8c5ae437807075e06c828019959c644ff05ef6a5a1dab72227c98e3a040b0cf219026640698d7a13d8538a570011010001889f04180102000905024d3c5c33021b0c000a0910d4984f961e35246b26c703ff7ee29ef53bc1ae1ead533c408fa136db508434e233d6e62be621e031e5940bbd4c08142aed0f82217e7c3e1ec8de574bc06ccf3c36633be41ad78a9eacd209f861cae7b064100758545cc9dd83db71806dc1cfd5fb9ae5c7474bba0c19c44034ae61bae5eca379383339dece94ff56ff7aa44a582f3e5c38f45763af577c0934b0020003" + +const testKeys1And2PrivateHex = "9501d8044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd00110100010003ff4d91393b9a8e3430b14d6209df42f98dc927425b881f1209f319220841273a802a97c7bdb8b3a7740b3ab5866c4d1d308ad0d3a79bd1e883aacf1ac92dfe720285d10d08752a7efe3c609b1d00f17f2805b217be53999a7da7e493bfc3e9618fd17018991b8128aea70a05dbce30e4fbe626aa45775fa255dd9177aabf4df7cf0200c1ded12566e4bc2bb590455e5becfb2e2c9796482270a943343a7835de41080582c2be3caf5981aa838140e97afa40ad652a0b544f83eb1833b0957dce26e47b0200eacd6046741e9ce2ec5beb6fb5e6335457844fb09477f83b050a96be7da043e17f3a9523567ed40e7a521f818813a8b8a72209f1442844843ccc7eb9805442570200bdafe0438d97ac36e773c7162028d65844c4d463e2420aa2228c6e50dc2743c3d6c72d0d782a5173fe7be2169c8a9f4ef8a7cf3e37165e8c61b89c346cdc6c1799d2b41054657374204b6579203120285253412988b804130102002205024d3c5c10021b03060b090807030206150802090a0b0416020301021e01021780000a0910a34d7e18c20c31bbb5b304009cc45fe610b641a2c146331be94dade0a396e73ca725e1b25c21708d9cab46ecca5ccebc23055879df8f99eea39b377962a400f2ebdc36a7c99c333d74aeba346315137c3ff9d0a09b0273299090343048afb8107cf94cbd1400e3026f0ccac7ecebbc4d78588eb3e478fe2754d3ca664bcf3eac96ca4a6b0c8d7df5102f60f6b00200009d01d8044d3c5c10010400b201df61d67487301f11879d514f4248ade90c8f68c7af1284c161098de4c28c2850f1ec7b8e30f959793e571542ffc6532189409cb51c3d30dad78c4ad5165eda18b20d9826d8707d0f742e2ab492103a85bbd9ddf4f5720f6de7064feb0d39ee002219765bb07bcfb8b877f47abe270ddeda4f676108cecb6b9bb2ad484a4f00110100010003fd17a7490c22a79c59281fb7b20f5e6553ec0c1637ae382e8adaea295f50241037f8997cf42c1ce26417e015091451b15424b2c59eb8d4161b0975630408e394d3b00f88d4b4e18e2cc85e8251d4753a27c639c83f5ad4a571c4f19d7cd460b9b73c25ade730c99df09637bd173d8e3e981ac64432078263bb6dc30d3e974150dd0200d0ee05be3d4604d2146fb0457f31ba17c057560785aa804e8ca5530a7cd81d3440d0f4ba6851efcfd3954b7e68908fc0ba47f7ac37bf559c6c168b70d3a7c8cd0200da1c677c4bce06a068070f2b3733b0a714e88d62aa3f9a26c6f5216d48d5c2b5624144f3807c0df30be66b3268eeeca4df1fbded58faf49fc95dc3c35f134f8b01fd1396b6c0fc1b6c4f0eb8f5e44b8eace1e6073e20d0b8bc5385f86f1cf3f050f66af789f3ef1fc107b7f4421e19e0349c730c68f0a226981f4e889054fdb4dc149e8e889f04180102000905024d3c5c10021b0c000a0910a34d7e18c20c31bb1a03040085c8d62e16d05dc4e9dad64953c8a2eed8b6c12f92b1575eeaa6dcf7be9473dd5b24b37b6dffbb4e7c99ed1bd3cb11634be19b3e6e207bed7505c7ca111ccf47cb323bf1f8851eb6360e8034cbff8dd149993c959de89f8f77f38e7e98b8e3076323aa719328e2b408db5ec0d03936efd57422ba04f925cdc7b4c1af7590e40ab00200009501fe044d3c5c33010400b488c3e5f83f4d561f317817538d9d0397981e9aef1321ca68ebfae1cf8b7d388e19f4b5a24a82e2fbbf1c6c26557a6c5845307a03d815756f564ac7325b02bc83e87d5480a8fae848f07cb891f2d51ce7df83dcafdc12324517c86d472cc0ee10d47a68fd1d9ae49a6c19bbd36d82af597a0d88cc9c49de9df4e696fc1f0b5d0011010001fe030302e9030f3c783e14856063f16938530e148bc57a7aa3f3e4f90df9dceccdc779bc0835e1ad3d006e4a8d7b36d08b8e0de5a0d947254ecfbd22037e6572b426bcfdc517796b224b0036ff90bc574b5509bede85512f2eefb520fb4b02aa523ba739bff424a6fe81c5041f253f8d757e69a503d3563a104d0d49e9e890b9d0c26f96b55b743883b472caa7050c4acfd4a21f875bdf1258d88bd61224d303dc9df77f743137d51e6d5246b88c406780528fd9a3e15bab5452e5b93970d9dcc79f48b38651b9f15bfbcf6da452837e9cc70683d1bdca94507870f743e4ad902005812488dd342f836e72869afd00ce1850eea4cfa53ce10e3608e13d3c149394ee3cbd0e23d018fcbcb6e2ec5a1a22972d1d462ca05355d0d290dd2751e550d5efb38c6c89686344df64852bf4ff86638708f644e8ec6bd4af9b50d8541cb91891a431326ab2e332faa7ae86cfb6e0540aa63160c1e5cdd5a4add518b303fff0a20117c6bc77f7cfbaf36b04c865c6c2b42754657374204b6579203220285253412c20656e637279707465642070726976617465206b65792988b804130102002205024d3c5c33021b03060b090807030206150802090a0b0416020301021e01021780000a0910d4984f961e35246b98940400908a73b6a6169f700434f076c6c79015a49bee37130eaf23aaa3cfa9ce60bfe4acaa7bc95f1146ada5867e0079babb38804891f4f0b8ebca57a86b249dee786161a755b7a342e68ccf3f78ed6440a93a6626beb9a37aa66afcd4f888790cb4bb46d94a4ae3eb3d7d3e6b00f6bfec940303e89ec5b32a1eaaacce66497d539328b00200009d01fe044d3c5c33010400a4e913f9442abcc7f1804ccab27d2f787ffa592077ca935a8bb23165bd8d57576acac647cc596b2c3f814518cc8c82953c7a4478f32e0cf645630a5ba38d9618ef2bc3add69d459ae3dece5cab778938d988239f8c5ae437807075e06c828019959c644ff05ef6a5a1dab72227c98e3a040b0cf219026640698d7a13d8538a570011010001fe030302e9030f3c783e148560f936097339ae381d63116efcf802ff8b1c9360767db5219cc987375702a4123fd8657d3e22700f23f95020d1b261eda5257e9a72f9a918e8ef22dd5b3323ae03bbc1923dd224db988cadc16acc04b120a9f8b7e84da9716c53e0334d7b66586ddb9014df604b41be1e960dcfcbc96f4ed150a1a0dd070b9eb14276b9b6be413a769a75b519a53d3ecc0c220e85cd91ca354d57e7344517e64b43b6e29823cbd87eae26e2b2e78e6dedfbb76e3e9f77bcb844f9a8932eb3db2c3f9e44316e6f5d60e9e2a56e46b72abe6b06dc9a31cc63f10023d1f5e12d2a3ee93b675c96f504af0001220991c88db759e231b3320dcedf814dcf723fd9857e3d72d66a0f2af26950b915abdf56c1596f46a325bf17ad4810d3535fb02a259b247ac3dbd4cc3ecf9c51b6c07cebb009c1506fba0a89321ec8683e3fd009a6e551d50243e2d5092fefb3321083a4bad91320dc624bd6b5dddf93553e3d53924c05bfebec1fb4bd47e89a1a889f04180102000905024d3c5c33021b0c000a0910d4984f961e35246b26c703ff7ee29ef53bc1ae1ead533c408fa136db508434e233d6e62be621e031e5940bbd4c08142aed0f82217e7c3e1ec8de574bc06ccf3c36633be41ad78a9eacd209f861cae7b064100758545cc9dd83db71806dc1cfd5fb9ae5c7474bba0c19c44034ae61bae5eca379383339dece94ff56ff7aa44a582f3e5c38f45763af577c0934b0020000" + +const dsaElGamalTestKeysHex = "9501e1044dfcb16a110400aa3e5c1a1f43dd28c2ffae8abf5cfce555ee874134d8ba0a0f7b868ce2214beddc74e5e1e21ded354a95d18acdaf69e5e342371a71fbb9093162e0c5f3427de413a7f2c157d83f5cd2f9d791256dc4f6f0e13f13c3302af27f2384075ab3021dff7a050e14854bbde0a1094174855fc02f0bae8e00a340d94a1f22b32e48485700a0cec672ac21258fb95f61de2ce1af74b2c4fa3e6703ff698edc9be22c02ae4d916e4fa223f819d46582c0516235848a77b577ea49018dcd5e9e15cff9dbb4663a1ae6dd7580fa40946d40c05f72814b0f88481207e6c0832c3bded4853ebba0a7e3bd8e8c66df33d5a537cd4acf946d1080e7a3dcea679cb2b11a72a33a2b6a9dc85f466ad2ddf4c3db6283fa645343286971e3dd700703fc0c4e290d45767f370831a90187e74e9972aae5bff488eeff7d620af0362bfb95c1a6c3413ab5d15a2e4139e5d07a54d72583914661ed6a87cce810be28a0aa8879a2dd39e52fb6fe800f4f181ac7e328f740cde3d09a05cecf9483e4cca4253e60d4429ffd679d9996a520012aad119878c941e3cf151459873bdfc2a9563472fe0303027a728f9feb3b864260a1babe83925ce794710cfd642ee4ae0e5b9d74cee49e9c67b6cd0ea5dfbb582132195a121356a1513e1bca73e5b80c58c7ccb4164453412f456c47616d616c2054657374204b65792031886204131102002205024dfcb16a021b03060b090807030206150802090a0b0416020301021e01021780000a091033af447ccd759b09fadd00a0b8fd6f5a790bad7e9f2dbb7632046dc4493588db009c087c6a9ba9f7f49fab221587a74788c00db4889ab00200009d0157044dfcb16a1004008dec3f9291205255ccff8c532318133a6840739dd68b03ba942676f9038612071447bf07d00d559c5c0875724ea16a4c774f80d8338b55fca691a0522e530e604215b467bbc9ccfd483a1da99d7bc2648b4318fdbd27766fc8bfad3fddb37c62b8ae7ccfe9577e9b8d1e77c1d417ed2c2ef02d52f4da11600d85d3229607943700030503ff506c94c87c8cab778e963b76cf63770f0a79bf48fb49d3b4e52234620fc9f7657f9f8d56c96a2b7c7826ae6b57ebb2221a3fe154b03b6637cea7e6d98e3e45d87cf8dc432f723d3d71f89c5192ac8d7290684d2c25ce55846a80c9a7823f6acd9bb29fa6cd71f20bc90eccfca20451d0c976e460e672b000df49466408d527affe0303027a728f9feb3b864260abd761730327bca2aaa4ea0525c175e92bf240682a0e83b226f97ecb2e935b62c9a133858ce31b271fa8eb41f6a1b3cd72a63025ce1a75ee4180dcc284884904181102000905024dfcb16a021b0c000a091033af447ccd759b09dd0b009e3c3e7296092c81bee5a19929462caaf2fff3ae26009e218c437a2340e7ea628149af1ec98ec091a43992b00200009501e1044dfcb1be1104009f61faa61aa43df75d128cbe53de528c4aec49ce9360c992e70c77072ad5623de0a3a6212771b66b39a30dad6781799e92608316900518ec01184a85d872365b7d2ba4bacfb5882ea3c2473d3750dc6178cc1cf82147fb58caa28b28e9f12f6d1efcb0534abed644156c91cca4ab78834268495160b2400bc422beb37d237c2300a0cac94911b6d493bda1e1fbc6feeca7cb7421d34b03fe22cec6ccb39675bb7b94a335c2b7be888fd3906a1125f33301d8aa6ec6ee6878f46f73961c8d57a3e9544d8ef2a2cbfd4d52da665b1266928cfe4cb347a58c412815f3b2d2369dec04b41ac9a71cc9547426d5ab941cccf3b18575637ccfb42df1a802df3cfe0a999f9e7109331170e3a221991bf868543960f8c816c28097e503fe319db10fb98049f3a57d7c80c420da66d56f3644371631fad3f0ff4040a19a4fedc2d07727a1b27576f75a4d28c47d8246f27071e12d7a8de62aad216ddbae6aa02efd6b8a3e2818cda48526549791ab277e447b3a36c57cefe9b592f5eab73959743fcc8e83cbefec03a329b55018b53eec196765ae40ef9e20521a603c551efe0303020950d53a146bf9c66034d00c23130cce95576a2ff78016ca471276e8227fb30b1ffbd92e61804fb0c3eff9e30b1a826ee8f3e4730b4d86273ca977b4164453412f456c47616d616c2054657374204b65792032886204131102002205024dfcb1be021b03060b090807030206150802090a0b0416020301021e01021780000a0910a86bf526325b21b22bd9009e34511620415c974750a20df5cb56b182f3b48e6600a0a9466cb1a1305a84953445f77d461593f1d42bc1b00200009d0157044dfcb1be1004009565a951da1ee87119d600c077198f1c1bceb0f7aa54552489298e41ff788fa8f0d43a69871f0f6f77ebdfb14a4260cf9fbeb65d5844b4272a1904dd95136d06c3da745dc46327dd44a0f16f60135914368c8039a34033862261806bb2c5ce1152e2840254697872c85441ccb7321431d75a747a4bfb1d2c66362b51ce76311700030503fc0ea76601c196768070b7365a200e6ddb09307f262d5f39eec467b5f5784e22abdf1aa49226f59ab37cb49969d8f5230ea65caf56015abda62604544ed526c5c522bf92bed178a078789f6c807b6d34885688024a5bed9e9f8c58d11d4b82487b44c5f470c5606806a0443b79cadb45e0f897a561a53f724e5349b9267c75ca17fe0303020950d53a146bf9c660bc5f4ce8f072465e2d2466434320c1e712272fafc20e342fe7608101580fa1a1a367e60486a7cd1246b7ef5586cf5e10b32762b710a30144f12dd17dd4884904181102000905024dfcb1be021b0c000a0910a86bf526325b21b2904c00a0b2b66b4b39ccffda1d10f3ea8d58f827e30a8b8e009f4255b2d8112a184e40cde43a34e8655ca7809370b0020000" + +const signedMessageHex = "a3019bc0cbccc0c4b8d8b74ee2108fe16ec6d3ca490cbe362d3f8333d3f352531472538b8b13d353b97232f352158c20943157c71c16064626063656269052062e4e01987e9b6fccff4b7df3a34c534b23e679cbec3bc0f8f6e64dfb4b55fe3f8efa9ce110ddb5cd79faf1d753c51aecfa669f7e7aa043436596cccc3359cb7dd6bbe9ecaa69e5989d9e57209571edc0b2fa7f57b9b79a64ee6e99ce1371395fee92fec2796f7b15a77c386ff668ee27f6d38f0baa6c438b561657377bf6acff3c5947befd7bf4c196252f1d6e5c524d0300" + +const signedTextMessageHex = "a3019bc0cbccc8c4b8d8b74ee2108fe16ec6d36a250cbece0c178233d3f352531472538b8b13d35379b97232f352158ca0b4312f57c71c1646462606365626906a062e4e019811591798ff99bf8afee860b0d8a8c2a85c3387e3bcf0bb3b17987f2bbcfab2aa526d930cbfd3d98757184df3995c9f3e7790e36e3e9779f06089d4c64e9e47dd6202cb6e9bc73c5d11bb59fbaf89d22d8dc7cf199ddf17af96e77c5f65f9bbed56f427bd8db7af37f6c9984bf9385efaf5f184f986fb3e6adb0ecfe35bbf92d16a7aa2a344fb0bc52fb7624f0200" + +const signedEncryptedMessageHex = "c18c032a67d68660df41c70103ff5a84c9a72f80e74ef0384c2d6a9ebfe2b09e06a8f298394f6d2abf174e40934ab0ec01fb2d0ddf21211c6fe13eb238563663b017a6b44edca552eb4736c4b7dc6ed907dd9e12a21b51b64b46f902f76fb7aaf805c1db8070574d8d0431a23e324a750f77fb72340a17a42300ee4ca8207301e95a731da229a63ab9c6b44541fbd2c11d016d810b3b3b2b38f15b5b40f0a4910332829c2062f1f7cc61f5b03677d73c54cafa1004ced41f315d46444946faae571d6f426e6dbd45d9780eb466df042005298adabf7ce0ef766dfeb94cd449c7ed0046c880339599c4711af073ce649b1e237c40b50a5536283e03bdbb7afad78bd08707715c67fb43295f905b4c479178809d429a8e167a9a8c6dfd8ab20b4edebdc38d6dec879a3202e1b752690d9bb5b0c07c5a227c79cc200e713a99251a4219d62ad5556900cf69bd384b6c8e726c7be267471d0d23af956da165af4af757246c2ebcc302b39e8ef2fccb4971b234fcda22d759ddb20e27269ee7f7fe67898a9de721bfa02ab0becaa046d00ea16cb1afc4e2eab40d0ac17121c565686e5cbd0cbdfbd9d6db5c70278b9c9db5a83176d04f61fbfbc4471d721340ede2746e5c312ded4f26787985af92b64fae3f253dbdde97f6a5e1996fd4d865599e32ff76325d3e9abe93184c02988ee89a4504356a4ef3b9b7a57cbb9637ca90af34a7676b9ef559325c3cca4e29d69fec1887f5440bb101361d744ad292a8547f22b4f22b419a42aa836169b89190f46d9560824cb2ac6e8771de8223216a5e647e132ab9eebcba89569ab339cb1c3d70fe806b31f4f4c600b4103b8d7583ebff16e43dcda551e6530f975122eb8b29" + +const verifiedSignatureEncryptedMessageHex = "c2b304000108000605026048f6d600210910a34d7e18c20c31bb1621045fb74b1d03b1e3cb31bc2f8aa34d7e18c20c31bb9a3b0400a32ddac1af259c1b0abab0041327ea04970944401978fb647dd1cf9aba4f164e43f0d8a9389501886474bdd4a6e77f6aea945c07dfbf87743835b44cc2c39a1f9aeecfa83135abc92e18e50396f2e6a06c44e0188b0081effbfb4160d28f118d4ff73dd199a102e47cffd8c7ff2bacd83ae72b5820c021a486766dd587b5da61" + +const unverifiedSignatureEncryptedMessageHex = "c2b304000108000605026048f6d600210910a34d7e18c20c31bb1621045fb74b1d03b1e3cb31bc2f8aa34d7e18c20c31bb9a3b0400a32ddac1af259c1b0abab0041327ea04970944401978fb647dd1cf9aba4f164e43f0d8a9389501886474bdd4a6e77f6aea945c07dfbf87743835b44cc2c39a1f9aeecfa83135abc92e18e50396f2e6a06c44e0188b0081effbfb4160d28f118d4ff73dd199a102e47cffd8c7ff2bacd83ae72b5820c021a486766dd587b5da61" + +const signedEncryptedMessage2Hex = "85010e03cf6a7abcd43e36731003fb057f5495b79db367e277cdbe4ab90d924ddee0c0381494112ff8c1238fb0184af35d1731573b01bc4c55ecacd2aafbe2003d36310487d1ecc9ac994f3fada7f9f7f5c3a64248ab7782906c82c6ff1303b69a84d9a9529c31ecafbcdb9ba87e05439897d87e8a2a3dec55e14df19bba7f7bd316291c002ae2efd24f83f9e3441203fc081c0c23dc3092a454ca8a082b27f631abf73aca341686982e8fbda7e0e7d863941d68f3de4a755c2964407f4b5e0477b3196b8c93d551dd23c8beef7d0f03fbb1b6066f78907faf4bf1677d8fcec72651124080e0b7feae6b476e72ab207d38d90b958759fdedfc3c6c35717c9dbfc979b3cfbbff0a76d24a5e57056bb88acbd2a901ef64bc6e4db02adc05b6250ff378de81dca18c1910ab257dff1b9771b85bb9bbe0a69f5989e6d1710a35e6dfcceb7d8fb5ccea8db3932b3d9ff3fe0d327597c68b3622aec8e3716c83a6c93f497543b459b58ba504ed6bcaa747d37d2ca746fe49ae0a6ce4a8b694234e941b5159ff8bd34b9023da2814076163b86f40eed7c9472f81b551452d5ab87004a373c0172ec87ea6ce42ccfa7dbdad66b745496c4873d8019e8c28d6b3" + +const signatureEncryptedMessage2Hex = "c24604001102000605024dfd0166000a091033af447ccd759b09bae600a096ec5e63ecf0a403085e10f75cc3bab327663282009f51fad9df457ed8d2b70d8a73c76e0443eac0f377" + +const symmetricallyEncryptedCompressedHex = "c32e040903085a357c1a7b5614ed00cc0d1d92f428162058b3f558a0fb0980d221ebac6c97d5eda4e0fe32f6e706e94dd263012d6ca1ef8c4bbd324098225e603a10c85ebf09cbf7b5aeeb5ce46381a52edc51038b76a8454483be74e6dcd1e50d5689a8ae7eceaeefed98a0023d49b22eb1f65c2aa1ef1783bb5e1995713b0457102ec3c3075fe871267ffa4b686ad5d52000d857" + +const dsaTestKeyHex = "9901a2044d6c49de110400cb5ce438cf9250907ac2ba5bf6547931270b89f7c4b53d9d09f4d0213a5ef2ec1f26806d3d259960f872a4a102ef1581ea3f6d6882d15134f21ef6a84de933cc34c47cc9106efe3bd84c6aec12e78523661e29bc1a61f0aab17fa58a627fd5fd33f5149153fbe8cd70edf3d963bc287ef875270ff14b5bfdd1bca4483793923b00a0fe46d76cb6e4cbdc568435cd5480af3266d610d303fe33ae8273f30a96d4d34f42fa28ce1112d425b2e3bf7ea553d526e2db6b9255e9dc7419045ce817214d1a0056dbc8d5289956a4b1b69f20f1105124096e6a438f41f2e2495923b0f34b70642607d45559595c7fe94d7fa85fc41bf7d68c1fd509ebeaa5f315f6059a446b9369c277597e4f474a9591535354c7e7f4fd98a08aa60400b130c24ff20bdfbf683313f5daebf1c9b34b3bdadfc77f2ddd72ee1fb17e56c473664bc21d66467655dd74b9005e3a2bacce446f1920cd7017231ae447b67036c9b431b8179deacd5120262d894c26bc015bffe3d827ba7087ad9b700d2ca1f6d16cc1786581e5dd065f293c31209300f9b0afcc3f7c08dd26d0a22d87580b4db41054657374204b65792033202844534129886204131102002205024d6c49de021b03060b090807030206150802090a0b0416020301021e01021780000a0910338934250ccc03607e0400a0bdb9193e8a6b96fc2dfc108ae848914b504481f100a09c4dc148cb693293a67af24dd40d2b13a9e36794" + +const dsaTestKeyPrivateHex = "9501bb044d6c49de110400cb5ce438cf9250907ac2ba5bf6547931270b89f7c4b53d9d09f4d0213a5ef2ec1f26806d3d259960f872a4a102ef1581ea3f6d6882d15134f21ef6a84de933cc34c47cc9106efe3bd84c6aec12e78523661e29bc1a61f0aab17fa58a627fd5fd33f5149153fbe8cd70edf3d963bc287ef875270ff14b5bfdd1bca4483793923b00a0fe46d76cb6e4cbdc568435cd5480af3266d610d303fe33ae8273f30a96d4d34f42fa28ce1112d425b2e3bf7ea553d526e2db6b9255e9dc7419045ce817214d1a0056dbc8d5289956a4b1b69f20f1105124096e6a438f41f2e2495923b0f34b70642607d45559595c7fe94d7fa85fc41bf7d68c1fd509ebeaa5f315f6059a446b9369c277597e4f474a9591535354c7e7f4fd98a08aa60400b130c24ff20bdfbf683313f5daebf1c9b34b3bdadfc77f2ddd72ee1fb17e56c473664bc21d66467655dd74b9005e3a2bacce446f1920cd7017231ae447b67036c9b431b8179deacd5120262d894c26bc015bffe3d827ba7087ad9b700d2ca1f6d16cc1786581e5dd065f293c31209300f9b0afcc3f7c08dd26d0a22d87580b4d00009f592e0619d823953577d4503061706843317e4fee083db41054657374204b65792033202844534129886204131102002205024d6c49de021b03060b090807030206150802090a0b0416020301021e01021780000a0910338934250ccc03607e0400a0bdb9193e8a6b96fc2dfc108ae848914b504481f100a09c4dc148cb693293a67af24dd40d2b13a9e36794" + +const p256TestKeyHex = "98520456e5b83813082a8648ce3d030107020304a2072cd6d21321266c758cc5b83fab0510f751cb8d91897cddb7047d8d6f185546e2107111b0a95cb8ef063c33245502af7a65f004d5919d93ee74eb71a66253b424502d3235362054657374204b6579203c696e76616c6964406578616d706c652e636f6d3e8879041313080021050256e5b838021b03050b09080702061508090a0b020416020301021e01021780000a0910d44a2c495918513e54e50100dfa64f97d9b47766fc1943c6314ba3f2b2a103d71ad286dc5b1efb96a345b0c80100dbc8150b54241f559da6ef4baacea6d31902b4f4b1bdc09b34bf0502334b7754b8560456e5b83812082a8648ce3d030107020304bfe3cea9cee13486f8d518aa487fecab451f25467d2bf08e58f63e5fa525d5482133e6a79299c274b068ef0be448152ad65cf11cf764348588ca4f6a0bcf22b6030108078861041813080009050256e5b838021b0c000a0910d44a2c495918513e4a4800ff49d589fa64024ad30be363a032e3a0e0e6f5db56ba4c73db850518bf0121b8f20100fd78e065f4c70ea5be9df319ea67e493b936fc78da834a71828043d3154af56e" + +const p256TestKeyPrivateHex = "94a50456e5b83813082a8648ce3d030107020304a2072cd6d21321266c758cc5b83fab0510f751cb8d91897cddb7047d8d6f185546e2107111b0a95cb8ef063c33245502af7a65f004d5919d93ee74eb71a66253fe070302f0c2bfb0b6c30f87ee1599472b8636477eab23ced13b271886a4b50ed34c9d8436af5af5b8f88921f0efba6ef8c37c459bbb88bc1c6a13bbd25c4ce9b1e97679569ee77645d469bf4b43de637f5561b424502d3235362054657374204b6579203c696e76616c6964406578616d706c652e636f6d3e8879041313080021050256e5b838021b03050b09080702061508090a0b020416020301021e01021780000a0910d44a2c495918513e54e50100dfa64f97d9b47766fc1943c6314ba3f2b2a103d71ad286dc5b1efb96a345b0c80100dbc8150b54241f559da6ef4baacea6d31902b4f4b1bdc09b34bf0502334b77549ca90456e5b83812082a8648ce3d030107020304bfe3cea9cee13486f8d518aa487fecab451f25467d2bf08e58f63e5fa525d5482133e6a79299c274b068ef0be448152ad65cf11cf764348588ca4f6a0bcf22b603010807fe0703027510012471a603cfee2968dce19f732721ddf03e966fd133b4e3c7a685b788705cbc46fb026dc94724b830c9edbaecd2fb2c662f23169516cacd1fe423f0475c364ecc10abcabcfd4bbbda1a36a1bd8861041813080009050256e5b838021b0c000a0910d44a2c495918513e4a4800ff49d589fa64024ad30be363a032e3a0e0e6f5db56ba4c73db850518bf0121b8f20100fd78e065f4c70ea5be9df319ea67e493b936fc78da834a71828043d3154af56e" + +const armoredPrivateKeyBlock = `-----BEGIN PGP PRIVATE KEY BLOCK----- +Version: GnuPG v1.4.10 (GNU/Linux) + +lQHYBE2rFNoBBADFwqWQIW/DSqcB4yCQqnAFTJ27qS5AnB46ccAdw3u4Greeu3Bp +idpoHdjULy7zSKlwR1EA873dO/k/e11Ml3dlAFUinWeejWaK2ugFP6JjiieSsrKn +vWNicdCS4HTWn0X4sjl0ZiAygw6GNhqEQ3cpLeL0g8E9hnYzJKQ0LWJa0QARAQAB +AAP/TB81EIo2VYNmTq0pK1ZXwUpxCrvAAIG3hwKjEzHcbQznsjNvPUihZ+NZQ6+X +0HCfPAdPkGDCLCb6NavcSW+iNnLTrdDnSI6+3BbIONqWWdRDYJhqZCkqmG6zqSfL +IdkJgCw94taUg5BWP/AAeQrhzjChvpMQTVKQL5mnuZbUCeMCAN5qrYMP2S9iKdnk +VANIFj7656ARKt/nf4CBzxcpHTyB8+d2CtPDKCmlJP6vL8t58Jmih+kHJMvC0dzn +gr5f5+sCAOOe5gt9e0am7AvQWhdbHVfJU0TQJx+m2OiCJAqGTB1nvtBLHdJnfdC9 +TnXXQ6ZXibqLyBies/xeY2sCKL5qtTMCAKnX9+9d/5yQxRyrQUHt1NYhaXZnJbHx +q4ytu0eWz+5i68IYUSK69jJ1NWPM0T6SkqpB3KCAIv68VFm9PxqG1KmhSrQIVGVz +dCBLZXmIuAQTAQIAIgUCTasU2gIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AA +CgkQO9o98PRieSoLhgQAkLEZex02Qt7vGhZzMwuN0R22w3VwyYyjBx+fM3JFETy1 +ut4xcLJoJfIaF5ZS38UplgakHG0FQ+b49i8dMij0aZmDqGxrew1m4kBfjXw9B/v+ +eIqpODryb6cOSwyQFH0lQkXC040pjq9YqDsO5w0WYNXYKDnzRV0p4H1pweo2VDid +AdgETasU2gEEAN46UPeWRqKHvA99arOxee38fBt2CI08iiWyI8T3J6ivtFGixSqV +bRcPxYO/qLpVe5l84Nb3X71GfVXlc9hyv7CD6tcowL59hg1E/DC5ydI8K8iEpUmK +/UnHdIY5h8/kqgGxkY/T/hgp5fRQgW1ZoZxLajVlMRZ8W4tFtT0DeA+JABEBAAEA +A/0bE1jaaZKj6ndqcw86jd+QtD1SF+Cf21CWRNeLKnUds4FRRvclzTyUMuWPkUeX +TaNNsUOFqBsf6QQ2oHUBBK4VCHffHCW4ZEX2cd6umz7mpHW6XzN4DECEzOVksXtc +lUC1j4UB91DC/RNQqwX1IV2QLSwssVotPMPqhOi0ZLNY7wIA3n7DWKInxYZZ4K+6 +rQ+POsz6brEoRHwr8x6XlHenq1Oki855pSa1yXIARoTrSJkBtn5oI+f8AzrnN0BN +oyeQAwIA/7E++3HDi5aweWrViiul9cd3rcsS0dEnksPhvS0ozCJiHsq/6GFmy7J8 +QSHZPteedBnZyNp5jR+H7cIfVN3KgwH/Skq4PsuPhDq5TKK6i8Pc1WW8MA6DXTdU +nLkX7RGmMwjC0DBf7KWAlPjFaONAX3a8ndnz//fy1q7u2l9AZwrj1qa1iJ8EGAEC +AAkFAk2rFNoCGwwACgkQO9o98PRieSo2/QP/WTzr4ioINVsvN1akKuekmEMI3LAp +BfHwatufxxP1U+3Si/6YIk7kuPB9Hs+pRqCXzbvPRrI8NHZBmc8qIGthishdCYad +AHcVnXjtxrULkQFGbGvhKURLvS9WnzD/m1K2zzwxzkPTzT9/Yf06O6Mal5AdugPL +VrM0m72/jnpKo04= +=zNCn +-----END PGP PRIVATE KEY BLOCK-----` + +const e2ePublicKey = `-----BEGIN PGP PUBLIC KEY BLOCK----- +Charset: UTF-8 + +xv8AAABSBAAAAAATCCqGSM49AwEHAgME1LRoXSpOxtHXDUdmuvzchyg6005qIBJ4 +sfaSxX7QgH9RV2ONUhC+WiayCNADq+UMzuR/vunSr4aQffXvuGnR383/AAAAFDxk +Z2lsQHlhaG9vLWluYy5jb20+wv8AAACGBBATCAA4/wAAAAWCVGvAG/8AAAACiwn/ +AAAACZC2VkQCOjdvYf8AAAAFlQgJCgv/AAAAA5YBAv8AAAACngEAAE1BAP0X8veD +24IjmI5/C6ZAfVNXxgZZFhTAACFX75jUA3oD6AEAzoSwKf1aqH6oq62qhCN/pekX ++WAsVMBhNwzLpqtCRjLO/wAAAFYEAAAAABIIKoZIzj0DAQcCAwT50ain7vXiIRv8 +B1DO3x3cE/aattZ5sHNixJzRCXi2vQIA5QmOxZ6b5jjUekNbdHG3SZi1a2Ak5mfX +fRxC/5VGAwEIB8L/AAAAZQQYEwgAGP8AAAAFglRrwBz/AAAACZC2VkQCOjdvYQAA +FJAA9isX3xtGyMLYwp2F3nXm7QEdY5bq5VUcD/RJlj792VwA/1wH0pCzVLl4Q9F9 +ex7En5r7rHR5xwX82Msc+Rq9dSyO +=7MrZ +-----END PGP PUBLIC KEY BLOCK-----` + +const dsaKeyWithSHA512 = `9901a2044f04b07f110400db244efecc7316553ee08d179972aab87bb1214de7692593fcf5b6feb1c80fba268722dd464748539b85b81d574cd2d7ad0ca2444de4d849b8756bad7768c486c83a824f9bba4af773d11742bdfb4ac3b89ef8cc9452d4aad31a37e4b630d33927bff68e879284a1672659b8b298222fc68f370f3e24dccacc4a862442b9438b00a0ea444a24088dc23e26df7daf8f43cba3bffc4fe703fe3d6cd7fdca199d54ed8ae501c30e3ec7871ea9cdd4cf63cfe6fc82281d70a5b8bb493f922cd99fba5f088935596af087c8d818d5ec4d0b9afa7f070b3d7c1dd32a84fca08d8280b4890c8da1dde334de8e3cad8450eed2a4a4fcc2db7b8e5528b869a74a7f0189e11ef097ef1253582348de072bb07a9fa8ab838e993cef0ee203ff49298723e2d1f549b00559f886cd417a41692ce58d0ac1307dc71d85a8af21b0cf6eaa14baf2922d3a70389bedf17cc514ba0febbd107675a372fe84b90162a9e88b14d4b1c6be855b96b33fb198c46f058568817780435b6936167ebb3724b680f32bf27382ada2e37a879b3d9de2abe0c3f399350afd1ad438883f4791e2e3b4184453412068617368207472756e636174696f6e207465737488620413110a002205024f04b07f021b03060b090807030206150802090a0b0416020301021e01021780000a0910ef20e0cefca131581318009e2bf3bf047a44d75a9bacd00161ee04d435522397009a03a60d51bd8a568c6c021c8d7cf1be8d990d6417b0020003` + +const unknownHashFunctionHex = `8a00000040040001990006050253863c24000a09103b4fe6acc0b21f32ffff01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101` + +const rsaSignatureBadMPIlength = `8a00000040040001030006050253863c24000a09103b4fe6acc0b21f32ffff0101010101010101010101010101010101010101010101010101010101010101010101010101` + +const missingHashFunctionHex = `8a00000040040001030006050253863c24000a09103b4fe6acc0b21f32ffff0101010101010101010101010101010101010101010101010101010101010101010101` + +const campbellQuine = `a0b001000300fcffa0b001000d00f2ff000300fcffa0b001000d00f2ff8270a01c00000500faff8270a01c00000500faff000500faff001400ebff8270a01c00000500faff000500faff001400ebff428821c400001400ebff428821c400001400ebff428821c400001400ebff428821c400001400ebff428821c400000000ffff000000ffff000b00f4ff428821c400000000ffff000000ffff000b00f4ff0233214c40000100feff000233214c40000100feff0000` + +const keyV4forVerifyingSignedMessageV3 = `-----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: GPGTools - https://gpgtools.org + +mI0EVfxoFQEEAMBIqmbDfYygcvP6Phr1wr1XI41IF7Qixqybs/foBF8qqblD9gIY +BKpXjnBOtbkcVOJ0nljd3/sQIfH4E0vQwK5/4YRQSI59eKOqd6Fx+fWQOLG+uu6z +tewpeCj9LLHvibx/Sc7VWRnrznia6ftrXxJ/wHMezSab3tnGC0YPVdGNABEBAAG0 +JEdvY3J5cHRvIFRlc3QgS2V5IDx0aGVtYXhAZ21haWwuY29tPoi5BBMBCgAjBQJV +/GgVAhsDBwsJCAcDAgEGFQgCCQoLBBYCAwECHgECF4AACgkQeXnQmhdGW9PFVAP+ +K7TU0qX5ArvIONIxh/WAweyOk884c5cE8f+3NOPOOCRGyVy0FId5A7MmD5GOQh4H +JseOZVEVCqlmngEvtHZb3U1VYtVGE5WZ+6rQhGsMcWP5qaT4soYwMBlSYxgYwQcx +YhN9qOr292f9j2Y//TTIJmZT4Oa+lMxhWdqTfX+qMgG4jQRV/GgVAQQArhFSiij1 +b+hT3dnapbEU+23Z1yTu1DfF6zsxQ4XQWEV3eR8v+8mEDDNcz8oyyF56k6UQ3rXi +UMTIwRDg4V6SbZmaFbZYCOwp/EmXJ3rfhm7z7yzXj2OFN22luuqbyVhuL7LRdB0M +pxgmjXb4tTvfgKd26x34S+QqUJ7W6uprY4sAEQEAAYifBBgBCgAJBQJV/GgVAhsM +AAoJEHl50JoXRlvT7y8D/02ckx4OMkKBZo7viyrBw0MLG92i+DC2bs35PooHR6zz +786mitjOp5z2QWNLBvxC70S0qVfCIz8jKupO1J6rq6Z8CcbLF3qjm6h1omUBf8Nd +EfXKD2/2HV6zMKVknnKzIEzauh+eCKS2CeJUSSSryap/QLVAjRnckaES/OsEWhNB +=RZia +-----END PGP PUBLIC KEY BLOCK----- +` + +const signedMessageV3 = `-----BEGIN PGP MESSAGE----- +Comment: GPGTools - https://gpgtools.org + +owGbwMvMwMVYWXlhlrhb9GXG03JJDKF/MtxDMjKLFYAoUaEktbhEITe1uDgxPVWP +q5NhKjMrWAVcC9evD8z/bF/uWNjqtk/X3y5/38XGRQHm/57rrDRYuGnTw597Xqka +uM3137/hH3Os+Jf2dc0fXOITKwJvXJvecPVs0ta+Vg7ZO1MLn8w58Xx+6L58mbka +DGHyU9yTueZE8D+QF/Tz28Y78dqtF56R1VPn9Xw4uJqrWYdd7b3vIZ1V6R4Nh05d +iT57d/OhWwA= +=hG7R +-----END PGP MESSAGE----- +` + +// https://mailarchive.ietf.org/arch/msg/openpgp/9SheW_LENE0Kxf7haNllovPyAdY/ +const v5PrivKey = `-----BEGIN PGP PRIVATE KEY BLOCK----- + +lGEFXJH05BYAAAAtCSsGAQQB2kcPAQEHQFhZlVcVVtwf+21xNQPX+ecMJJBL0MPd +fj75iux+my8QAAAAAAAiAQCHZ1SnSUmWqxEsoI6facIVZQu6mph3cBFzzTvcm5lA +Ng5ctBhlbW1hLmdvbGRtYW5AZXhhbXBsZS5uZXSIlgUTFggASCIhBRk0e8mHJGQC +X5nfPsLgAA7ZiEiS4fez6kyUAJFZVptUBQJckfTkAhsDBQsJCAcCAyICAQYVCgkI +CwIEFgIDAQIeBwIXgAAA9cAA/jiR3yMsZMeEQ40u6uzEoXa6UXeV/S3wwJAXRJy9 +M8s0AP9vuL/7AyTfFXwwzSjDnYmzS0qAhbLDQ643N+MXGBJ2BZxmBVyR9OQSAAAA +MgorBgEEAZdVAQUBAQdA+nysrzml2UCweAqtpDuncSPlvrcBWKU0yfU0YvYWWAoD +AQgHAAAAAAAiAP9OdAPppjU1WwpqjIItkxr+VPQRT8Zm/Riw7U3F6v3OiBFHiHoF +GBYIACwiIQUZNHvJhyRkAl+Z3z7C4AAO2YhIkuH3s+pMlACRWVabVAUCXJH05AIb +DAAAOSQBAP4BOOIR/sGLNMOfeb5fPs/02QMieoiSjIBnijhob2U5AQC+RtOHCHx7 +TcIYl5/Uyoi+FOvPLcNw4hOv2nwUzSSVAw== +=IiS2 +-----END PGP PRIVATE KEY BLOCK-----` + +// Generated with the above private key +const v5PrivKeyMsg = `-----BEGIN PGP MESSAGE----- +Version: OpenPGP.js v4.10.7 +Comment: https://openpgpjs.org + +xA0DAQoWGTR7yYckZAIByxF1B21zZy50eHRfbIGSdGVzdMJ3BQEWCgAGBQJf +bIGSACMiIQUZNHvJhyRkAl+Z3z7C4AAO2YhIkuH3s+pMlACRWVabVDQvAP9G +y29VPonFXqi2zKkpZrvyvZxg+n5e8Nt9wNbuxeCd3QD/TtO2s+JvjrE4Siwv +UQdl5MlBka1QSNbMq2Bz7XwNPg4= +=6lbM +-----END PGP MESSAGE-----` diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k.go new file mode 100644 index 0000000000000..14f58548bd220 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k.go @@ -0,0 +1,367 @@ +// Copyright 2011 The 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 s2k implements the various OpenPGP string-to-key transforms as +// specified in RFC 4800 section 3.7.1. +package s2k // import "github.com/ProtonMail/go-crypto/openpgp/s2k" + +import ( + "crypto" + "hash" + "io" + "strconv" + + "github.com/ProtonMail/go-crypto/openpgp/errors" + "github.com/ProtonMail/go-crypto/openpgp/internal/algorithm" +) + +// Config collects configuration parameters for s2k key-stretching +// transformations. A nil *Config is valid and results in all default +// values. Currently, Config is used only by the Serialize function in +// this package. +type Config struct { + // S2KMode is the mode of s2k function. + // It can be 0 (simple), 1(salted), 3(iterated) + // 2(reserved) 100-110(private/experimental). + S2KMode uint8 + // Hash is the default hash function to be used. If + // nil, SHA256 is used. + Hash crypto.Hash + // S2KCount is only used for symmetric encryption. It + // determines the strength of the passphrase stretching when + // the said passphrase is hashed to produce a key. S2KCount + // should be between 65536 and 65011712, inclusive. If Config + // is nil or S2KCount is 0, the value 16777216 used. Not all + // values in the above range can be represented. S2KCount will + // be rounded up to the next representable value if it cannot + // be encoded exactly. See RFC 4880 Section 3.7.1.3. + S2KCount int +} + +// Params contains all the parameters of the s2k packet +type Params struct { + // mode is the mode of s2k function. + // It can be 0 (simple), 1(salted), 3(iterated) + // 2(reserved) 100-110(private/experimental). + mode uint8 + // hashId is the ID of the hash function used in any of the modes + hashId byte + // salt is a byte array to use as a salt in hashing process + salt []byte + // countByte is used to determine how many rounds of hashing are to + // be performed in s2k mode 3. See RFC 4880 Section 3.7.1.3. + countByte byte +} + +func (c *Config) hash() crypto.Hash { + if c == nil || uint(c.Hash) == 0 { + return crypto.SHA256 + } + + return c.Hash +} + +// EncodedCount get encoded count +func (c *Config) EncodedCount() uint8 { + if c == nil || c.S2KCount == 0 { + return 224 // The common case. Corresponding to 16777216 + } + + i := c.S2KCount + + switch { + case i < 65536: + i = 65536 + case i > 65011712: + i = 65011712 + } + + return encodeCount(i) +} + +// encodeCount converts an iterative "count" in the range 1024 to +// 65011712, inclusive, to an encoded count. The return value is the +// octet that is actually stored in the GPG file. encodeCount panics +// if i is not in the above range (encodedCount above takes care to +// pass i in the correct range). See RFC 4880 Section 3.7.7.1. +func encodeCount(i int) uint8 { + if i < 65536 || i > 65011712 { + panic("count arg i outside the required range") + } + + for encoded := 96; encoded < 256; encoded++ { + count := decodeCount(uint8(encoded)) + if count >= i { + return uint8(encoded) + } + } + + return 255 +} + +// decodeCount returns the s2k mode 3 iterative "count" corresponding to +// the encoded octet c. +func decodeCount(c uint8) int { + return (16 + int(c&15)) << (uint32(c>>4) + 6) +} + +// Simple writes to out the result of computing the Simple S2K function (RFC +// 4880, section 3.7.1.1) using the given hash and input passphrase. +func Simple(out []byte, h hash.Hash, in []byte) { + Salted(out, h, in, nil) +} + +var zero [1]byte + +// Salted writes to out the result of computing the Salted S2K function (RFC +// 4880, section 3.7.1.2) using the given hash, input passphrase and salt. +func Salted(out []byte, h hash.Hash, in []byte, salt []byte) { + done := 0 + var digest []byte + + for i := 0; done < len(out); i++ { + h.Reset() + for j := 0; j < i; j++ { + h.Write(zero[:]) + } + h.Write(salt) + h.Write(in) + digest = h.Sum(digest[:0]) + n := copy(out[done:], digest) + done += n + } +} + +// Iterated writes to out the result of computing the Iterated and Salted S2K +// function (RFC 4880, section 3.7.1.3) using the given hash, input passphrase, +// salt and iteration count. +func Iterated(out []byte, h hash.Hash, in []byte, salt []byte, count int) { + combined := make([]byte, len(in)+len(salt)) + copy(combined, salt) + copy(combined[len(salt):], in) + + if count < len(combined) { + count = len(combined) + } + + done := 0 + var digest []byte + for i := 0; done < len(out); i++ { + h.Reset() + for j := 0; j < i; j++ { + h.Write(zero[:]) + } + written := 0 + for written < count { + if written+len(combined) > count { + todo := count - written + h.Write(combined[:todo]) + written = count + } else { + h.Write(combined) + written += len(combined) + } + } + digest = h.Sum(digest[:0]) + n := copy(out[done:], digest) + done += n + } +} + +// Generate generates valid parameters from given configuration. +// It will enforce salted + hashed s2k method +func Generate(rand io.Reader, c *Config) (*Params, error) { + hashId, ok := HashToHashId(c.Hash) + if !ok { + return nil, errors.UnsupportedError("no such hash") + } + + params := &Params{ + mode: 3, // Enforce iterared + salted method + hashId: hashId, + salt: make([]byte, 8), + countByte: c.EncodedCount(), + } + + if _, err := io.ReadFull(rand, params.salt); err != nil { + return nil, err + } + + return params, nil +} + +// Parse reads a binary specification for a string-to-key transformation from r +// and returns a function which performs that transform. If the S2K is a special +// GNU extension that indicates that the private key is missing, then the error +// returned is errors.ErrDummyPrivateKey. +func Parse(r io.Reader) (f func(out, in []byte), err error) { + params, err := ParseIntoParams(r) + if err != nil { + return nil, err + } + + return params.Function() +} + +// ParseIntoParams reads a binary specification for a string-to-key +// transformation from r and returns a struct describing the s2k parameters. +func ParseIntoParams(r io.Reader) (params *Params, err error) { + var buf [9]byte + + _, err = io.ReadFull(r, buf[:2]) + if err != nil { + return + } + + params = &Params{ + mode: buf[0], + hashId: buf[1], + } + + switch params.mode { + case 0: + return params, nil + case 1: + _, err = io.ReadFull(r, buf[:8]) + if err != nil { + return nil, err + } + + params.salt = buf[:8] + return params, nil + case 3: + _, err = io.ReadFull(r, buf[:9]) + if err != nil { + return nil, err + } + + params.salt = buf[:8] + params.countByte = buf[8] + return params, nil + case 101: + // This is a GNU extension. See + // https://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=doc/DETAILS;h=fe55ae16ab4e26d8356dc574c9e8bc935e71aef1;hb=23191d7851eae2217ecdac6484349849a24fd94a#l1109 + if _, err = io.ReadFull(r, buf[:4]); err != nil { + return nil, err + } + if buf[0] == 'G' && buf[1] == 'N' && buf[2] == 'U' && buf[3] == 1 { + return params, nil + } + return nil, errors.UnsupportedError("GNU S2K extension") + } + + return nil, errors.UnsupportedError("S2K function") +} + +func (params *Params) Dummy() bool { + return params != nil && params.mode == 101 +} + +func (params *Params) Function() (f func(out, in []byte), err error) { + if params.Dummy() { + return nil, errors.ErrDummyPrivateKey("dummy key found") + } + hashObj, ok := HashIdToHash(params.hashId) + if !ok { + return nil, errors.UnsupportedError("hash for S2K function: " + strconv.Itoa(int(params.hashId))) + } + if !hashObj.Available() { + return nil, errors.UnsupportedError("hash not available: " + strconv.Itoa(int(hashObj))) + } + + switch params.mode { + case 0: + f := func(out, in []byte) { + Simple(out, hashObj.New(), in) + } + + return f, nil + case 1: + f := func(out, in []byte) { + Salted(out, hashObj.New(), in, params.salt) + } + + return f, nil + case 3: + f := func(out, in []byte) { + Iterated(out, hashObj.New(), in, params.salt, decodeCount(params.countByte)) + } + + return f, nil + } + + return nil, errors.UnsupportedError("S2K function") +} + +func (params *Params) Serialize(w io.Writer) (err error) { + if _, err = w.Write([]byte{params.mode}); err != nil { + return + } + if _, err = w.Write([]byte{params.hashId}); err != nil { + return + } + if params.Dummy() { + _, err = w.Write(append([]byte("GNU"), 1)) + return + } + if params.mode > 0 { + if _, err = w.Write(params.salt); err != nil { + return + } + if params.mode == 3 { + _, err = w.Write([]byte{params.countByte}) + } + } + return +} + +// Serialize salts and stretches the given passphrase and writes the +// resulting key into key. It also serializes an S2K descriptor to +// w. The key stretching can be configured with c, which may be +// nil. In that case, sensible defaults will be used. +func Serialize(w io.Writer, key []byte, rand io.Reader, passphrase []byte, c *Config) error { + params, err := Generate(rand, c) + if err != nil { + return err + } + err = params.Serialize(w) + if err != nil { + return err + } + + f, err := params.Function() + if err != nil { + return err + } + f(key, passphrase) + return nil +} + +// HashIdToHash returns a crypto.Hash which corresponds to the given OpenPGP +// hash id. +func HashIdToHash(id byte) (h crypto.Hash, ok bool) { + if hash, ok := algorithm.HashById[id]; ok { + return hash.HashFunc(), true + } + return 0, false +} + +// HashIdToString returns the name of the hash function corresponding to the +// given OpenPGP hash id. +func HashIdToString(id byte) (name string, ok bool) { + if hash, ok := algorithm.HashById[id]; ok { + return hash.String(), true + } + return "", false +} + +// HashIdToHash returns an OpenPGP hash id which corresponds the given Hash. +func HashToHashId(h crypto.Hash) (id byte, ok bool) { + for id, hash := range algorithm.HashById { + if hash.HashFunc() == h { + return id, true + } + } + return 0, false +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/write.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/write.go new file mode 100644 index 0000000000000..f0d7fe10de5cb --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/write.go @@ -0,0 +1,568 @@ +// Copyright 2011 The 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 openpgp + +import ( + "crypto" + "hash" + "io" + "strconv" + "time" + + "github.com/ProtonMail/go-crypto/openpgp/armor" + "github.com/ProtonMail/go-crypto/openpgp/errors" + "github.com/ProtonMail/go-crypto/openpgp/packet" + "github.com/ProtonMail/go-crypto/openpgp/s2k" +) + +// DetachSign signs message with the private key from signer (which must +// already have been decrypted) and writes the signature to w. +// If config is nil, sensible defaults will be used. +func DetachSign(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error { + return detachSign(w, signer, message, packet.SigTypeBinary, config) +} + +// ArmoredDetachSign signs message with the private key from signer (which +// must already have been decrypted) and writes an armored signature to w. +// If config is nil, sensible defaults will be used. +func ArmoredDetachSign(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) (err error) { + return armoredDetachSign(w, signer, message, packet.SigTypeBinary, config) +} + +// DetachSignText signs message (after canonicalising the line endings) with +// the private key from signer (which must already have been decrypted) and +// writes the signature to w. +// If config is nil, sensible defaults will be used. +func DetachSignText(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error { + return detachSign(w, signer, message, packet.SigTypeText, config) +} + +// ArmoredDetachSignText signs message (after canonicalising the line endings) +// with the private key from signer (which must already have been decrypted) +// and writes an armored signature to w. +// If config is nil, sensible defaults will be used. +func ArmoredDetachSignText(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error { + return armoredDetachSign(w, signer, message, packet.SigTypeText, config) +} + +func armoredDetachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) { + out, err := armor.Encode(w, SignatureType, nil) + if err != nil { + return + } + err = detachSign(out, signer, message, sigType, config) + if err != nil { + return + } + return out.Close() +} + +func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) { + signingKey, ok := signer.SigningKeyById(config.Now(), config.SigningKey()) + if !ok { + return errors.InvalidArgumentError("no valid signing keys") + } + if signingKey.PrivateKey == nil { + return errors.InvalidArgumentError("signing key doesn't have a private key") + } + if signingKey.PrivateKey.Encrypted { + return errors.InvalidArgumentError("signing key is encrypted") + } + + sig := new(packet.Signature) + sig.SigType = sigType + sig.PubKeyAlgo = signingKey.PrivateKey.PubKeyAlgo + sig.Hash = config.Hash() + sig.CreationTime = config.Now() + sigLifetimeSecs := config.SigLifetime() + sig.SigLifetimeSecs = &sigLifetimeSecs + sig.IssuerKeyId = &signingKey.PrivateKey.KeyId + + h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType) + if err != nil { + return + } + if _, err = io.Copy(wrappedHash, message); err != nil { + return err + } + + err = sig.Sign(h, signingKey.PrivateKey, config) + if err != nil { + return + } + + return sig.Serialize(w) +} + +// FileHints contains metadata about encrypted files. This metadata is, itself, +// encrypted. +type FileHints struct { + // IsBinary can be set to hint that the contents are binary data. + IsBinary bool + // FileName hints at the name of the file that should be written. It's + // truncated to 255 bytes if longer. It may be empty to suggest that the + // file should not be written to disk. It may be equal to "_CONSOLE" to + // suggest the data should not be written to disk. + FileName string + // ModTime contains the modification time of the file, or the zero time if not applicable. + ModTime time.Time +} + +// SymmetricallyEncrypt acts like gpg -c: it encrypts a file with a passphrase. +// The resulting WriteCloser must be closed after the contents of the file have +// been written. +// If config is nil, sensible defaults will be used. +func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) { + if hints == nil { + hints = &FileHints{} + } + + key, err := packet.SerializeSymmetricKeyEncrypted(ciphertext, passphrase, config) + if err != nil { + return + } + + var w io.WriteCloser + if config.AEAD() != nil { + w, err = packet.SerializeAEADEncrypted(ciphertext, key, config.Cipher(), config.AEAD().Mode(), config) + if err != nil { + return + } + } else { + w, err = packet.SerializeSymmetricallyEncrypted(ciphertext, config.Cipher(), key, config) + if err != nil { + return + } + } + + literalData := w + if algo := config.Compression(); algo != packet.CompressionNone { + var compConfig *packet.CompressionConfig + if config != nil { + compConfig = config.CompressionConfig + } + literalData, err = packet.SerializeCompressed(w, algo, compConfig) + if err != nil { + return + } + } + + var epochSeconds uint32 + if !hints.ModTime.IsZero() { + epochSeconds = uint32(hints.ModTime.Unix()) + } + return packet.SerializeLiteral(literalData, hints.IsBinary, hints.FileName, epochSeconds) +} + +// intersectPreferences mutates and returns a prefix of a that contains only +// the values in the intersection of a and b. The order of a is preserved. +func intersectPreferences(a []uint8, b []uint8) (intersection []uint8) { + var j int + for _, v := range a { + for _, v2 := range b { + if v == v2 { + a[j] = v + j++ + break + } + } + } + + return a[:j] +} + +func hashToHashId(h crypto.Hash) uint8 { + v, ok := s2k.HashToHashId(h) + if !ok { + panic("tried to convert unknown hash") + } + return v +} + +// EncryptText encrypts a message to a number of recipients and, optionally, +// signs it. Optional information is contained in 'hints', also encrypted, that +// aids the recipients in processing the message. The resulting WriteCloser +// must be closed after the contents of the file have been written. If config +// is nil, sensible defaults will be used. The signing is done in text mode. +func EncryptText(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) { + return encrypt(ciphertext, ciphertext, to, signed, hints, packet.SigTypeText, config) +} + +// Encrypt encrypts a message to a number of recipients and, optionally, signs +// it. hints contains optional information, that is also encrypted, that aids +// the recipients in processing the message. The resulting WriteCloser must +// be closed after the contents of the file have been written. +// If config is nil, sensible defaults will be used. +func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) { + return encrypt(ciphertext, ciphertext, to, signed, hints, packet.SigTypeBinary, config) +} + +// EncryptSplit encrypts a message to a number of recipients and, optionally, signs +// it. hints contains optional information, that is also encrypted, that aids +// the recipients in processing the message. The resulting WriteCloser must +// be closed after the contents of the file have been written. +// If config is nil, sensible defaults will be used. +func EncryptSplit(keyWriter io.Writer, dataWriter io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) { + return encrypt(keyWriter, dataWriter, to, signed, hints, packet.SigTypeBinary, config) +} + +// EncryptTextSplit encrypts a message to a number of recipients and, optionally, signs +// it. hints contains optional information, that is also encrypted, that aids +// the recipients in processing the message. The resulting WriteCloser must +// be closed after the contents of the file have been written. +// If config is nil, sensible defaults will be used. +func EncryptTextSplit(keyWriter io.Writer, dataWriter io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) { + return encrypt(keyWriter, dataWriter, to, signed, hints, packet.SigTypeText, config) +} + +// writeAndSign writes the data as a payload package and, optionally, signs +// it. hints contains optional information, that is also encrypted, +// that aids the recipients in processing the message. The resulting +// WriteCloser must be closed after the contents of the file have been +// written. If config is nil, sensible defaults will be used. +func writeAndSign(payload io.WriteCloser, candidateHashes []uint8, signed *Entity, hints *FileHints, sigType packet.SignatureType, config *packet.Config) (plaintext io.WriteCloser, err error) { + var signer *packet.PrivateKey + if signed != nil { + signKey, ok := signed.SigningKeyById(config.Now(), config.SigningKey()) + if !ok { + return nil, errors.InvalidArgumentError("no valid signing keys") + } + signer = signKey.PrivateKey + if signer == nil { + return nil, errors.InvalidArgumentError("no private key in signing key") + } + if signer.Encrypted { + return nil, errors.InvalidArgumentError("signing key must be decrypted") + } + } + + var hash crypto.Hash + for _, hashId := range candidateHashes { + if h, ok := s2k.HashIdToHash(hashId); ok && h.Available() { + hash = h + break + } + } + + // If the hash specified by config is a candidate, we'll use that. + if configuredHash := config.Hash(); configuredHash.Available() { + for _, hashId := range candidateHashes { + if h, ok := s2k.HashIdToHash(hashId); ok && h == configuredHash { + hash = h + break + } + } + } + + if hash == 0 { + hashId := candidateHashes[0] + name, ok := s2k.HashIdToString(hashId) + if !ok { + name = "#" + strconv.Itoa(int(hashId)) + } + return nil, errors.InvalidArgumentError("cannot encrypt because no candidate hash functions are compiled in. (Wanted " + name + " in this case.)") + } + + if signer != nil { + ops := &packet.OnePassSignature{ + SigType: sigType, + Hash: hash, + PubKeyAlgo: signer.PubKeyAlgo, + KeyId: signer.KeyId, + IsLast: true, + } + if err := ops.Serialize(payload); err != nil { + return nil, err + } + } + + if hints == nil { + hints = &FileHints{} + } + + w := payload + if signer != nil { + // If we need to write a signature packet after the literal + // data then we need to stop literalData from closing + // encryptedData. + w = noOpCloser{w} + + } + var epochSeconds uint32 + if !hints.ModTime.IsZero() { + epochSeconds = uint32(hints.ModTime.Unix()) + } + literalData, err := packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, epochSeconds) + if err != nil { + return nil, err + } + + if signer != nil { + h, wrappedHash, err := hashForSignature(hash, sigType) + if err != nil { + return nil, err + } + metadata := &packet.LiteralData{ + Format: 't', + FileName: hints.FileName, + Time: epochSeconds, + } + if hints.IsBinary { + metadata.Format = 'b' + } + return signatureWriter{payload, literalData, hash, wrappedHash, h, signer, sigType, config, metadata}, nil + } + return literalData, nil +} + +// encrypt encrypts a message to a number of recipients and, optionally, signs +// it. hints contains optional information, that is also encrypted, that aids +// the recipients in processing the message. The resulting WriteCloser must +// be closed after the contents of the file have been written. +// If config is nil, sensible defaults will be used. +func encrypt(keyWriter io.Writer, dataWriter io.Writer, to []*Entity, signed *Entity, hints *FileHints, sigType packet.SignatureType, config *packet.Config) (plaintext io.WriteCloser, err error) { + if len(to) == 0 { + return nil, errors.InvalidArgumentError("no encryption recipient provided") + } + + // These are the possible ciphers that we'll use for the message. + candidateCiphers := []uint8{ + uint8(packet.CipherAES128), + uint8(packet.CipherAES256), + uint8(packet.CipherCAST5), + } + // These are the possible hash functions that we'll use for the signature. + candidateHashes := []uint8{ + hashToHashId(crypto.SHA256), + hashToHashId(crypto.SHA384), + hashToHashId(crypto.SHA512), + hashToHashId(crypto.SHA1), + hashToHashId(crypto.RIPEMD160), + } + candidateAeadModes := []uint8{ + uint8(packet.AEADModeEAX), + uint8(packet.AEADModeOCB), + uint8(packet.AEADModeExperimentalGCM), + } + candidateCompression := []uint8{ + uint8(packet.CompressionNone), + uint8(packet.CompressionZIP), + uint8(packet.CompressionZLIB), + } + // In the event that a recipient doesn't specify any supported ciphers + // or hash functions, these are the ones that we assume that every + // implementation supports. + defaultCiphers := candidateCiphers[0:1] + defaultHashes := candidateHashes[0:1] + defaultAeadModes := candidateAeadModes[0:1] + defaultCompression := candidateCompression[0:1] + + encryptKeys := make([]Key, len(to)) + // AEAD is used only if every key supports it. + aeadSupported := true + + for i := range to { + var ok bool + encryptKeys[i], ok = to[i].EncryptionKey(config.Now()) + if !ok { + return nil, errors.InvalidArgumentError("cannot encrypt a message to key id " + strconv.FormatUint(to[i].PrimaryKey.KeyId, 16) + " because it has no encryption keys") + } + + sig := to[i].PrimaryIdentity().SelfSignature + if sig.AEAD == false { + aeadSupported = false + } + + preferredSymmetric := sig.PreferredSymmetric + if len(preferredSymmetric) == 0 { + preferredSymmetric = defaultCiphers + } + preferredHashes := sig.PreferredHash + if len(preferredHashes) == 0 { + preferredHashes = defaultHashes + } + preferredAeadModes := sig.PreferredAEAD + if len(preferredAeadModes) == 0 { + preferredAeadModes = defaultAeadModes + } + preferredCompression := sig.PreferredCompression + if len(preferredCompression) == 0 { + preferredCompression = defaultCompression + } + candidateCiphers = intersectPreferences(candidateCiphers, preferredSymmetric) + candidateHashes = intersectPreferences(candidateHashes, preferredHashes) + candidateAeadModes = intersectPreferences(candidateAeadModes, preferredAeadModes) + candidateCompression = intersectPreferences(candidateCompression, preferredCompression) + } + + if len(candidateCiphers) == 0 || len(candidateHashes) == 0 || len(candidateAeadModes) == 0 { + return nil, errors.InvalidArgumentError("cannot encrypt because recipient set shares no common algorithms") + } + + cipher := packet.CipherFunction(candidateCiphers[0]) + mode := packet.AEADMode(candidateAeadModes[0]) + // If the cipher specified by config is a candidate, we'll use that. + configuredCipher := config.Cipher() + for _, c := range candidateCiphers { + cipherFunc := packet.CipherFunction(c) + if cipherFunc == configuredCipher { + cipher = cipherFunc + break + } + } + + symKey := make([]byte, cipher.KeySize()) + if _, err := io.ReadFull(config.Random(), symKey); err != nil { + return nil, err + } + + for _, key := range encryptKeys { + if err := packet.SerializeEncryptedKey(keyWriter, key.PublicKey, cipher, symKey, config); err != nil { + return nil, err + } + } + + var payload io.WriteCloser + if aeadSupported { + payload, err = packet.SerializeAEADEncrypted(dataWriter, symKey, cipher, mode, config) + if err != nil { + return + } + } else { + payload, err = packet.SerializeSymmetricallyEncrypted(dataWriter, cipher, symKey, config) + if err != nil { + return + } + } + payload, err = handleCompression(payload, candidateCompression, config) + if err != nil { + return nil, err + } + + return writeAndSign(payload, candidateHashes, signed, hints, sigType, config) +} + +// Sign signs a message. The resulting WriteCloser must be closed after the +// contents of the file have been written. hints contains optional information +// that aids the recipients in processing the message. +// If config is nil, sensible defaults will be used. +func Sign(output io.Writer, signed *Entity, hints *FileHints, config *packet.Config) (input io.WriteCloser, err error) { + if signed == nil { + return nil, errors.InvalidArgumentError("no signer provided") + } + + // These are the possible hash functions that we'll use for the signature. + candidateHashes := []uint8{ + hashToHashId(crypto.SHA256), + hashToHashId(crypto.SHA384), + hashToHashId(crypto.SHA512), + hashToHashId(crypto.SHA1), + hashToHashId(crypto.RIPEMD160), + } + defaultHashes := candidateHashes[0:1] + preferredHashes := signed.PrimaryIdentity().SelfSignature.PreferredHash + if len(preferredHashes) == 0 { + preferredHashes = defaultHashes + } + candidateHashes = intersectPreferences(candidateHashes, preferredHashes) + if len(candidateHashes) == 0 { + return nil, errors.InvalidArgumentError("cannot sign because signing key shares no common algorithms with candidate hashes") + } + + return writeAndSign(noOpCloser{output}, candidateHashes, signed, hints, packet.SigTypeBinary, config) +} + +// signatureWriter hashes the contents of a message while passing it along to +// literalData. When closed, it closes literalData, writes a signature packet +// to encryptedData and then also closes encryptedData. +type signatureWriter struct { + encryptedData io.WriteCloser + literalData io.WriteCloser + hashType crypto.Hash + wrappedHash hash.Hash + h hash.Hash + signer *packet.PrivateKey + sigType packet.SignatureType + config *packet.Config + metadata *packet.LiteralData // V5 signatures protect document metadata +} + +func (s signatureWriter) Write(data []byte) (int, error) { + s.wrappedHash.Write(data) + switch s.sigType { + case packet.SigTypeBinary: + return s.literalData.Write(data) + case packet.SigTypeText: + flag := 0 + return writeCanonical(s.literalData, data, &flag) + } + return 0, errors.UnsupportedError("unsupported signature type: " + strconv.Itoa(int(s.sigType))) +} + +func (s signatureWriter) Close() error { + sig := &packet.Signature{ + Version: s.signer.Version, + SigType: s.sigType, + PubKeyAlgo: s.signer.PubKeyAlgo, + Hash: s.hashType, + CreationTime: s.config.Now(), + IssuerKeyId: &s.signer.KeyId, + Metadata: s.metadata, + } + + if err := sig.Sign(s.h, s.signer, s.config); err != nil { + return err + } + if err := s.literalData.Close(); err != nil { + return err + } + if err := sig.Serialize(s.encryptedData); err != nil { + return err + } + return s.encryptedData.Close() +} + +// noOpCloser is like an ioutil.NopCloser, but for an io.Writer. +// TODO: we have two of these in OpenPGP packages alone. This probably needs +// to be promoted somewhere more common. +type noOpCloser struct { + w io.Writer +} + +func (c noOpCloser) Write(data []byte) (n int, err error) { + return c.w.Write(data) +} + +func (c noOpCloser) Close() error { + return nil +} + +func handleCompression(compressed io.WriteCloser, candidateCompression []uint8, config *packet.Config) (data io.WriteCloser, err error) { + data = compressed + confAlgo := config.Compression() + if confAlgo == packet.CompressionNone { + return + } + finalAlgo := packet.CompressionNone + // if compression specified by config available we will use it + for _, c := range candidateCompression { + if uint8(confAlgo) == c { + finalAlgo = confAlgo + break + } + } + + if finalAlgo != packet.CompressionNone { + var compConfig *packet.CompressionConfig + if config != nil { + compConfig = config.CompressionConfig + } + data, err = packet.SerializeCompressed(compressed, finalAlgo, compConfig) + if err != nil { + return + } + } + return data, nil +} diff --git a/vendor/github.com/PuerkitoBio/goquery/.travis.yml b/vendor/github.com/PuerkitoBio/goquery/.travis.yml index 27c3ce81361c0..29e9e64ab23b5 100644 --- a/vendor/github.com/PuerkitoBio/goquery/.travis.yml +++ b/vendor/github.com/PuerkitoBio/goquery/.travis.yml @@ -1,3 +1,6 @@ +arch: + - amd64 + - ppc64le language: go go: @@ -15,3 +18,27 @@ go: - 1.13.x - tip +jobs: + exclude: + - arch: ppc64le + go: 1.2.x + - arch: ppc64le + go: 1.3.x + - arch: ppc64le + go: 1.4.x + - arch: ppc64le + go: 1.5.x + - arch: ppc64le + go: 1.6.x + - arch: ppc64le + go: 1.7.x + - arch: ppc64le + go: 1.8.x + - arch: ppc64le + go: 1.9.x + - arch: ppc64le + go: 1.10.x + - arch: ppc64le + go: 1.11.x + - arch: ppc64le + go: 1.12.x diff --git a/vendor/github.com/PuerkitoBio/goquery/README.md b/vendor/github.com/PuerkitoBio/goquery/README.md index 0bc221d163852..ac9b2c206caa7 100644 --- a/vendor/github.com/PuerkitoBio/goquery/README.md +++ b/vendor/github.com/PuerkitoBio/goquery/README.md @@ -37,6 +37,8 @@ Please note that because of the net/html dependency, goquery requires Go1.1+. **Note that goquery's API is now stable, and will not break.** +* **2021-01-11 (v1.6.1)** : Fix panic when calling `{Prepend,Append,Set}Html` on a `Selection` that contains non-Element nodes. +* **2020-10-08 (v1.6.0)** : Parse html in context of the container node for all functions that deal with html strings (`AfterHtml`, `AppendHtml`, etc.). Thanks to [@thiemok][thiemok] and [@davidjwilkins][djw] for their work on this. * **2020-02-04 (v1.5.1)** : Update module dependencies. * **2018-11-15 (v1.5.0)** : Go module support (thanks @Zaba505). * **2018-06-07 (v1.4.1)** : Add `NewDocumentFromReader` examples. @@ -143,8 +145,10 @@ func main() { - [gnulnx/goperf](https://github.com/gnulnx/goperf), a website performance test tool that also fetches static assets. - [MontFerret/ferret](https://github.com/MontFerret/ferret), declarative web scraping. - [tacusci/berrycms](https://github.com/tacusci/berrycms), a modern simple to use CMS with easy to write plugins -- [Dataflow kit](https://github.com/slotix/dataflowkit), Web Scraping framework for Gophers. +- [Dataflow kit](https://github.com/slotix/dataflowkit), Web Scraping framework for Gophers. - [Geziyor](https://github.com/geziyor/geziyor), a fast web crawling & scraping framework for Go. Supports JS rendering. +- [Pagser](https://github.com/foolin/pagser), a simple, easy, extensible, configurable HTML parser to struct based on goquery and struct tags. +- [stitcherd](https://github.com/vhodges/stitcherd), A server for doing server side includes using css selectors and DOM updates. ## Support @@ -181,3 +185,5 @@ The [BSD 3-Clause license][bsd], the same as the [Go language][golic]. Cascadia' [thatguystone]: https://github.com/thatguystone [piotr]: https://github.com/piotrkowalczuk [goq]: https://github.com/andrewstuart/goq +[thiemok]: https://github.com/thiemok +[djw]: https://github.com/davidjwilkins diff --git a/vendor/github.com/PuerkitoBio/goquery/manipulation.go b/vendor/github.com/PuerkitoBio/goquery/manipulation.go index 34eb7570fbe9e..35febf1189596 100644 --- a/vendor/github.com/PuerkitoBio/goquery/manipulation.go +++ b/vendor/github.com/PuerkitoBio/goquery/manipulation.go @@ -39,8 +39,15 @@ func (s *Selection) AfterSelection(sel *Selection) *Selection { // AfterHtml parses the html and inserts it after the set of matched elements. // // This follows the same rules as Selection.Append. -func (s *Selection) AfterHtml(html string) *Selection { - return s.AfterNodes(parseHtml(html)...) +func (s *Selection) AfterHtml(htmlStr string) *Selection { + return s.eachNodeHtml(htmlStr, true, func(node *html.Node, nodes []*html.Node) { + nextSibling := node.NextSibling + for _, n := range nodes { + if node.Parent != nil { + node.Parent.InsertBefore(n, nextSibling) + } + } + }) } // AfterNodes inserts the nodes after each element in the set of matched elements. @@ -85,8 +92,12 @@ func (s *Selection) AppendSelection(sel *Selection) *Selection { } // AppendHtml parses the html and appends it to the set of matched elements. -func (s *Selection) AppendHtml(html string) *Selection { - return s.AppendNodes(parseHtml(html)...) +func (s *Selection) AppendHtml(htmlStr string) *Selection { + return s.eachNodeHtml(htmlStr, false, func(node *html.Node, nodes []*html.Node) { + for _, n := range nodes { + node.AppendChild(n) + } + }) } // AppendNodes appends the specified nodes to each node in the set of matched elements. @@ -123,8 +134,14 @@ func (s *Selection) BeforeSelection(sel *Selection) *Selection { // BeforeHtml parses the html and inserts it before the set of matched elements. // // This follows the same rules as Selection.Append. -func (s *Selection) BeforeHtml(html string) *Selection { - return s.BeforeNodes(parseHtml(html)...) +func (s *Selection) BeforeHtml(htmlStr string) *Selection { + return s.eachNodeHtml(htmlStr, true, func(node *html.Node, nodes []*html.Node) { + for _, n := range nodes { + if node.Parent != nil { + node.Parent.InsertBefore(n, node) + } + } + }) } // BeforeNodes inserts the nodes before each element in the set of matched elements. @@ -184,8 +201,13 @@ func (s *Selection) PrependSelection(sel *Selection) *Selection { } // PrependHtml parses the html and prepends it to the set of matched elements. -func (s *Selection) PrependHtml(html string) *Selection { - return s.PrependNodes(parseHtml(html)...) +func (s *Selection) PrependHtml(htmlStr string) *Selection { + return s.eachNodeHtml(htmlStr, false, func(node *html.Node, nodes []*html.Node) { + firstChild := node.FirstChild + for _, n := range nodes { + node.InsertBefore(n, firstChild) + } + }) } // PrependNodes prepends the specified nodes to each node in the set of @@ -212,14 +234,19 @@ func (s *Selection) Remove() *Selection { return s } -// RemoveFiltered removes the set of matched elements by selector. -// It returns the Selection of removed nodes. +// RemoveFiltered removes from the current set of matched elements those that +// match the selector filter. It returns the Selection of removed nodes. +// +// For example if the selection s contains "

", "

" and "

" +// and s.RemoveFiltered("h2") is called, only the "

" node is removed +// (and returned), while "

" and "

" are kept in the document. func (s *Selection) RemoveFiltered(selector string) *Selection { return s.RemoveMatcher(compileMatcher(selector)) } -// RemoveMatcher removes the set of matched elements. -// It returns the Selection of removed nodes. +// RemoveMatcher removes from the current set of matched elements those that +// match the Matcher filter. It returns the Selection of removed nodes. +// See RemoveFiltered for additional information. func (s *Selection) RemoveMatcher(m Matcher) *Selection { return s.FilterMatcher(m).Remove() } @@ -256,8 +283,16 @@ func (s *Selection) ReplaceWithSelection(sel *Selection) *Selection { // It returns the removed elements. // // This follows the same rules as Selection.Append. -func (s *Selection) ReplaceWithHtml(html string) *Selection { - return s.ReplaceWithNodes(parseHtml(html)...) +func (s *Selection) ReplaceWithHtml(htmlStr string) *Selection { + s.eachNodeHtml(htmlStr, true, func(node *html.Node, nodes []*html.Node) { + nextSibling := node.NextSibling + for _, n := range nodes { + if node.Parent != nil { + node.Parent.InsertBefore(n, nextSibling) + } + } + }) + return s.Remove() } // ReplaceWithNodes replaces each element in the set of matched elements with @@ -272,8 +307,17 @@ func (s *Selection) ReplaceWithNodes(ns ...*html.Node) *Selection { // SetHtml sets the html content of each element in the selection to // specified html string. -func (s *Selection) SetHtml(html string) *Selection { - return setHtmlNodes(s, parseHtml(html)...) +func (s *Selection) SetHtml(htmlStr string) *Selection { + for _, context := range s.Nodes { + for c := context.FirstChild; c != nil; c = context.FirstChild { + context.RemoveChild(c) + } + } + return s.eachNodeHtml(htmlStr, false, func(node *html.Node, nodes []*html.Node) { + for _, n := range nodes { + node.AppendChild(n) + } + }) } // SetText sets the content of each element in the selection to specified content. @@ -329,8 +373,23 @@ func (s *Selection) WrapSelection(sel *Selection) *Selection { // most child of the given HTML. // // It returns the original set of elements. -func (s *Selection) WrapHtml(html string) *Selection { - return s.wrapNodes(parseHtml(html)...) +func (s *Selection) WrapHtml(htmlStr string) *Selection { + nodesMap := make(map[string][]*html.Node) + for _, context := range s.Nodes { + var parent *html.Node + if context.Parent != nil { + parent = context.Parent + } else { + parent = &html.Node{Type: html.ElementNode} + } + nodes, found := nodesMap[nodeName(parent)] + if !found { + nodes = parseHtmlWithContext(htmlStr, parent) + nodesMap[nodeName(parent)] = nodes + } + newSingleSelection(context, s.document).wrapAllNodes(cloneNodes(nodes)...) + } + return s } // WrapNode wraps each element in the set of matched elements inside the inner- @@ -382,8 +441,18 @@ func (s *Selection) WrapAllSelection(sel *Selection) *Selection { // document. // // It returns the original set of elements. -func (s *Selection) WrapAllHtml(html string) *Selection { - return s.wrapAllNodes(parseHtml(html)...) +func (s *Selection) WrapAllHtml(htmlStr string) *Selection { + var context *html.Node + var nodes []*html.Node + if len(s.Nodes) > 0 { + context = s.Nodes[0] + if context.Parent != nil { + nodes = parseHtmlWithContext(htmlStr, context) + } else { + nodes = parseHtml(htmlStr) + } + } + return s.wrapAllNodes(nodes...) } func (s *Selection) wrapAllNodes(ns ...*html.Node) *Selection { @@ -452,8 +521,17 @@ func (s *Selection) WrapInnerSelection(sel *Selection) *Selection { // cloned before being inserted into the document. // // It returns the original set of elements. -func (s *Selection) WrapInnerHtml(html string) *Selection { - return s.wrapInnerNodes(parseHtml(html)...) +func (s *Selection) WrapInnerHtml(htmlStr string) *Selection { + nodesMap := make(map[string][]*html.Node) + for _, context := range s.Nodes { + nodes, found := nodesMap[nodeName(context)] + if !found { + nodes = parseHtmlWithContext(htmlStr, context) + nodesMap[nodeName(context)] = nodes + } + newSingleSelection(context, s.document).wrapInnerNodes(cloneNodes(nodes)...) + } + return s } // WrapInnerNode wraps an HTML structure, matched by the given selector, around @@ -493,16 +571,14 @@ func parseHtml(h string) []*html.Node { return nodes } -func setHtmlNodes(s *Selection, ns ...*html.Node) *Selection { - for _, n := range s.Nodes { - for c := n.FirstChild; c != nil; c = n.FirstChild { - n.RemoveChild(c) - } - for _, c := range ns { - n.AppendChild(cloneNode(c)) - } +func parseHtmlWithContext(h string, context *html.Node) []*html.Node { + // Errors are only returned when the io.Reader returns any error besides + // EOF, but strings.Reader never will + nodes, err := html.ParseFragment(strings.NewReader(h), context) + if err != nil { + panic("goquery: failed to parse HTML: " + err.Error()) } - return s + return nodes } // Get the first child that is an ElementNode @@ -572,3 +648,32 @@ func (s *Selection) manipulateNodes(ns []*html.Node, reverse bool, return s } + +// eachNodeHtml parses the given html string and inserts the resulting nodes in the dom with the mergeFn. +// The parsed nodes are inserted for each element of the selection. +// isParent can be used to indicate that the elements of the selection should be treated as the parent for the parsed html. +// A cache is used to avoid parsing the html multiple times should the elements of the selection result in the same context. +func (s *Selection) eachNodeHtml(htmlStr string, isParent bool, mergeFn func(n *html.Node, nodes []*html.Node)) *Selection { + // cache to avoid parsing the html for the same context multiple times + nodeCache := make(map[string][]*html.Node) + var context *html.Node + for _, n := range s.Nodes { + if isParent { + context = n.Parent + } else { + if n.Type != html.ElementNode { + continue + } + context = n + } + if context != nil { + nodes, found := nodeCache[nodeName(context)] + if !found { + nodes = parseHtmlWithContext(htmlStr, context) + nodeCache[nodeName(context)] = nodes + } + mergeFn(n, cloneNodes(nodes)) + } + } + return s +} diff --git a/vendor/github.com/PuerkitoBio/goquery/utilities.go b/vendor/github.com/PuerkitoBio/goquery/utilities.go index b4c061a4d4304..3e11b1db1b424 100644 --- a/vendor/github.com/PuerkitoBio/goquery/utilities.go +++ b/vendor/github.com/PuerkitoBio/goquery/utilities.go @@ -36,12 +36,22 @@ func NodeName(s *Selection) string { if s.Length() == 0 { return "" } - switch n := s.Get(0); n.Type { + return nodeName(s.Get(0)) +} + +// nodeName returns the node name of the given html node. +// See NodeName for additional details on behaviour. +func nodeName(node *html.Node) string { + if node == nil { + return "" + } + + switch node.Type { case html.ElementNode, html.DoctypeNode: - return n.Data + return node.Data default: - if n.Type >= 0 && int(n.Type) < len(nodeNames) { - return nodeNames[n.Type] + if node.Type >= 0 && int(node.Type) < len(nodeNames) { + return nodeNames[node.Type] } return "" } diff --git a/vendor/github.com/RoaringBitmap/roaring/Makefile b/vendor/github.com/RoaringBitmap/roaring/Makefile index a22f3d330bcde..0a4f9f0aae524 100644 --- a/vendor/github.com/RoaringBitmap/roaring/Makefile +++ b/vendor/github.com/RoaringBitmap/roaring/Makefile @@ -64,7 +64,7 @@ qa: fmtcheck test vet lint # Get the dependencies deps: GOPATH=$(GOPATH) go get github.com/stretchr/testify - GOPATH=$(GOPATH) go get github.com/willf/bitset + GOPATH=$(GOPATH) go get github.com/bits-and-blooms/bitset GOPATH=$(GOPATH) go get github.com/golang/lint/golint GOPATH=$(GOPATH) go get github.com/mschoch/smat GOPATH=$(GOPATH) go get github.com/dvyukov/go-fuzz/go-fuzz diff --git a/vendor/github.com/RoaringBitmap/roaring/README.md b/vendor/github.com/RoaringBitmap/roaring/README.md index 00d2351c05bbb..2a7a1290609c5 100644 --- a/vendor/github.com/RoaringBitmap/roaring/README.md +++ b/vendor/github.com/RoaringBitmap/roaring/README.md @@ -1,4 +1,4 @@ -roaring [![Build Status](https://travis-ci.org/RoaringBitmap/roaring.png)](https://travis-ci.org/RoaringBitmap/roaring) [![GoDoc](https://godoc.org/github.com/RoaringBitmap/roaring?status.svg)](https://godoc.org/github.com/RoaringBitmap/roaring) [![GoDoc](https://godoc.org/github.com/RoaringBitmap/roaring/roaring64?status.svg)](https://godoc.org/github.com/RoaringBitmap/roaring/roaring64) [![Go Report Card](https://goreportcard.com/badge/RoaringBitmap/roaring)](https://goreportcard.com/report/github.com/RoaringBitmap/roaring) +roaring [![Build Status](https://travis-ci.org/RoaringBitmap/roaring.png)](https://travis-ci.org/RoaringBitmap/roaring) [![GoDoc](https://godoc.org/github.com/RoaringBitmap/roaring/roaring64?status.svg)](https://godoc.org/github.com/RoaringBitmap/roaring/roaring64) [![Go Report Card](https://goreportcard.com/badge/RoaringBitmap/roaring)](https://goreportcard.com/report/github.com/RoaringBitmap/roaring) [![Build Status](https://cloud.drone.io/api/badges/RoaringBitmap/roaring/status.svg)](https://cloud.drone.io/RoaringBitmap/roaring) ![Go-CI](https://github.com/RoaringBitmap/roaring/workflows/Go-CI/badge.svg) ![Go-ARM-CI](https://github.com/RoaringBitmap/roaring/workflows/Go-ARM-CI/badge.svg) @@ -84,7 +84,7 @@ When the bitset approach is applicable, it can be orders of magnitude faster than other possible implementation of a set (e.g., as a hash set) while using several times less memory. -However, a bitset, even a compressed one is not always applicable. For example, if the +However, a bitset, even a compressed one is not always applicable. For example, if you have 1000 random-looking integers, then a simple array might be the best representation. We refer to this case as the "sparse" scenario. @@ -158,7 +158,7 @@ http://arxiv.org/abs/1402.6407 This paper used data from http://lemire.me/data/r Dependencies are fetched automatically by giving the `-t` flag to `go get`. they include - - github.com/willf/bitset + - github.com/bits-and-blooms/bitset - github.com/mschoch/smat - github.com/glycerine/go-unsnap-stream - github.com/philhofer/fwd @@ -384,12 +384,14 @@ You can help us test further the library with fuzzy testing: go get github.com/dvyukov/go-fuzz/go-fuzz-build go test -tags=gofuzz -run=TestGenerateSmatCorpus go-fuzz-build github.com/RoaringBitmap/roaring - go-fuzz -bin=./roaring-fuzz.zip -workdir=workdir/ -timeout=200 + go-fuzz -bin=./roaring-fuzz.zip -workdir=workdir/ -timeout=200 -func FuzzSmat Let it run, and if the # of crashers is > 0, check out the reports in the workdir where you should be able to find the panic goroutine stack traces. +You may also replace `-func FuzzSmat` by `-func FuzzSerializationBuffer` or `-func FuzzSerializationStream`. + ### Alternative in Go There is a Go version wrapping the C/C++ implementation https://github.com/RoaringBitmap/gocroaring diff --git a/vendor/github.com/RoaringBitmap/roaring/arraycontainer.go b/vendor/github.com/RoaringBitmap/roaring/arraycontainer.go index 7390ba8df6993..260e1cbcef7e0 100644 --- a/vendor/github.com/RoaringBitmap/roaring/arraycontainer.go +++ b/vendor/github.com/RoaringBitmap/roaring/arraycontainer.go @@ -16,10 +16,11 @@ func (ac *arrayContainer) String() string { return s + "}" } -func (ac *arrayContainer) fillLeastSignificant16bits(x []uint32, i int, mask uint32) { +func (ac *arrayContainer) fillLeastSignificant16bits(x []uint32, i int, mask uint32) int { for k := 0; k < len(ac.content); k++ { x[k+i] = uint32(ac.content[k]) | mask } + return i + len(ac.content) } func (ac *arrayContainer) iterate(cb func(x uint16) bool) bool { @@ -394,11 +395,19 @@ func (ac *arrayContainer) iorBitmap(bc2 *bitmapContainer) container { } func (ac *arrayContainer) iorRun16(rc *runContainer16) container { - bc1 := ac.toBitmapContainer() - bc2 := rc.toBitmapContainer() - bc1.iorBitmap(bc2) - *ac = *newArrayContainerFromBitmap(bc1) - return ac + runCardinality := rc.getCardinality() + // heuristic for if the container should maybe be an + // array container. + if runCardinality < ac.getCardinality() && + runCardinality+ac.getCardinality() < arrayDefaultMaxSize { + var result container + result = ac + for _, run := range rc.iv { + result = result.iaddRange(int(run.start), int(run.start)+int(run.length)) + } + return result + } + return rc.orArray(ac) } func (ac *arrayContainer) lazyIOR(a container) container { @@ -843,6 +852,10 @@ func (ac *arrayContainer) getCardinality() int { return len(ac.content) } +func (ac *arrayContainer) isEmpty() bool { + return len(ac.content) == 0 +} + func (ac *arrayContainer) rank(x uint16) int { answer := binarySearch(ac.content, x) if answer >= 0 { @@ -882,7 +895,7 @@ func (ac *arrayContainer) resetTo(a container) { x.fillArray(ac.content) case *runContainer16: - card := int(x.cardinality()) + card := int(x.getCardinality()) ac.realloc(card) cur := 0 for _, r := range x.iv { @@ -956,10 +969,10 @@ func (ac *arrayContainer) numberOfRuns() (nr int) { runlen++ } else { if cur < prev { - panic("then fundamental arrayContainer assumption of sorted ac.content was broken") + panic("the fundamental arrayContainer assumption of sorted ac.content was broken") } if cur == prev { - panic("then fundamental arrayContainer assumption of deduplicated content was broken") + panic("the fundamental arrayContainer assumption of deduplicated content was broken") } else { nr++ runlen = 0 diff --git a/vendor/github.com/RoaringBitmap/roaring/bitmapcontainer.go b/vendor/github.com/RoaringBitmap/roaring/bitmapcontainer.go index b576c2ac85770..f8367da0e5f2f 100644 --- a/vendor/github.com/RoaringBitmap/roaring/bitmapcontainer.go +++ b/vendor/github.com/RoaringBitmap/roaring/bitmapcontainer.go @@ -264,7 +264,7 @@ func bitmapEquals(a, b []uint64) bool { return true } -func (bc *bitmapContainer) fillLeastSignificant16bits(x []uint32, i int, mask uint32) { +func (bc *bitmapContainer) fillLeastSignificant16bits(x []uint32, i int, mask uint32) int { // TODO: should be written as optimized assembly pos := i base := mask @@ -278,6 +278,7 @@ func (bc *bitmapContainer) fillLeastSignificant16bits(x []uint32, i int, mask ui } base += 64 } + return pos } func (bc *bitmapContainer) equals(o container) bool { @@ -349,6 +350,11 @@ func (bc *bitmapContainer) getCardinality() int { return bc.cardinality } + +func (bc *bitmapContainer) isEmpty() bool { + return bc.cardinality == 0 +} + func (bc *bitmapContainer) clone() container { ptr := bitmapContainer{bc.cardinality, make([]uint64, len(bc.bitmap))} copy(ptr.bitmap, bc.bitmap[:]) @@ -1132,16 +1138,12 @@ func (bc *bitmapContainer) addOffset(x uint16) []container { low.bitmap[b] = bc.bitmap[0] << i for k := uint32(1); k < end; k++ { newval := bc.bitmap[k] << i - if newval == 0 { - newval = bc.bitmap[k-1] >> (64 - i) - } + newval |= bc.bitmap[k-1] >> (64 - i) low.bitmap[b+k] = newval } for k := end; k < 1024; k++ { newval := bc.bitmap[k] << i - if newval == 0 { - newval = bc.bitmap[k-1] >> (64 - i) - } + newval |= bc.bitmap[k-1] >> (64 - i) high.bitmap[k-end] = newval } high.bitmap[b] = bc.bitmap[1023] >> (64 - i) diff --git a/vendor/github.com/RoaringBitmap/roaring/fastaggregation.go b/vendor/github.com/RoaringBitmap/roaring/fastaggregation.go index ae731b35a4ca2..342c7fd4844bc 100644 --- a/vendor/github.com/RoaringBitmap/roaring/fastaggregation.go +++ b/vendor/github.com/RoaringBitmap/roaring/fastaggregation.go @@ -301,9 +301,6 @@ func (x1 *Bitmap) AndAny(bitmaps ...*Bitmap) { tmpBitmap = newBitmapContainer() } tmpBitmap.resetTo(keyContainers[0]) - for _, c := range keyContainers[1:] { - tmpBitmap.ior(c) - } ored = tmpBitmap } else { if tmpArray == nil { @@ -311,15 +308,15 @@ func (x1 *Bitmap) AndAny(bitmaps ...*Bitmap) { } tmpArray.realloc(maxPossibleOr) tmpArray.resetTo(keyContainers[0]) - for _, c := range keyContainers[1:] { - tmpArray.ior(c) - } ored = tmpArray } + for _, c := range keyContainers[1:] { + ored = ored.ior(c) + } } result := x1.highlowcontainer.getWritableContainerAtIndex(basePos).iand(ored) - if result.getCardinality() > 0 { + if !result.isEmpty() { x1.highlowcontainer.replaceKeyAndContainerAtIndex(intersections, baseKey, result, false) intersections++ } diff --git a/vendor/github.com/RoaringBitmap/roaring/go.mod b/vendor/github.com/RoaringBitmap/roaring/go.mod index 4667133cf3c1c..2a31b3ccc50d7 100644 --- a/vendor/github.com/RoaringBitmap/roaring/go.mod +++ b/vendor/github.com/RoaringBitmap/roaring/go.mod @@ -3,15 +3,18 @@ module github.com/RoaringBitmap/roaring go 1.14 require ( + github.com/bits-and-blooms/bitset v1.2.0 + github.com/dvyukov/go-fuzz v0.0.0-20210429054444-fca39067bc72 // indirect + github.com/elazarl/go-bindata-assetfs v1.0.1 // indirect github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2 github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31 // indirect github.com/golang/snappy v0.0.1 // indirect github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 // indirect github.com/jtolds/gls v4.20.0+incompatible // indirect - github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae + github.com/mschoch/smat v0.2.0 github.com/philhofer/fwd v1.0.0 // indirect + github.com/stephens2424/writerset v1.0.2 // indirect github.com/stretchr/testify v1.4.0 - github.com/willf/bitset v1.1.10 golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect golang.org/x/tools v0.0.0-20200928182047-19e03678916f // indirect ) diff --git a/vendor/github.com/RoaringBitmap/roaring/go.sum b/vendor/github.com/RoaringBitmap/roaring/go.sum index 8798080871d0c..85373d358f114 100644 --- a/vendor/github.com/RoaringBitmap/roaring/go.sum +++ b/vendor/github.com/RoaringBitmap/roaring/go.sum @@ -1,5 +1,12 @@ +github.com/Julusian/godocdown v0.0.0-20170816220326-6d19f8ff2df8/go.mod h1:INZr5t32rG59/5xeltqoCJoNY7e5x/3xoY9WSWVWg74= +github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA= +github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= 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/dvyukov/go-fuzz v0.0.0-20210429054444-fca39067bc72 h1:XiR1YwcWcRFzxjAhWK29HQL4nocj0QWJjpeRi/YASV0= +github.com/dvyukov/go-fuzz v0.0.0-20210429054444-fca39067bc72/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= +github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw= +github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2 h1:Ujru1hufTHVb++eG6OuNDKMxZnGIvF6o/u8q/8h2+I4= github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31 h1:gclg6gY70GLy3PbkQ1AERPfmLMMagS60DKF78eWwLn8= @@ -12,16 +19,20 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae h1:VeRdUYdCw49yizlSbMEn2SZ+gT+3IUKx8BqxyQdz+BY= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= +github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM= +github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= 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/robertkrimen/godocdown v0.0.0-20130622164427-0bfa04905481/go.mod h1:C9WhFzY47SzYBIvzFqSvHIR6ROgDo4TtdTuRaOMjF/s= +github.com/stephens2424/writerset v1.0.2 h1:znRLgU6g8RS5euYRcy004XeE4W+Tu44kALzy7ghPif8= +github.com/stephens2424/writerset v1.0.2/go.mod h1:aS2JhsMn6eA7e82oNmW4rfsgAOp9COBTTl8mzkwADnc= github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= 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/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc= -github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -29,6 +40,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -46,6 +58,7 @@ golang.org/x/tools v0.0.0-20200928182047-19e03678916f h1:VwGa2Wf+rHGIxvsssCkUNIy golang.org/x/tools v0.0.0-20200928182047-19e03678916f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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= diff --git a/vendor/github.com/RoaringBitmap/roaring/parallel.go b/vendor/github.com/RoaringBitmap/roaring/parallel.go index 6d8e29360a9cc..805f146bac272 100644 --- a/vendor/github.com/RoaringBitmap/roaring/parallel.go +++ b/vendor/github.com/RoaringBitmap/roaring/parallel.go @@ -285,14 +285,14 @@ func ParAnd(parallelism int, bitmaps ...*Bitmap) *Bitmap { for input := range inputChan { c := input.containers[0].and(input.containers[1]) for _, next := range input.containers[2:] { - if c.getCardinality() == 0 { + if c.isEmpty() { break } c = c.iand(next) } // Send a nil explicitly if the result of the intersection is an empty container - if c.getCardinality() == 0 { + if c.isEmpty() { c = nil } @@ -357,7 +357,7 @@ func ParOr(parallelism int, bitmaps ...*Bitmap) *Bitmap { return bitmaps[0] } - keyRange := hKey - lKey + 1 + keyRange := int(hKey) - int(lKey) + 1 if keyRange == 1 { // revert to FastOr. Since the key range is 0 // no container-level aggregation parallelism is achievable diff --git a/vendor/github.com/RoaringBitmap/roaring/roaring.go b/vendor/github.com/RoaringBitmap/roaring/roaring.go index b78f83cc27127..15332e4956c58 100644 --- a/vendor/github.com/RoaringBitmap/roaring/roaring.go +++ b/vendor/github.com/RoaringBitmap/roaring/roaring.go @@ -151,8 +151,7 @@ func (rb *Bitmap) ToArray() []uint32 { hs := uint32(rb.highlowcontainer.getKeyAtIndex(pos)) << 16 c := rb.highlowcontainer.getContainerAtIndex(pos) pos++ - c.fillLeastSignificant16bits(array, pos2, hs) - pos2 += c.getCardinality() + pos2 = c.fillLeastSignificant16bits(array, pos2, hs) } return array } @@ -542,7 +541,7 @@ func AddOffset64(x *Bitmap, offset int64) (answer *Bitmap) { c := x.highlowcontainer.getContainerAtIndex(pos) offsetted := c.addOffset(inOffset) - if offsetted[0].getCardinality() > 0 && (key >= 0 && key <= MaxUint16) { + if !offsetted[0].isEmpty() && (key >= 0 && key <= MaxUint16) { curSize := answer.highlowcontainer.size() lastkey := int32(0) @@ -559,7 +558,7 @@ func AddOffset64(x *Bitmap, offset int64) (answer *Bitmap) { } } - if offsetted[1].getCardinality() > 0 && ((key+1) >= 0 && (key+1) <= MaxUint16) { + if !offsetted[1].isEmpty() && ((key+1) >= 0 && (key+1) <= MaxUint16) { answer.highlowcontainer.appendContainer(uint16(key+1), offsetted[1], false) } } @@ -630,13 +629,13 @@ func (rb *Bitmap) Remove(x uint32) { if i >= 0 { c := rb.highlowcontainer.getWritableContainerAtIndex(i).iremoveReturnMinimized(lowbits(x)) rb.highlowcontainer.setContainerAtIndex(i, c) - if rb.highlowcontainer.getContainerAtIndex(i).getCardinality() == 0 { + if rb.highlowcontainer.getContainerAtIndex(i).isEmpty() { rb.highlowcontainer.removeAtIndex(i) } } } -// CheckedRemove removes the integer x from the bitmap and return true if the integer was effectively remove (and false if the integer was not present) +// CheckedRemove removes the integer x from the bitmap and return true if the integer was effectively removed (and false if the integer was not present) func (rb *Bitmap) CheckedRemove(x uint32) bool { // TODO: add unit tests for this method hb := highbits(x) @@ -646,7 +645,7 @@ func (rb *Bitmap) CheckedRemove(x uint32) bool { oldcard := C.getCardinality() C = C.iremoveReturnMinimized(lowbits(x)) rb.highlowcontainer.setContainerAtIndex(i, C) - if rb.highlowcontainer.getContainerAtIndex(i).getCardinality() == 0 { + if rb.highlowcontainer.getContainerAtIndex(i).isEmpty() { rb.highlowcontainer.removeAtIndex(i) return true } @@ -701,8 +700,9 @@ func (rb *Bitmap) Select(x uint32) (uint32, error) { remaining := x for i := 0; i < rb.highlowcontainer.size(); i++ { c := rb.highlowcontainer.getContainerAtIndex(i) - if remaining >= uint32(c.getCardinality()) { - remaining -= uint32(c.getCardinality()) + card := uint32(c.getCardinality()) + if remaining >= card { + remaining -= card } else { key := rb.highlowcontainer.getKeyAtIndex(i) return uint32(key)<<16 + uint32(c.selectInt(uint16(remaining))), nil @@ -729,7 +729,7 @@ main: c1 := rb.highlowcontainer.getWritableContainerAtIndex(pos1) c2 := x2.highlowcontainer.getContainerAtIndex(pos2) diff := c1.iand(c2) - if diff.getCardinality() > 0 { + if !diff.isEmpty() { rb.highlowcontainer.replaceKeyAndContainerAtIndex(intersectionsize, s1, diff, false) intersectionsize++ } @@ -931,7 +931,7 @@ func (rb *Bitmap) Xor(x2 *Bitmap) { } else { // TODO: couple be computed in-place for reduced memory usage c := rb.highlowcontainer.getContainerAtIndex(pos1).xor(x2.highlowcontainer.getContainerAtIndex(pos2)) - if c.getCardinality() > 0 { + if !c.isEmpty() { rb.highlowcontainer.setContainerAtIndex(pos1, c) pos1++ } else { @@ -1011,7 +1011,7 @@ main: c1 := rb.highlowcontainer.getWritableContainerAtIndex(pos1) c2 := x2.highlowcontainer.getContainerAtIndex(pos2) diff := c1.iandNot(c2) - if diff.getCardinality() > 0 { + if !diff.isEmpty() { rb.highlowcontainer.replaceKeyAndContainerAtIndex(intersectionsize, s1, diff, false) intersectionsize++ } @@ -1120,7 +1120,7 @@ main: C := x1.highlowcontainer.getContainerAtIndex(pos1) C = C.and(x2.highlowcontainer.getContainerAtIndex(pos2)) - if C.getCardinality() > 0 { + if !C.isEmpty() { answer.highlowcontainer.appendContainer(s1, C, false) } pos1++ @@ -1167,7 +1167,7 @@ func Xor(x1, x2 *Bitmap) *Bitmap { pos2++ } else { c := x1.highlowcontainer.getContainerAtIndex(pos1).xor(x2.highlowcontainer.getContainerAtIndex(pos2)) - if c.getCardinality() > 0 { + if !c.isEmpty() { answer.highlowcontainer.appendContainer(s1, c, false) } pos1++ @@ -1210,7 +1210,7 @@ main: c1 := x1.highlowcontainer.getContainerAtIndex(pos1) c2 := x2.highlowcontainer.getContainerAtIndex(pos2) diff := c1.andNot(c2) - if diff.getCardinality() > 0 { + if !diff.isEmpty() { answer.highlowcontainer.appendContainer(s1, diff, false) } pos1++ @@ -1300,7 +1300,7 @@ func (rb *Bitmap) Flip(rangeStart, rangeEnd uint64) { if i >= 0 { c := rb.highlowcontainer.getWritableContainerAtIndex(i).inot(int(containerStart), int(containerLast)+1) - if c.getCardinality() > 0 { + if !c.isEmpty() { rb.highlowcontainer.setContainerAtIndex(i, c) } else { rb.highlowcontainer.removeAtIndex(i) @@ -1381,7 +1381,7 @@ func (rb *Bitmap) RemoveRange(rangeStart, rangeEnd uint64) { return } c := rb.highlowcontainer.getWritableContainerAtIndex(i).iremoveRange(int(lbStart), int(lbLast+1)) - if c.getCardinality() > 0 { + if !c.isEmpty() { rb.highlowcontainer.setContainerAtIndex(i, c) } else { rb.highlowcontainer.removeAtIndex(i) @@ -1394,7 +1394,7 @@ func (rb *Bitmap) RemoveRange(rangeStart, rangeEnd uint64) { if ifirst >= 0 { if lbStart != 0 { c := rb.highlowcontainer.getWritableContainerAtIndex(ifirst).iremoveRange(int(lbStart), int(max+1)) - if c.getCardinality() > 0 { + if !c.isEmpty() { rb.highlowcontainer.setContainerAtIndex(ifirst, c) ifirst++ } @@ -1405,7 +1405,7 @@ func (rb *Bitmap) RemoveRange(rangeStart, rangeEnd uint64) { if ilast >= 0 { if lbLast != max { c := rb.highlowcontainer.getWritableContainerAtIndex(ilast).iremoveRange(int(0), int(lbLast+1)) - if c.getCardinality() > 0 { + if !c.isEmpty() { rb.highlowcontainer.setContainerAtIndex(ilast, c) } else { ilast++ @@ -1461,7 +1461,7 @@ func Flip(bm *Bitmap, rangeStart, rangeEnd uint64) *Bitmap { if i >= 0 { c := bm.highlowcontainer.getContainerAtIndex(i).not(int(containerStart), int(containerLast)+1) - if c.getCardinality() > 0 { + if !c.isEmpty() { answer.highlowcontainer.insertNewKeyValueAt(-j-1, uint16(hb), c) } diff --git a/vendor/github.com/RoaringBitmap/roaring/roaringarray.go b/vendor/github.com/RoaringBitmap/roaring/roaringarray.go index 4aefc6a31619e..6aff57806a55f 100644 --- a/vendor/github.com/RoaringBitmap/roaring/roaringarray.go +++ b/vendor/github.com/RoaringBitmap/roaring/roaringarray.go @@ -17,6 +17,7 @@ type container interface { iand(container) container // i stands for inplace andNot(container) container iandNot(container) container // i stands for inplace + isEmpty() bool getCardinality() int // rank returns the number of integers that are // smaller or equal to x. rank(infinity) would be getCardinality(). @@ -47,7 +48,7 @@ type container interface { // any of the implementations. equals(r container) bool - fillLeastSignificant16bits(array []uint32, i int, mask uint32) + fillLeastSignificant16bits(array []uint32, i int, mask uint32) int or(r container) container orCardinality(r container) int isFull() bool @@ -645,7 +646,6 @@ func (ra *roaringArray) readFrom(stream internal.ByteInput, cookieHeader ...byte nb := runContainer16{ iv: byteSliceAsInterval16Slice(buf), - card: int64(card), } ra.containers[i] = &nb diff --git a/vendor/github.com/RoaringBitmap/roaring/runcontainer.go b/vendor/github.com/RoaringBitmap/roaring/runcontainer.go index 97c0036589fef..a722760b48811 100644 --- a/vendor/github.com/RoaringBitmap/roaring/runcontainer.go +++ b/vendor/github.com/RoaringBitmap/roaring/runcontainer.go @@ -47,8 +47,7 @@ import ( // runContainer16 does run-length encoding of sets of // uint16 integers. type runContainer16 struct { - iv []interval16 - card int64 + iv []interval16 } // interval16 is the internal to runContainer16 @@ -71,8 +70,8 @@ func newInterval16Range(start, last uint16) interval16 { } // runlen returns the count of integers in the interval. -func (iv interval16) runlen() int64 { - return int64(iv.length) + 1 +func (iv interval16) runlen() int { + return int(iv.length) + 1 } func (iv interval16) last() uint16 { @@ -194,7 +193,6 @@ func newRunContainer16FromVals(alreadySorted bool, vals ...uint16) *runContainer ah.storeIval(ah.runstart, ah.runlen) } rc.iv = ah.m - rc.card = int64(ah.actuallyAdded) return rc } @@ -284,7 +282,6 @@ func newRunContainer16FromArray(arr *arrayContainer) *runContainer16 { ah.storeIval(ah.runstart, ah.runlen) } rc.iv = ah.m - rc.card = int64(ah.actuallyAdded) return rc } @@ -301,7 +298,6 @@ func (rc *runContainer16) set(alreadySorted bool, vals ...uint16) { rc2 := newRunContainer16FromVals(alreadySorted, vals...) un := rc.union(rc2) rc.iv = un.iv - rc.card = 0 } // canMerge returns true iff the intervals @@ -309,10 +305,10 @@ func (rc *runContainer16) set(alreadySorted bool, vals ...uint16) { // contiguous and so can be merged into // a single interval. func canMerge16(a, b interval16) bool { - if int64(a.last())+1 < int64(b.start) { + if int(a.last())+1 < int(b.start) { return false } - return int64(b.last())+1 >= int64(a.start) + return int(b.last())+1 >= int(a.start) } // haveOverlap differs from canMerge in that @@ -321,10 +317,10 @@ func canMerge16(a, b interval16) bool { // it would be the empty set, and we return // false). func haveOverlap16(a, b interval16) bool { - if int64(a.last())+1 <= int64(b.start) { + if int(a.last())+1 <= int(b.start) { return false } - return int64(b.last())+1 > int64(a.start) + return int(b.last())+1 > int(a.start) } // mergeInterval16s joins a and b into a @@ -385,11 +381,11 @@ func (rc *runContainer16) union(b *runContainer16) *runContainer16 { var m []interval16 - alim := int64(len(rc.iv)) - blim := int64(len(b.iv)) + alim := int(len(rc.iv)) + blim := int(len(b.iv)) - var na int64 // next from a - var nb int64 // next from b + var na int // next from a + var nb int // next from b // merged holds the current merge output, which might // get additional merges before being appended to m. @@ -409,12 +405,12 @@ func (rc *runContainer16) union(b *runContainer16) *runContainer16 { mergedUpdated := false if canMerge16(cura, merged) { merged = mergeInterval16s(cura, merged) - na = rc.indexOfIntervalAtOrAfter(int64(merged.last())+1, na+1) + na = rc.indexOfIntervalAtOrAfter(int(merged.last())+1, na+1) mergedUpdated = true } if canMerge16(curb, merged) { merged = mergeInterval16s(curb, merged) - nb = b.indexOfIntervalAtOrAfter(int64(merged.last())+1, nb+1) + nb = b.indexOfIntervalAtOrAfter(int(merged.last())+1, nb+1) mergedUpdated = true } if !mergedUpdated { @@ -437,8 +433,8 @@ func (rc *runContainer16) union(b *runContainer16) *runContainer16 { } else { merged = mergeInterval16s(cura, curb) mergedUsed = true - na = rc.indexOfIntervalAtOrAfter(int64(merged.last())+1, na+1) - nb = b.indexOfIntervalAtOrAfter(int64(merged.last())+1, nb+1) + na = rc.indexOfIntervalAtOrAfter(int(merged.last())+1, na+1) + nb = b.indexOfIntervalAtOrAfter(int(merged.last())+1, nb+1) } } } @@ -457,7 +453,7 @@ func (rc *runContainer16) union(b *runContainer16) *runContainer16 { cura = rc.iv[na] if canMerge16(cura, merged) { merged = mergeInterval16s(cura, merged) - na = rc.indexOfIntervalAtOrAfter(int64(merged.last())+1, na+1) + na = rc.indexOfIntervalAtOrAfter(int(merged.last())+1, na+1) } else { break aAdds } @@ -471,7 +467,7 @@ func (rc *runContainer16) union(b *runContainer16) *runContainer16 { curb = b.iv[nb] if canMerge16(curb, merged) { merged = mergeInterval16s(curb, merged) - nb = b.indexOfIntervalAtOrAfter(int64(merged.last())+1, nb+1) + nb = b.indexOfIntervalAtOrAfter(int(merged.last())+1, nb+1) } else { break bAdds } @@ -493,17 +489,17 @@ func (rc *runContainer16) union(b *runContainer16) *runContainer16 { } // unionCardinality returns the cardinality of the merger of two runContainer16s, the union of rc and b. -func (rc *runContainer16) unionCardinality(b *runContainer16) uint64 { +func (rc *runContainer16) unionCardinality(b *runContainer16) uint { // rc is also known as 'a' here, but golint insisted we // call it rc for consistency with the rest of the methods. - answer := uint64(0) + answer := uint(0) - alim := int64(len(rc.iv)) - blim := int64(len(b.iv)) + alim := int(len(rc.iv)) + blim := int(len(b.iv)) - var na int64 // next from a - var nb int64 // next from b + var na int // next from a + var nb int // next from b // merged holds the current merge output, which might // get additional merges before being appended to m. @@ -523,18 +519,18 @@ func (rc *runContainer16) unionCardinality(b *runContainer16) uint64 { mergedUpdated := false if canMerge16(cura, merged) { merged = mergeInterval16s(cura, merged) - na = rc.indexOfIntervalAtOrAfter(int64(merged.last())+1, na+1) + na = rc.indexOfIntervalAtOrAfter(int(merged.last())+1, na+1) mergedUpdated = true } if canMerge16(curb, merged) { merged = mergeInterval16s(curb, merged) - nb = b.indexOfIntervalAtOrAfter(int64(merged.last())+1, nb+1) + nb = b.indexOfIntervalAtOrAfter(int(merged.last())+1, nb+1) mergedUpdated = true } if !mergedUpdated { // we know that merged is disjoint from cura and curb //m = append(m, merged) - answer += uint64(merged.last()) - uint64(merged.start) + 1 + answer += uint(merged.last()) - uint(merged.start) + 1 mergedUsed = false } continue @@ -543,19 +539,19 @@ func (rc *runContainer16) unionCardinality(b *runContainer16) uint64 { // !mergedUsed if !canMerge16(cura, curb) { if cura.start < curb.start { - answer += uint64(cura.last()) - uint64(cura.start) + 1 + answer += uint(cura.last()) - uint(cura.start) + 1 //m = append(m, cura) na++ } else { - answer += uint64(curb.last()) - uint64(curb.start) + 1 + answer += uint(curb.last()) - uint(curb.start) + 1 //m = append(m, curb) nb++ } } else { merged = mergeInterval16s(cura, curb) mergedUsed = true - na = rc.indexOfIntervalAtOrAfter(int64(merged.last())+1, na+1) - nb = b.indexOfIntervalAtOrAfter(int64(merged.last())+1, nb+1) + na = rc.indexOfIntervalAtOrAfter(int(merged.last())+1, na+1) + nb = b.indexOfIntervalAtOrAfter(int(merged.last())+1, nb+1) } } } @@ -574,7 +570,7 @@ func (rc *runContainer16) unionCardinality(b *runContainer16) uint64 { cura = rc.iv[na] if canMerge16(cura, merged) { merged = mergeInterval16s(cura, merged) - na = rc.indexOfIntervalAtOrAfter(int64(merged.last())+1, na+1) + na = rc.indexOfIntervalAtOrAfter(int(merged.last())+1, na+1) } else { break aAdds } @@ -588,7 +584,7 @@ func (rc *runContainer16) unionCardinality(b *runContainer16) uint64 { curb = b.iv[nb] if canMerge16(curb, merged) { merged = mergeInterval16s(curb, merged) - nb = b.indexOfIntervalAtOrAfter(int64(merged.last())+1, nb+1) + nb = b.indexOfIntervalAtOrAfter(int(merged.last())+1, nb+1) } else { break bAdds } @@ -597,19 +593,19 @@ func (rc *runContainer16) unionCardinality(b *runContainer16) uint64 { } //m = append(m, merged) - answer += uint64(merged.last()) - uint64(merged.start) + 1 + answer += uint(merged.last()) - uint(merged.start) + 1 } for _, r := range rc.iv[na:] { - answer += uint64(r.last()) - uint64(r.start) + 1 + answer += uint(r.last()) - uint(r.start) + 1 } for _, r := range b.iv[nb:] { - answer += uint64(r.last()) - uint64(r.start) + 1 + answer += uint(r.last()) - uint(r.start) + 1 } return answer } // indexOfIntervalAtOrAfter is a helper for union. -func (rc *runContainer16) indexOfIntervalAtOrAfter(key int64, startIndex int64) int64 { +func (rc *runContainer16) indexOfIntervalAtOrAfter(key int, startIndex int) int { w, already, _ := rc.searchRange(key, startIndex, 0) if already { return w @@ -622,8 +618,8 @@ func (rc *runContainer16) indexOfIntervalAtOrAfter(key int64, startIndex int64) func (rc *runContainer16) intersect(b *runContainer16) *runContainer16 { a := rc - numa := int64(len(a.iv)) - numb := int64(len(b.iv)) + numa := int(len(a.iv)) + numb := int(len(b.iv)) res := &runContainer16{} if numa == 0 || numb == 0 { return res @@ -637,21 +633,21 @@ func (rc *runContainer16) intersect(b *runContainer16) *runContainer16 { var output []interval16 - var acuri int64 - var bcuri int64 + var acuri int + var bcuri int - astart := int64(a.iv[acuri].start) - bstart := int64(b.iv[bcuri].start) + astart := int(a.iv[acuri].start) + bstart := int(b.iv[bcuri].start) var intersection interval16 - var leftoverstart int64 + var leftoverstart int var isOverlap, isLeftoverA, isLeftoverB bool var done bool toploop: for acuri < numa && bcuri < numb { isOverlap, isLeftoverA, isLeftoverB, leftoverstart, intersection = - intersectWithLeftover16(astart, int64(a.iv[acuri].last()), bstart, int64(b.iv[bcuri].last())) + intersectWithLeftover16(astart, int(a.iv[acuri].last()), bstart, int(b.iv[bcuri].last())) if !isOverlap { switch { @@ -660,17 +656,14 @@ toploop: if done { break toploop } - astart = int64(a.iv[acuri].start) + astart = int(a.iv[acuri].start) case astart > bstart: bcuri, done = b.findNextIntervalThatIntersectsStartingFrom(bcuri+1, astart) if done { break toploop } - bstart = int64(b.iv[bcuri].start) - - //default: - // panic("impossible that astart == bstart, since !isOverlap") + bstart = int(b.iv[bcuri].start) } } else { @@ -685,7 +678,7 @@ toploop: if bcuri >= numb { break toploop } - bstart = int64(b.iv[bcuri].start) + bstart = int(b.iv[bcuri].start) case isLeftoverB: // note that we change bstart without advancing bcuri, // since we need to capture any 2ndary intersections with b.iv[bcuri] @@ -694,27 +687,23 @@ toploop: if acuri >= numa { break toploop } - astart = int64(a.iv[acuri].start) + astart = int(a.iv[acuri].start) default: // neither had leftover, both completely consumed - // optionally, assert for sanity: - //if a.iv[acuri].endx != b.iv[bcuri].endx { - // panic("huh? should only be possible that endx agree now!") - //} // advance to next a interval acuri++ if acuri >= numa { break toploop } - astart = int64(a.iv[acuri].start) + astart = int(a.iv[acuri].start) // advance to next b interval bcuri++ if bcuri >= numb { break toploop } - bstart = int64(b.iv[bcuri].start) + bstart = int(b.iv[bcuri].start) } } } // end for toploop @@ -729,12 +718,12 @@ toploop: // intersectCardinality returns the cardinality of the // intersection of rc (also known as 'a') and b. -func (rc *runContainer16) intersectCardinality(b *runContainer16) int64 { - answer := int64(0) +func (rc *runContainer16) intersectCardinality(b *runContainer16) int { + answer := int(0) a := rc - numa := int64(len(a.iv)) - numb := int64(len(b.iv)) + numa := int(len(a.iv)) + numb := int(len(b.iv)) if numa == 0 || numb == 0 { return 0 } @@ -745,14 +734,14 @@ func (rc *runContainer16) intersectCardinality(b *runContainer16) int64 { } } - var acuri int64 - var bcuri int64 + var acuri int + var bcuri int - astart := int64(a.iv[acuri].start) - bstart := int64(b.iv[bcuri].start) + astart := int(a.iv[acuri].start) + bstart := int(b.iv[bcuri].start) var intersection interval16 - var leftoverstart int64 + var leftoverstart int var isOverlap, isLeftoverA, isLeftoverB bool var done bool pass := 0 @@ -761,7 +750,7 @@ toploop: pass++ isOverlap, isLeftoverA, isLeftoverB, leftoverstart, intersection = - intersectWithLeftover16(astart, int64(a.iv[acuri].last()), bstart, int64(b.iv[bcuri].last())) + intersectWithLeftover16(astart, int(a.iv[acuri].last()), bstart, int(b.iv[bcuri].last())) if !isOverlap { switch { @@ -770,22 +759,19 @@ toploop: if done { break toploop } - astart = int64(a.iv[acuri].start) + astart = int(a.iv[acuri].start) case astart > bstart: bcuri, done = b.findNextIntervalThatIntersectsStartingFrom(bcuri+1, astart) if done { break toploop } - bstart = int64(b.iv[bcuri].start) - - //default: - // panic("impossible that astart == bstart, since !isOverlap") + bstart = int(b.iv[bcuri].start) } } else { // isOverlap - answer += int64(intersection.last()) - int64(intersection.start) + 1 + answer += int(intersection.last()) - int(intersection.start) + 1 switch { case isLeftoverA: // note that we change astart without advancing acuri, @@ -795,7 +781,7 @@ toploop: if bcuri >= numb { break toploop } - bstart = int64(b.iv[bcuri].start) + bstart = int(b.iv[bcuri].start) case isLeftoverB: // note that we change bstart without advancing bcuri, // since we need to capture any 2ndary intersections with b.iv[bcuri] @@ -804,27 +790,23 @@ toploop: if acuri >= numa { break toploop } - astart = int64(a.iv[acuri].start) + astart = int(a.iv[acuri].start) default: // neither had leftover, both completely consumed - // optionally, assert for sanity: - //if a.iv[acuri].endx != b.iv[bcuri].endx { - // panic("huh? should only be possible that endx agree now!") - //} // advance to next a interval acuri++ if acuri >= numa { break toploop } - astart = int64(a.iv[acuri].start) + astart = int(a.iv[acuri].start) // advance to next b interval bcuri++ if bcuri >= numb { break toploop } - bstart = int64(b.iv[bcuri].start) + bstart = int(b.iv[bcuri].start) } } } // end for toploop @@ -834,7 +816,7 @@ toploop: // get returns true iff key is in the container. func (rc *runContainer16) contains(key uint16) bool { - _, in, _ := rc.search(int64(key)) + _, in, _ := rc.search(int(key)) return in } @@ -867,11 +849,11 @@ func (rc *runContainer16) numIntervals() int { // // runContainer16.search always returns whichInterval16 < len(rc.iv). // -// The search space is from startIndex to endxIndex. If endxIndex is set to zero, then there +// The search space is from startIndex to endxIndex. If endxIndex is set to zero, then there // no upper bound. // -func (rc *runContainer16) searchRange(key int64, startIndex int64, endxIndex int64) (whichInterval16 int64, alreadyPresent bool, numCompares int) { - n := int64(len(rc.iv)) +func (rc *runContainer16) searchRange(key int, startIndex int, endxIndex int) (whichInterval16 int, alreadyPresent bool, numCompares int) { + n := int(len(rc.iv)) if n == 0 { return -1, false, 0 } @@ -894,7 +876,7 @@ func (rc *runContainer16) searchRange(key int64, startIndex int64, endxIndex int h := i + (j-i)/2 // avoid overflow when computing h as the bisector // i <= h < j numCompares++ - if !(key < int64(rc.iv[h].start)) { + if !(key < int(rc.iv[h].start)) { i = h + 1 } else { j = h @@ -914,7 +896,7 @@ func (rc *runContainer16) searchRange(key int64, startIndex int64, endxIndex int if below == n { // all falses => key is >= start of all interval16s // ... so does it belong to the last interval16? - if key < int64(rc.iv[n-1].last())+1 { + if key < int(rc.iv[n-1].last())+1 { // yes, it belongs to the last interval16 alreadyPresent = true return @@ -935,7 +917,7 @@ func (rc *runContainer16) searchRange(key int64, startIndex int64, endxIndex int // key is < rc.iv[below].start // is key in below-1 interval16? - if key >= int64(rc.iv[below-1].start) && key < int64(rc.iv[below-1].last())+1 { + if key >= int(rc.iv[below-1].start) && key < int(rc.iv[below-1].last())+1 { // yes, it is. key is in below-1 interval16. alreadyPresent = true return @@ -970,32 +952,31 @@ func (rc *runContainer16) searchRange(key int64, startIndex int64, endxIndex int // // runContainer16.search always returns whichInterval16 < len(rc.iv). // -func (rc *runContainer16) search(key int64) (whichInterval16 int64, alreadyPresent bool, numCompares int) { +func (rc *runContainer16) search(key int) (whichInterval16 int, alreadyPresent bool, numCompares int) { return rc.searchRange(key, 0, 0) } -// cardinality returns the count of the integers stored in the -// runContainer16. -func (rc *runContainer16) cardinality() int64 { - if len(rc.iv) == 0 { - rc.card = 0 - return 0 - } - if rc.card > 0 { - return rc.card // already cached - } +// getCardinality returns the count of the integers stored in the +// runContainer16. The running complexity depends on the size +// of the container. +func (rc *runContainer16) getCardinality() int { // have to compute it - var n int64 + n := 0 for _, p := range rc.iv { n += p.runlen() } - rc.card = n // cache it return n } +// isEmpty returns true if the container is empty. +// It runs in constant time. +func (rc *runContainer16) isEmpty() bool { + return len(rc.iv) == 0 +} + // AsSlice decompresses the contents into a []uint16 slice. func (rc *runContainer16) AsSlice() []uint16 { - s := make([]uint16, rc.cardinality()) + s := make([]uint16, rc.getCardinality()) j := 0 for _, p := range rc.iv { for i := p.start; i <= p.last(); i++ { @@ -1065,7 +1046,7 @@ func (rc *runContainer16) Add(k uint16) (wasNew bool) { // but note that some unit tests use this method to build up test // runcontainers without calling runOptimize - k64 := int64(k) + k64 := int(k) index, present, _ := rc.search(k64) if present { @@ -1073,11 +1054,7 @@ func (rc *runContainer16) Add(k uint16) (wasNew bool) { } wasNew = true - // increment card if it is cached already - if rc.card > 0 { - rc.card++ - } - n := int64(len(rc.iv)) + n := int(len(rc.iv)) if index == -1 { // we may need to extend the first run if n > 0 { @@ -1094,7 +1071,7 @@ func (rc *runContainer16) Add(k uint16) (wasNew bool) { // are we off the end? handle both index == n and index == n-1: if index >= n-1 { - if int64(rc.iv[n-1].last())+1 == k64 { + if int(rc.iv[n-1].last())+1 == k64 { rc.iv[n-1].length++ return } @@ -1113,7 +1090,7 @@ func (rc *runContainer16) Add(k uint16) (wasNew bool) { right := index + 1 // are we fusing left and right by adding k? - if int64(rc.iv[left].last())+1 == k64 && int64(rc.iv[right].start) == k64+1 { + if int(rc.iv[left].last())+1 == k64 && int(rc.iv[right].start) == k64+1 { // fuse into left rc.iv[left].length = rc.iv[right].last() - rc.iv[left].start // remove redundant right @@ -1122,14 +1099,14 @@ func (rc *runContainer16) Add(k uint16) (wasNew bool) { } // are we an addition to left? - if int64(rc.iv[left].last())+1 == k64 { + if int(rc.iv[left].last())+1 == k64 { // yes rc.iv[left].length++ return } // are we an addition to right? - if int64(rc.iv[right].start) == k64+1 { + if int(rc.iv[right].start) == k64+1 { // yes rc.iv[right].start = k rc.iv[right].length++ @@ -1146,7 +1123,7 @@ func (rc *runContainer16) Add(k uint16) (wasNew bool) { // before calling next()/peekNext() to insure there are contents. type runIterator16 struct { rc *runContainer16 - curIndex int64 + curIndex int curPosInIndex uint16 } @@ -1171,8 +1148,8 @@ func (rc *runContainer16) iterate(cb func(x uint16) bool) bool { // returns true when there is at least one more value // available in the iteration sequence. func (ri *runIterator16) hasNext() bool { - return int64(len(ri.rc.iv)) > ri.curIndex+1 || - (int64(len(ri.rc.iv)) == ri.curIndex+1 && ri.rc.iv[ri.curIndex].length >= ri.curPosInIndex) + return int(len(ri.rc.iv)) > ri.curIndex+1 || + (int(len(ri.rc.iv)) == ri.curIndex+1 && ri.rc.iv[ri.curIndex].length >= ri.curPosInIndex) } // next returns the next value in the iteration sequence. @@ -1201,7 +1178,7 @@ func (ri *runIterator16) advanceIfNeeded(minval uint16) { } // interval cannot be -1 because of minval > peekNext - interval, isPresent, _ := ri.rc.searchRange(int64(minval), ri.curIndex, int64(len(ri.rc.iv))) + interval, isPresent, _ := ri.rc.searchRange(int(minval), ri.curIndex, int(len(ri.rc.iv))) // if the minval is present, set the curPosIndex at the right position if isPresent { @@ -1219,13 +1196,13 @@ func (ri *runIterator16) advanceIfNeeded(minval uint16) { // before calling next() to insure there are contents. type runReverseIterator16 struct { rc *runContainer16 - curIndex int64 // index into rc.iv + curIndex int // index into rc.iv curPosInIndex uint16 // offset in rc.iv[curIndex] } // newRunReverseIterator16 returns a new empty run iterator. func (rc *runContainer16) newRunReverseIterator16() *runReverseIterator16 { - index := int64(len(rc.iv)) - 1 + index := int(len(rc.iv)) - 1 pos := uint16(0) if index >= 0 { @@ -1298,7 +1275,7 @@ func (ri *runIterator16) nextMany(hs uint32, buf []uint32) int { ri.curPosInIndex = 0 ri.curIndex++ - if ri.curIndex == int64(len(ri.rc.iv)) { + if ri.curIndex == int(len(ri.rc.iv)) { break } } else { @@ -1339,7 +1316,7 @@ func (ri *runIterator16) nextMany64(hs uint64, buf []uint64) int { ri.curPosInIndex = 0 ri.curIndex++ - if ri.curIndex == int64(len(ri.rc.iv)) { + if ri.curIndex == int(len(ri.rc.iv)) { break } } else { @@ -1353,8 +1330,8 @@ func (ri *runIterator16) nextMany64(hs uint64, buf []uint64) int { // remove removes key from the container. func (rc *runContainer16) removeKey(key uint16) (wasPresent bool) { - var index int64 - index, wasPresent, _ = rc.search(int64(key)) + var index int + index, wasPresent, _ = rc.search(int(key)) if !wasPresent { return // already removed, nothing to do. } @@ -1365,15 +1342,14 @@ func (rc *runContainer16) removeKey(key uint16) (wasPresent bool) { // internal helper functions -func (rc *runContainer16) deleteAt(curIndex *int64, curPosInIndex *uint16) { - rc.card-- +func (rc *runContainer16) deleteAt(curIndex *int, curPosInIndex *uint16) { ci := *curIndex pos := *curPosInIndex // are we first, last, or in the middle of our interval16? switch { case pos == 0: - if int64(rc.iv[ci].length) == 0 { + if int(rc.iv[ci].length) == 0 { // our interval disappears rc.iv = append(rc.iv[:ci], rc.iv[ci+1:]...) // curIndex stays the same, since the delete did @@ -1394,8 +1370,8 @@ func (rc *runContainer16) deleteAt(curIndex *int64, curPosInIndex *uint16) { // split into two, adding an interval16 new0 := newInterval16Range(rc.iv[ci].start, rc.iv[ci].start+*curPosInIndex-1) - new1start := int64(rc.iv[ci].start+*curPosInIndex) + 1 - if new1start > int64(MaxUint16) { + new1start := int(rc.iv[ci].start+*curPosInIndex) + 1 + if new1start > int(MaxUint16) { panic("overflow?!?!") } new1 := newInterval16Range(uint16(new1start), rc.iv[ci].last()) @@ -1408,14 +1384,14 @@ func (rc *runContainer16) deleteAt(curIndex *int64, curPosInIndex *uint16) { } -func have4Overlap16(astart, alast, bstart, blast int64) bool { +func have4Overlap16(astart, alast, bstart, blast int) bool { if alast+1 <= bstart { return false } return blast+1 > astart } -func intersectWithLeftover16(astart, alast, bstart, blast int64) (isOverlap, isLeftoverA, isLeftoverB bool, leftoverstart int64, intersection interval16) { +func intersectWithLeftover16(astart, alast, bstart, blast int) (isOverlap, isLeftoverA, isLeftoverB bool, leftoverstart int, intersection interval16) { if !have4Overlap16(astart, alast, bstart, blast) { return } @@ -1445,13 +1421,13 @@ func intersectWithLeftover16(astart, alast, bstart, blast int64) (isOverlap, isL return } -func (rc *runContainer16) findNextIntervalThatIntersectsStartingFrom(startIndex int64, key int64) (index int64, done bool) { +func (rc *runContainer16) findNextIntervalThatIntersectsStartingFrom(startIndex int, key int) (index int, done bool) { w, _, _ := rc.searchRange(key, startIndex, 0) // rc.search always returns w < len(rc.iv) if w < startIndex { // not found and comes before lower bound startIndex, // so just use the lower bound. - if startIndex == int64(len(rc.iv)) { + if startIndex == int(len(rc.iv)) { // also this bump up means that we are done return startIndex, true } @@ -1469,25 +1445,6 @@ func sliceToString16(m []interval16) string { return s } -// selectInt16 returns the j-th value in the container. -// We panic of j is out of bounds. -func (rc *runContainer16) selectInt16(j uint16) int { - n := rc.cardinality() - if int64(j) > n { - panic(fmt.Sprintf("Cannot select %v since Cardinality is %v", j, n)) - } - - var offset int64 - for k := range rc.iv { - nextOffset := offset + rc.iv[k].runlen() - if nextOffset > int64(j) { - return int(int64(rc.iv[k].start) + (int64(j) - offset)) - } - offset = nextOffset - } - panic(fmt.Sprintf("Cannot select %v since Cardinality is %v", j, n)) -} - // helper for invert func (rc *runContainer16) invertlastInterval(origin uint16, lastIdx int) []interval16 { cur := rc.iv[lastIdx] @@ -1519,7 +1476,7 @@ func (rc *runContainer16) invert() *runContainer16 { case 1: return &runContainer16{iv: rc.invertlastInterval(0, 0)} } - var invstart int64 + var invstart int ult := ni - 1 for i, cur := range rc.iv { if i == ult { @@ -1538,7 +1495,7 @@ func (rc *runContainer16) invert() *runContainer16 { if cur.start > 0 { m = append(m, newInterval16Range(uint16(invstart), cur.start-1)) } - invstart = int64(cur.last() + 1) + invstart = int(cur.last() + 1) } return &runContainer16{iv: m} } @@ -1551,7 +1508,7 @@ func (iv interval16) isSuperSetOf(b interval16) bool { return iv.start <= b.start && b.last() <= iv.last() } -func (iv interval16) subtractInterval(del interval16) (left []interval16, delcount int64) { +func (iv interval16) subtractInterval(del interval16) (left []interval16, delcount int) { isect, isEmpty := intersectInterval16s(iv, del) if isEmpty { @@ -1576,7 +1533,7 @@ func (iv interval16) subtractInterval(del interval16) (left []interval16, delcou func (rc *runContainer16) isubtract(del interval16) { origiv := make([]interval16, len(rc.iv)) copy(origiv, rc.iv) - n := int64(len(rc.iv)) + n := int(len(rc.iv)) if n == 0 { return // already done. } @@ -1587,9 +1544,8 @@ func (rc *runContainer16) isubtract(del interval16) { } // INVAR there is some intersection between rc and del - istart, startAlready, _ := rc.search(int64(del.start)) - ilast, lastAlready, _ := rc.search(int64(del.last())) - rc.card = -1 + istart, startAlready, _ := rc.search(int(del.start)) + ilast, lastAlready, _ := rc.search(int(del.last())) if istart == -1 { if ilast == n-1 && !lastAlready { rc.iv = nil @@ -1604,8 +1560,8 @@ func (rc *runContainer16) isubtract(del interval16) { // would overwrite values in iv b/c res0 can have len 2. so // write to origiv instead. lost := 1 + ilast - istart - changeSize := int64(len(res0)) - lost - newSize := int64(len(rc.iv)) + changeSize + changeSize := int(len(res0)) - lost + newSize := int(len(rc.iv)) + changeSize // rc.iv = append(pre, caboose...) // return @@ -1613,19 +1569,19 @@ func (rc *runContainer16) isubtract(del interval16) { if ilast != istart { res1, _ := rc.iv[ilast].subtractInterval(del) res0 = append(res0, res1...) - changeSize = int64(len(res0)) - lost - newSize = int64(len(rc.iv)) + changeSize + changeSize = int(len(res0)) - lost + newSize = int(len(rc.iv)) + changeSize } switch { case changeSize < 0: // shrink - copy(rc.iv[istart+int64(len(res0)):], rc.iv[ilast+1:]) - copy(rc.iv[istart:istart+int64(len(res0))], res0) + copy(rc.iv[istart+int(len(res0)):], rc.iv[ilast+1:]) + copy(rc.iv[istart:istart+int(len(res0))], res0) rc.iv = rc.iv[:newSize] return case changeSize == 0: // stay the same - copy(rc.iv[istart:istart+int64(len(res0))], res0) + copy(rc.iv[istart:istart+int(len(res0))], res0) return default: // changeSize > 0 is only possible when ilast == istart. @@ -1682,7 +1638,7 @@ func (rc *runContainer16) isubtract(del interval16) { // INVAR: ilast < n-1 lost := ilast - istart changeSize := -lost - newSize := int64(len(rc.iv)) + changeSize + newSize := int(len(rc.iv)) + changeSize if changeSize != 0 { copy(rc.iv[ilast+1+changeSize:], rc.iv[ilast+1:]) } @@ -1699,8 +1655,8 @@ func (rc *runContainer16) isubtract(del interval16) { rc.iv[istart] = res0[0] } lost := 1 + (ilast - istart) - changeSize := int64(len(res0)) - lost - newSize := int64(len(rc.iv)) + changeSize + changeSize := int(len(res0)) - lost + newSize := int(len(rc.iv)) + changeSize if changeSize != 0 { copy(rc.iv[ilast+1+changeSize:], rc.iv[ilast+1:]) } @@ -1711,8 +1667,8 @@ func (rc *runContainer16) isubtract(del interval16) { // we can only shrink or stay the same size res1, _ := rc.iv[ilast].subtractInterval(del) lost := ilast - istart - changeSize := int64(len(res1)) - lost - newSize := int64(len(rc.iv)) + changeSize + changeSize := int(len(res1)) - lost + newSize := int(len(rc.iv)) + changeSize if changeSize != 0 { // move the tail first to make room for res1 copy(rc.iv[ilast+1+changeSize:], rc.iv[ilast+1:]) @@ -1916,8 +1872,6 @@ func (rc *runContainer16) iand(a container) container { } func (rc *runContainer16) inplaceIntersect(rc2 *runContainer16) container { - // TODO: optimize by doing less allocation, possibly? - // sect will be new sect := rc.intersect(rc2) *rc = *sect return rc @@ -1971,17 +1925,18 @@ func (rc *runContainer16) andNot(a container) container { panic("unsupported container type") } -func (rc *runContainer16) fillLeastSignificant16bits(x []uint32, i int, mask uint32) { - k := 0 - var val int64 +func (rc *runContainer16) fillLeastSignificant16bits(x []uint32, i int, mask uint32) int { + k := i + var val int for _, p := range rc.iv { n := p.runlen() - for j := int64(0); j < n; j++ { - val = int64(p.start) + j - x[k+i] = uint32(val) | mask + for j := int(0); j < n; j++ { + val = int(p.start) + j + x[k] = uint32(val) | mask k++ } } + return k } func (rc *runContainer16) getShortIterator() shortPeekable { @@ -2000,8 +1955,11 @@ func (rc *runContainer16) getManyIterator() manyIterable { // is still abe to express 2^16 because it is an int not an uint16. func (rc *runContainer16) iaddRange(firstOfRange, endx int) container { - if firstOfRange >= endx { - panic(fmt.Sprintf("invalid %v = endx >= firstOfRange", endx)) + if firstOfRange > endx { + panic(fmt.Sprintf("invalid %v = endx > firstOfRange", endx)) + } + if firstOfRange == endx { + return rc } addme := newRunContainer16TakeOwnership([]interval16{ { @@ -2015,10 +1973,13 @@ func (rc *runContainer16) iaddRange(firstOfRange, endx int) container { // remove the values in the range [firstOfRange,endx) func (rc *runContainer16) iremoveRange(firstOfRange, endx int) container { - if firstOfRange >= endx { + if firstOfRange > endx { panic(fmt.Sprintf("request to iremove empty set [%v, %v),"+ " nothing to do.", firstOfRange, endx)) - //return rc + } + // empty removal + if firstOfRange == endx { + return rc } x := newInterval16Range(uint16(firstOfRange), uint16(endx-1)) rc.isubtract(x) @@ -2027,8 +1988,8 @@ func (rc *runContainer16) iremoveRange(firstOfRange, endx int) container { // not flip the values in the range [firstOfRange,endx) func (rc *runContainer16) not(firstOfRange, endx int) container { - if firstOfRange >= endx { - panic(fmt.Sprintf("invalid %v = endx >= firstOfRange = %v", endx, firstOfRange)) + if firstOfRange > endx { + panic(fmt.Sprintf("invalid %v = endx > firstOfRange = %v", endx, firstOfRange)) } return rc.Not(firstOfRange, endx) @@ -2048,8 +2009,8 @@ func (rc *runContainer16) not(firstOfRange, endx int) container { // func (rc *runContainer16) Not(firstOfRange, endx int) *runContainer16 { - if firstOfRange >= endx { - panic(fmt.Sprintf("invalid %v = endx >= firstOfRange == %v", endx, firstOfRange)) + if firstOfRange > endx { + panic(fmt.Sprintf("invalid %v = endx > firstOfRange == %v", endx, firstOfRange)) } if firstOfRange >= endx { @@ -2187,9 +2148,21 @@ func (rc *runContainer16) orBitmapContainerCardinality(bc *bitmapContainer) int // orArray finds the union of rc and ac. func (rc *runContainer16) orArray(ac *arrayContainer) container { - bc1 := newBitmapContainerFromRun(rc) - bc2 := ac.toBitmapContainer() - return bc1.orBitmap(bc2) + if ac.isEmpty() { + return rc.clone() + } + if rc.isEmpty() { + return ac.clone() + } + intervals, cardMinusOne := runArrayUnionToRuns(rc, ac) + result := newRunContainer16TakeOwnership(intervals) + if len(intervals) >= 2048 && cardMinusOne >= arrayDefaultMaxSize { + return newBitmapContainerFromRun(result) + } + if len(intervals)*2 > 1+int(cardMinusOne) { + return result.toArrayContainer() + } + return result } // orArray finds the union of rc and ac. @@ -2214,8 +2187,8 @@ func (rc *runContainer16) ior(a container) container { func (rc *runContainer16) inplaceUnion(rc2 *runContainer16) container { for _, p := range rc2.iv { - last := int64(p.last()) - for i := int64(p.start); i <= last; i++ { + last := int(p.last()) + for i := int(p.start); i <= last; i++ { rc.Add(uint16(i)) } } @@ -2232,13 +2205,88 @@ func (rc *runContainer16) iorBitmapContainer(bc *bitmapContainer) container { } func (rc *runContainer16) iorArray(ac *arrayContainer) container { - it := ac.getShortIterator() - for it.hasNext() { - rc.Add(it.next()) + if rc.isEmpty() { + return ac.clone() + } + if ac.isEmpty() { + return rc + } + var cardMinusOne uint16 + //TODO: perform the union algorithm in-place using rc.iv + // this can be done with methods like the in-place array container union + // but maybe lazily moving the remaining elements back. + rc.iv, cardMinusOne = runArrayUnionToRuns(rc, ac) + if len(rc.iv) >= 2048 && cardMinusOne >= arrayDefaultMaxSize { + return newBitmapContainerFromRun(rc) + } + if len(rc.iv)*2 > 1+int(cardMinusOne) { + return rc.toArrayContainer() } return rc } +func runArrayUnionToRuns(rc *runContainer16, ac *arrayContainer) ([]interval16, uint16) { + pos1 := 0 + pos2 := 0 + length1 := len(ac.content) + length2 := len(rc.iv) + target := make([]interval16, 0, len(rc.iv)) + // have to find the first range + // options are + // 1. from array container + // 2. from run container + var previousInterval interval16 + var cardMinusOne uint16 + if ac.content[0] < rc.iv[0].start { + previousInterval.start = ac.content[0] + previousInterval.length = 0 + pos1++ + } else { + previousInterval.start = rc.iv[0].start + previousInterval.length = rc.iv[0].length + pos2++ + } + + for pos1 < length1 || pos2 < length2 { + if pos1 < length1 { + s1 := ac.content[pos1] + if s1 <= previousInterval.start+previousInterval.length { + pos1++ + continue + } + if previousInterval.last() < MaxUint16 && previousInterval.last()+1 == s1 { + previousInterval.length++ + pos1++ + continue + } + } + if pos2 < length2 { + range2 := rc.iv[pos2] + if range2.start <= previousInterval.last() || range2.start > 0 && range2.start-1 == previousInterval.last() { + pos2++ + if previousInterval.last() < range2.last() { + previousInterval.length = range2.last() - previousInterval.start + } + continue + } + } + cardMinusOne += previousInterval.length + 1 + target = append(target, previousInterval) + if pos2 == length2 || pos1 < length1 && ac.content[pos1] < rc.iv[pos2].start { + previousInterval.start = ac.content[pos1] + previousInterval.length = 0 + pos1++ + } else { + previousInterval = rc.iv[pos2] + pos2++ + } + } + cardMinusOne += previousInterval.length + 1 + target = append(target, previousInterval) + + return target, cardMinusOne +} + // lazyIOR is described (not yet implemented) in // this nice note from @lemire on // https://github.com/RoaringBitmap/roaring/pull/70#issuecomment-263613737 @@ -2294,9 +2342,9 @@ func (rc *runContainer16) lazyOR(a container) container { } func (rc *runContainer16) intersects(a container) bool { - // TODO: optimize by doing inplace/less allocation, possibly? + // TODO: optimize by doing inplace/less allocation isect := rc.and(a) - return isect.getCardinality() > 0 + return !isect.isEmpty() } func (rc *runContainer16) xor(a container) container { @@ -2325,21 +2373,20 @@ func (rc *runContainer16) iandNot(a container) container { // flip the values in the range [firstOfRange,endx) func (rc *runContainer16) inot(firstOfRange, endx int) container { - if firstOfRange >= endx { - panic(fmt.Sprintf("invalid %v = endx >= firstOfRange = %v", endx, firstOfRange)) + if firstOfRange > endx { + panic(fmt.Sprintf("invalid %v = endx > firstOfRange = %v", endx, firstOfRange)) + } + if firstOfRange > endx { + return rc } // TODO: minimize copies, do it all inplace; not() makes a copy. rc = rc.Not(firstOfRange, endx) return rc } -func (rc *runContainer16) getCardinality() int { - return int(rc.cardinality()) -} - func (rc *runContainer16) rank(x uint16) int { - n := int64(len(rc.iv)) - xx := int64(x) + n := int(len(rc.iv)) + xx := int(x) w, already, _ := rc.search(xx) if w < 0 { return 0 @@ -2347,22 +2394,30 @@ func (rc *runContainer16) rank(x uint16) int { if !already && w == n-1 { return rc.getCardinality() } - var rnk int64 + var rnk int if !already { - for i := int64(0); i <= w; i++ { + for i := int(0); i <= w; i++ { rnk += rc.iv[i].runlen() } return int(rnk) } - for i := int64(0); i < w; i++ { + for i := int(0); i < w; i++ { rnk += rc.iv[i].runlen() } - rnk += int64(x-rc.iv[w].start) + 1 + rnk += int(x-rc.iv[w].start) + 1 return int(rnk) } func (rc *runContainer16) selectInt(x uint16) int { - return rc.selectInt16(x) + var offset int + for k := range rc.iv { + nextOffset := offset + rc.iv[k].runlen() + if nextOffset > int(x) { + return int(int(rc.iv[k].start) + (int(x) - offset)) + } + offset = nextOffset + } + panic("cannot select x") } func (rc *runContainer16) andNotRunContainer16(b *runContainer16) container { @@ -2440,11 +2495,9 @@ func (rc *runContainer16) xorBitmap(bc *bitmapContainer) container { // convert to bitmap or array *if needed* func (rc *runContainer16) toEfficientContainer() container { - - // runContainer16SerializedSizeInBytes(numRuns) sizeAsRunContainer := rc.getSizeInBytes() sizeAsBitmapContainer := bitmapContainerSizeInBytes() - card := int(rc.cardinality()) + card := rc.getCardinality() sizeAsArrayContainer := arrayContainerSizeInBytes(card) if sizeAsRunContainer <= minOfInt(sizeAsBitmapContainer, sizeAsArrayContainer) { return rc diff --git a/vendor/github.com/RoaringBitmap/roaring/smat.go b/vendor/github.com/RoaringBitmap/roaring/smat.go index 9da4756349b65..972cd244da7e3 100644 --- a/vendor/github.com/RoaringBitmap/roaring/smat.go +++ b/vendor/github.com/RoaringBitmap/roaring/smat.go @@ -63,7 +63,7 @@ import ( "sort" "github.com/mschoch/smat" - "github.com/willf/bitset" + "github.com/bits-and-blooms/bitset" ) // fuzz test using state machine driven by byte stream. diff --git a/vendor/github.com/acomagu/bufpipe/README.md b/vendor/github.com/acomagu/bufpipe/README.md new file mode 100644 index 0000000000000..19df083142d63 --- /dev/null +++ b/vendor/github.com/acomagu/bufpipe/README.md @@ -0,0 +1,42 @@ +# bufpipe: Buffered Pipe + +[![CircleCI](https://img.shields.io/circleci/build/github/acomagu/bufpipe.svg?style=flat-square)](https://circleci.com/gh/acomagu/bufpipe) [![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/acomagu/bufpipe) + +The buffered version of io.Pipe. It's safe for concurrent use. + +## How does it differ from io.Pipe? + +Writes never block because the pipe has variable-sized buffer. + +```Go +r, w := bufpipe.New(nil) +io.WriteString(w, "abc") // No blocking. +io.WriteString(w, "def") // No blocking, too. +w.Close() +io.Copy(os.Stdout, r) +// Output: abcdef +``` + +[Playground](https://play.golang.org/p/PdyBAS3pVob) + +## How does it differ from bytes.Buffer? + +Reads block if the internal buffer is empty until the writer is closed. + +```Go +r, w := bufpipe.New(nil) + +done := make(chan struct{}) +go func() { + io.Copy(os.Stdout, r) // The reads block until the writer is closed. + done <- struct{}{} +}() + +io.WriteString(w, "abc") +io.WriteString(w, "def") +w.Close() +<-done +// Output: abcdef +``` + +[Playground](https://play.golang.org/p/UppmyLeRgX6) diff --git a/vendor/github.com/acomagu/bufpipe/bufpipe.go b/vendor/github.com/acomagu/bufpipe/bufpipe.go new file mode 100644 index 0000000000000..846dbcc290f26 --- /dev/null +++ b/vendor/github.com/acomagu/bufpipe/bufpipe.go @@ -0,0 +1,128 @@ +package bufpipe + +import ( + "bytes" + "errors" + "io" + "sync" +) + +// ErrClosedPipe is the error used for read or write operations on a closed pipe. +var ErrClosedPipe = errors.New("bufpipe: read/write on closed pipe") + +type pipe struct { + cond *sync.Cond + buf *bytes.Buffer + rerr, werr error +} + +// A PipeReader is the read half of a pipe. +type PipeReader struct { + *pipe +} + +// A PipeWriter is the write half of a pipe. +type PipeWriter struct { + *pipe +} + +// New creates a synchronous pipe using buf as its initial contents. It can be +// used to connect code expecting an io.Reader with code expecting an io.Writer. +// +// Unlike io.Pipe, writes never block because the internal buffer has variable +// size. Reads block only when the buffer is empty. +// +// It is safe to call Read and Write in parallel with each other or with Close. +// Parallel calls to Read and parallel calls to Write are also safe: the +// individual calls will be gated sequentially. +// +// The new pipe takes ownership of buf, and the caller should not use buf after +// this call. New is intended to prepare a PipeReader to read existing data. It +// can also be used to set the initial size of the internal buffer for writing. +// To do that, buf should have the desired capacity but a length of zero. +func New(buf []byte) (*PipeReader, *PipeWriter) { + p := &pipe{ + buf: bytes.NewBuffer(buf), + cond: sync.NewCond(new(sync.Mutex)), + } + return &PipeReader{ + pipe: p, + }, &PipeWriter{ + pipe: p, + } +} + +// Read implements the standard Read interface: it reads data from the pipe, +// reading from the internal buffer, otherwise blocking until a writer arrives +// or the write end is closed. If the write end is closed with an error, that +// error is returned as err; otherwise err is io.EOF. +func (r *PipeReader) Read(data []byte) (int, error) { + r.cond.L.Lock() + defer r.cond.L.Unlock() + +RETRY: + n, err := r.buf.Read(data) + // If not closed and no read, wait for writing. + if err == io.EOF && r.rerr == nil && n == 0 { + r.cond.Wait() + goto RETRY + } + if err == io.EOF { + return n, r.rerr + } + return n, err +} + +// Close closes the reader; subsequent writes from the write half of the pipe +// will return error ErrClosedPipe. +func (r *PipeReader) Close() error { + return r.CloseWithError(nil) +} + +// CloseWithError closes the reader; subsequent writes to the write half of the +// pipe will return the error err. +func (r *PipeReader) CloseWithError(err error) error { + r.cond.L.Lock() + defer r.cond.L.Unlock() + + if err == nil { + err = ErrClosedPipe + } + r.werr = err + return nil +} + +// Write implements the standard Write interface: it writes data to the internal +// buffer. If the read end is closed with an error, that err is returned as err; +// otherwise err is ErrClosedPipe. +func (w *PipeWriter) Write(data []byte) (int, error) { + w.cond.L.Lock() + defer w.cond.L.Unlock() + + if w.werr != nil { + return 0, w.werr + } + + n, err := w.buf.Write(data) + w.cond.Signal() + return n, err +} + +// Close closes the writer; subsequent reads from the read half of the pipe will +// return io.EOF once the internal buffer get empty. +func (w *PipeWriter) Close() error { + return w.CloseWithError(nil) +} + +// Close closes the writer; subsequent reads from the read half of the pipe will +// return err once the internal buffer get empty. +func (w *PipeWriter) CloseWithError(err error) error { + w.cond.L.Lock() + defer w.cond.L.Unlock() + + if err == nil { + err = io.EOF + } + w.rerr = err + return nil +} diff --git a/vendor/github.com/acomagu/bufpipe/doc.go b/vendor/github.com/acomagu/bufpipe/doc.go new file mode 100644 index 0000000000000..16a3948001701 --- /dev/null +++ b/vendor/github.com/acomagu/bufpipe/doc.go @@ -0,0 +1,2 @@ +// Package bufpipe provides a IO pipe, has variable-sized buffer. +package bufpipe diff --git a/vendor/github.com/acomagu/bufpipe/go.mod b/vendor/github.com/acomagu/bufpipe/go.mod new file mode 100644 index 0000000000000..bab097a2f5a7b --- /dev/null +++ b/vendor/github.com/acomagu/bufpipe/go.mod @@ -0,0 +1,5 @@ +module github.com/acomagu/bufpipe + +go 1.12 + +require github.com/matryer/is v1.2.0 diff --git a/vendor/github.com/acomagu/bufpipe/go.sum b/vendor/github.com/acomagu/bufpipe/go.sum new file mode 100644 index 0000000000000..6378c9bd6fcec --- /dev/null +++ b/vendor/github.com/acomagu/bufpipe/go.sum @@ -0,0 +1,2 @@ +github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= +github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= diff --git a/vendor/github.com/alecthomas/chroma/Makefile b/vendor/github.com/alecthomas/chroma/Makefile index 1b8320a58bccc..010ec0394f403 100644 --- a/vendor/github.com/alecthomas/chroma/Makefile +++ b/vendor/github.com/alecthomas/chroma/Makefile @@ -1,5 +1,7 @@ .PHONY: chromad upload all +VERSION ?= $(shell git describe --tags --dirty --always) + all: README.md tokentype_string.go README.md: lexers/*/*.go @@ -9,10 +11,8 @@ tokentype_string.go: types.go go generate chromad: - (cd ./cmd/chromad && go get github.com/GeertJohan/go.rice/rice@master && go install github.com/GeertJohan/go.rice/rice) rm -f chromad - (export CGOENABLED=0 GOOS=linux ; cd ./cmd/chromad && go build -o ../../chromad .) - rice append -i ./cmd/chromad --exec=./chromad + (export CGOENABLED=0 GOOS=linux ; cd ./cmd/chromad && go build -ldflags="-X 'main.version=$(VERSION)'" -o ../../chromad .) upload: chromad scp chromad root@swapoff.org: && \ diff --git a/vendor/github.com/alecthomas/chroma/README.md b/vendor/github.com/alecthomas/chroma/README.md index 42af02ea52a38..e47c88c874ade 100644 --- a/vendor/github.com/alecthomas/chroma/README.md +++ b/vendor/github.com/alecthomas/chroma/README.md @@ -1,4 +1,4 @@ -# Chroma — A general purpose syntax highlighter in pure Go [![Golang Documentation](https://godoc.org/github.com/alecthomas/chroma?status.svg)](https://godoc.org/github.com/alecthomas/chroma) [![CircleCI](https://img.shields.io/circleci/project/github/alecthomas/chroma.svg)](https://circleci.com/gh/alecthomas/chroma) [![Go Report Card](https://goreportcard.com/badge/github.com/alecthomas/chroma)](https://goreportcard.com/report/github.com/alecthomas/chroma) [![Slack chat](https://img.shields.io/static/v1?logo=slack&style=flat&label=slack&color=green&message=gophers)](https://gophers.slack.com/messages/CN9DS8YF3) +# Chroma — A general purpose syntax highlighter in pure Go [![Golang Documentation](https://godoc.org/github.com/alecthomas/chroma?status.svg)](https://godoc.org/github.com/alecthomas/chroma) [![CircleCI](https://img.shields.io/circleci/project/github/alecthomas/chroma.svg)](https://circleci.com/gh/alecthomas/chroma) [![Go Report Card](https://goreportcard.com/badge/github.com/alecthomas/chroma)](https://goreportcard.com/report/github.com/alecthomas/chroma) [![Slack chat](https://img.shields.io/static/v1?logo=slack&style=flat&label=slack&color=green&message=gophers)](https://invite.slack.golangbridge.org/) > **NOTE:** As Chroma has just been released, its API is still in flux. That said, the high-level interface should not change significantly. @@ -38,7 +38,7 @@ Prefix | Language A | ABAP, ABNF, ActionScript, ActionScript 3, Ada, Angular2, ANTLR, ApacheConf, APL, AppleScript, Arduino, Awk B | Ballerina, Base Makefile, Bash, Batchfile, BibTeX, BlitzBasic, BNF, Brainfuck C | C, C#, C++, Caddyfile, Caddyfile Directives, Cap'n Proto, Cassandra CQL, Ceylon, CFEngine3, cfstatement, ChaiScript, Cheetah, Clojure, CMake, COBOL, CoffeeScript, Common Lisp, Coq, Crystal, CSS, Cython -D | D, Dart, Diff, Django/Jinja, Docker, DTD +D | D, Dart, Diff, Django/Jinja, Docker, DTD, Dylan E | EBNF, Elixir, Elm, EmacsLisp, Erlang F | Factor, Fish, Forth, Fortran, FSharp G | GAS, GDScript, Genshi, Genshi HTML, Genshi Text, Gherkin, GLSL, Gnuplot, Go, Go HTML Template, Go Text Template, GraphQL, Groovy @@ -216,7 +216,7 @@ python3 ~/Projects/chroma/_tools/pygments2chroma.py \ && gofmt -s -w ~/Projects/chroma/lexers/*.go ``` -See notes in [pygments-lexers.go](https://github.com/alecthomas/chroma/blob/master/pygments-lexers.txt) +See notes in [pygments-lexers.txt](https://github.com/alecthomas/chroma/blob/master/pygments-lexers.txt) for a list of lexers, and notes on some of the issues importing them. diff --git a/vendor/github.com/alecthomas/chroma/go.mod b/vendor/github.com/alecthomas/chroma/go.mod index 10e6d6ead0f53..3baf1cb5089da 100644 --- a/vendor/github.com/alecthomas/chroma/go.mod +++ b/vendor/github.com/alecthomas/chroma/go.mod @@ -8,7 +8,7 @@ require ( github.com/alecthomas/kong v0.2.4 github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 // indirect github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 - github.com/dlclark/regexp2 v1.2.0 + github.com/dlclark/regexp2 v1.4.0 github.com/mattn/go-colorable v0.1.6 github.com/mattn/go-isatty v0.0.12 github.com/pkg/errors v0.9.1 // indirect diff --git a/vendor/github.com/alecthomas/chroma/go.sum b/vendor/github.com/alecthomas/chroma/go.sum index fb943fcafe3fd..273999b343ef9 100644 --- a/vendor/github.com/alecthomas/chroma/go.sum +++ b/vendor/github.com/alecthomas/chroma/go.sum @@ -13,6 +13,8 @@ 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/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk= github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= +github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= diff --git a/vendor/github.com/alecthomas/chroma/iterator.go b/vendor/github.com/alecthomas/chroma/iterator.go index c8845a1f5f0ac..d5175de89b612 100644 --- a/vendor/github.com/alecthomas/chroma/iterator.go +++ b/vendor/github.com/alecthomas/chroma/iterator.go @@ -4,7 +4,7 @@ import "strings" // An Iterator across tokens. // -// nil will be returned at the end of the Token stream. +// EOF will be returned at the end of the Token stream. // // If an error occurs within an Iterator, it may propagate this in a panic. Formatters should recover. type Iterator func() Token diff --git a/vendor/github.com/alecthomas/chroma/lexers/a/abap.go b/vendor/github.com/alecthomas/chroma/lexers/a/abap.go index 61c294e040517..268aa6a5927b2 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/a/abap.go +++ b/vendor/github.com/alecthomas/chroma/lexers/a/abap.go @@ -6,7 +6,7 @@ import ( ) // ABAP lexer. -var Abap = internal.Register(MustNewLexer( +var Abap = internal.Register(MustNewLazyLexer( &Config{ Name: "ABAP", Aliases: []string{"abap"}, @@ -14,7 +14,11 @@ var Abap = internal.Register(MustNewLexer( MimeTypes: []string{"text/x-abap"}, CaseInsensitive: true, }, - Rules{ + abapRules, +)) + +func abapRules() Rules { + return Rules{ "common": { {`\s+`, Text, nil}, {`^\*.*$`, CommentSingle, nil}, @@ -52,5 +56,5 @@ var Abap = internal.Register(MustNewLexer( {`[/;:()\[\],.]`, Punctuation, nil}, {`(!)(\w+)`, ByGroups(Operator, Name), nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/a/abnf.go b/vendor/github.com/alecthomas/chroma/lexers/a/abnf.go index ff29aed24e64e..85c47afce291f 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/a/abnf.go +++ b/vendor/github.com/alecthomas/chroma/lexers/a/abnf.go @@ -6,14 +6,18 @@ import ( ) // Abnf lexer. -var Abnf = internal.Register(MustNewLexer( +var Abnf = internal.Register(MustNewLazyLexer( &Config{ Name: "ABNF", Aliases: []string{"abnf"}, Filenames: []string{"*.abnf"}, MimeTypes: []string{"text/x-abnf"}, }, - Rules{ + abnfRules, +)) + +func abnfRules() Rules { + return Rules{ "root": { {`;.*$`, CommentSingle, nil}, {`(%[si])?"[^"]*"`, Literal, nil}, @@ -34,5 +38,5 @@ var Abnf = internal.Register(MustNewLexer( {`\s+`, Text, nil}, {`.`, Text, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/a/actionscript.go b/vendor/github.com/alecthomas/chroma/lexers/a/actionscript.go index 43d38521e714f..df55d6daca94c 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/a/actionscript.go +++ b/vendor/github.com/alecthomas/chroma/lexers/a/actionscript.go @@ -6,7 +6,7 @@ import ( ) // Actionscript lexer. -var Actionscript = internal.Register(MustNewLexer( +var Actionscript = internal.Register(MustNewLazyLexer( &Config{ Name: "ActionScript", Aliases: []string{"as", "actionscript"}, @@ -15,7 +15,11 @@ var Actionscript = internal.Register(MustNewLexer( NotMultiline: true, DotAll: true, }, - Rules{ + actionscriptRules, +)) + +func actionscriptRules() Rules { + return Rules{ "root": { {`\s+`, Text, nil}, {`//.*?\n`, CommentSingle, nil}, @@ -35,5 +39,5 @@ var Actionscript = internal.Register(MustNewLexer( {`"(\\\\|\\"|[^"])*"`, LiteralStringDouble, nil}, {`'(\\\\|\\'|[^'])*'`, LiteralStringSingle, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/a/actionscript3.go b/vendor/github.com/alecthomas/chroma/lexers/a/actionscript3.go index 3404bd5556f06..45596dcc17501 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/a/actionscript3.go +++ b/vendor/github.com/alecthomas/chroma/lexers/a/actionscript3.go @@ -6,7 +6,7 @@ import ( ) // Actionscript 3 lexer. -var Actionscript3 = internal.Register(MustNewLexer( +var Actionscript3 = internal.Register(MustNewLazyLexer( &Config{ Name: "ActionScript 3", Aliases: []string{"as3", "actionscript3"}, @@ -14,7 +14,11 @@ var Actionscript3 = internal.Register(MustNewLexer( MimeTypes: []string{"application/x-actionscript3", "text/x-actionscript3", "text/actionscript3"}, DotAll: true, }, - Rules{ + actionscript3Rules, +)) + +func actionscript3Rules() Rules { + return Rules{ "root": { {`\s+`, Text, nil}, {`(function\s+)([$a-zA-Z_]\w*)(\s*)(\()`, ByGroups(KeywordDeclaration, NameFunction, Text, Operator), Push("funcparams")}, @@ -52,5 +56,5 @@ var Actionscript3 = internal.Register(MustNewLexer( {`,`, Operator, Pop(1)}, Default(Pop(1)), }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/a/ada.go b/vendor/github.com/alecthomas/chroma/lexers/a/ada.go index d9b34e3ca7c6a..916727124bfb7 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/a/ada.go +++ b/vendor/github.com/alecthomas/chroma/lexers/a/ada.go @@ -6,7 +6,7 @@ import ( ) // Ada lexer. -var Ada = internal.Register(MustNewLexer( +var Ada = internal.Register(MustNewLazyLexer( &Config{ Name: "Ada", Aliases: []string{"ada", "ada95", "ada2005"}, @@ -14,7 +14,11 @@ var Ada = internal.Register(MustNewLexer( MimeTypes: []string{"text/x-ada"}, CaseInsensitive: true, }, - Rules{ + adaRules, +)) + +func adaRules() Rules { + return Rules{ "root": { {`[^\S\n]+`, Text, nil}, {`--.*?\n`, CommentSingle, nil}, @@ -110,5 +114,5 @@ var Ada = internal.Register(MustNewLexer( {`\)`, Punctuation, Pop(1)}, Include("root"), }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/a/al.go b/vendor/github.com/alecthomas/chroma/lexers/a/al.go new file mode 100644 index 0000000000000..d70164449a63d --- /dev/null +++ b/vendor/github.com/alecthomas/chroma/lexers/a/al.go @@ -0,0 +1,48 @@ +package a + +import ( + . "github.com/alecthomas/chroma" // nolint + "github.com/alecthomas/chroma/lexers/internal" +) + +// Al lexer. +var Al = internal.Register(MustNewLazyLexer( + &Config{ + Name: "AL", + Aliases: []string{"al"}, + Filenames: []string{"*.al", "*.dal"}, + MimeTypes: []string{"text/x-al"}, + DotAll: true, + CaseInsensitive: true, + }, + alRules, +)) + +// https://github.com/microsoft/AL/blob/master/grammar/alsyntax.tmlanguage +func alRules() Rules { + return Rules{ + "root": { + {`\s+`, TextWhitespace, nil}, + {`(?s)\/\*.*?\\*\*\/`, CommentMultiline, nil}, + {`(?s)//.*?\n`, CommentSingle, nil}, + {`\"([^\"])*\"`, Text, nil}, + {`'([^'])*'`, LiteralString, nil}, + {`\b(?i:(ARRAY|ASSERTERROR|BEGIN|BREAK|CASE|DO|DOWNTO|ELSE|END|EVENT|EXIT|FOR|FOREACH|FUNCTION|IF|IMPLEMENTS|IN|INDATASET|INTERFACE|INTERNAL|LOCAL|OF|PROCEDURE|PROGRAM|PROTECTED|REPEAT|RUNONCLIENT|SECURITYFILTERING|SUPPRESSDISPOSE|TEMPORARY|THEN|TO|TRIGGER|UNTIL|VAR|WHILE|WITH|WITHEVENTS))\b`, Keyword, nil}, + {`\b(?i:(AND|DIV|MOD|NOT|OR|XOR))\b`, OperatorWord, nil}, + {`\b(?i:(AVERAGE|CONST|COUNT|EXIST|FIELD|FILTER|LOOKUP|MAX|MIN|ORDER|SORTING|SUM|TABLEDATA|UPPERLIMIT|WHERE|ASCENDING|DESCENDING))\b`, Keyword, nil}, + // Added new objects types of BC 2021 wave 1 (REPORTEXTENSION|Entitlement|PermissionSet|PermissionSetExtension) + {`\b(?i:(CODEUNIT|PAGE|PAGEEXTENSION|PAGECUSTOMIZATION|DOTNET|ENUM|ENUMEXTENSION|VALUE|QUERY|REPORT|TABLE|TABLEEXTENSION|XMLPORT|PROFILE|CONTROLADDIN|REPORTEXTENSION|Entitlement|PermissionSet|PermissionSetExtension))\b`, Keyword, nil}, + {`\b(?i:(Action|Array|Automation|BigInteger|BigText|Blob|Boolean|Byte|Char|ClientType|Code|Codeunit|CompletionTriggerErrorLevel|ConnectionType|Database|DataClassification|DataScope|Date|DateFormula|DateTime|Decimal|DefaultLayout|Dialog|Dictionary|DotNet|DotNetAssembly|DotNetTypeDeclaration|Duration|Enum|ErrorInfo|ErrorType|ExecutionContext|ExecutionMode|FieldClass|FieldRef|FieldType|File|FilterPageBuilder|Guid|InStream|Integer|Joker|KeyRef|List|ModuleDependencyInfo|ModuleInfo|None|Notification|NotificationScope|ObjectType|Option|OutStream|Page|PageResult|Query|Record|RecordId|RecordRef|Report|ReportFormat|SecurityFilter|SecurityFiltering|Table|TableConnectionType|TableFilter|TestAction|TestField|TestFilterField|TestPage|TestPermissions|TestRequestPage|Text|TextBuilder|TextConst|TextEncoding|Time|TransactionModel|TransactionType|Variant|Verbosity|Version|XmlPort|HttpContent|HttpHeaders|HttpClient|HttpRequestMessage|HttpResponseMessage|JsonToken|JsonValue|JsonArray|JsonObject|View|Views|XmlAttribute|XmlAttributeCollection|XmlComment|XmlCData|XmlDeclaration|XmlDocument|XmlDocumentType|XmlElement|XmlNamespaceManager|XmlNameTable|XmlNode|XmlNodeList|XmlProcessingInstruction|XmlReadOptions|XmlText|XmlWriteOptions|WebServiceActionContext|WebServiceActionResultCode|SessionSettings))\b`, Keyword, nil}, + {`\b([<>]=|<>|<|>)\b?`, Operator, nil}, + {`\b(\-|\+|\/|\*)\b`, Operator, nil}, + {`\s*(\:=|\+=|-=|\/=|\*=)\s*?`, Operator, nil}, + {`\b(?i:(ADDFIRST|ADDLAST|ADDAFTER|ADDBEFORE|ACTION|ACTIONS|AREA|ASSEMBLY|CHARTPART|CUEGROUP|CUSTOMIZES|COLUMN|DATAITEM|DATASET|ELEMENTS|EXTENDS|FIELD|FIELDGROUP|FIELDATTRIBUTE|FIELDELEMENT|FIELDGROUPS|FIELDS|FILTER|FIXED|GRID|GROUP|MOVEAFTER|MOVEBEFORE|KEY|KEYS|LABEL|LABELS|LAYOUT|MODIFY|MOVEFIRST|MOVELAST|MOVEBEFORE|MOVEAFTER|PART|REPEATER|USERCONTROL|REQUESTPAGE|SCHEMA|SEPARATOR|SYSTEMPART|TABLEELEMENT|TEXTATTRIBUTE|TEXTELEMENT|TYPE))\b`, Keyword, nil}, + {`\s*[(\.\.)&\|]\s*`, Operator, nil}, + {`\b((0(x|X)[0-9a-fA-F]*)|(([0-9]+\.?[0-9]*)|(\.[0-9]+))((e|E)(\+|-)?[0-9]+)?)(L|l|UL|ul|u|U|F|f|ll|LL|ull|ULL)?\b`, LiteralNumber, nil}, + {`[;:,]`, Punctuation, nil}, + {`#[ \t]*(if|else|elif|endif|define|undef|region|endregion|pragma)\b.*?\n`, CommentPreproc, nil}, + {`\w+`, Text, nil}, + {`.`, Text, nil}, + }, + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/a/angular2.go b/vendor/github.com/alecthomas/chroma/lexers/a/angular2.go index 5258c928b8cc8..a947edad60a88 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/a/angular2.go +++ b/vendor/github.com/alecthomas/chroma/lexers/a/angular2.go @@ -6,14 +6,18 @@ import ( ) // Angular2 lexer. -var Angular2 = internal.Register(MustNewLexer( +var Angular2 = internal.Register(MustNewLazyLexer( &Config{ Name: "Angular2", Aliases: []string{"ng2"}, Filenames: []string{}, MimeTypes: []string{}, }, - Rules{ + angular2Rules, +)) + +func angular2Rules() Rules { + return Rules{ "root": { {`[^{([*#]+`, Other, nil}, {`(\{\{)(\s*)`, ByGroups(CommentPreproc, Text), Push("ngExpression")}, @@ -38,5 +42,5 @@ var Angular2 = internal.Register(MustNewLexer( {`'.*?'`, LiteralString, Pop(1)}, {`[^\s>]+`, LiteralString, Pop(1)}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/a/antlr.go b/vendor/github.com/alecthomas/chroma/lexers/a/antlr.go index d7649d4a4a9bb..c744353f1139b 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/a/antlr.go +++ b/vendor/github.com/alecthomas/chroma/lexers/a/antlr.go @@ -6,14 +6,18 @@ import ( ) // ANTLR lexer. -var ANTLR = internal.Register(MustNewLexer( +var ANTLR = internal.Register(MustNewLazyLexer( &Config{ Name: "ANTLR", Aliases: []string{"antlr"}, Filenames: []string{}, MimeTypes: []string{}, }, - Rules{ + antlrRules, +)) + +func antlrRules() Rules { + return Rules{ "whitespace": { {`\s+`, TextWhitespace, nil}, }, @@ -97,5 +101,5 @@ var ANTLR = internal.Register(MustNewLexer( {`(\$[a-zA-Z]+)(\.?)(text|value)?`, ByGroups(NameVariable, Punctuation, NameProperty), nil}, {`(\\\\|\\\]|\\\[|[^\[\]])+`, Other, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/a/apache.go b/vendor/github.com/alecthomas/chroma/lexers/a/apache.go index 6c56a1db87513..5685eb15ce063 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/a/apache.go +++ b/vendor/github.com/alecthomas/chroma/lexers/a/apache.go @@ -6,7 +6,7 @@ import ( ) // Apacheconf lexer. -var Apacheconf = internal.Register(MustNewLexer( +var Apacheconf = internal.Register(MustNewLazyLexer( &Config{ Name: "ApacheConf", Aliases: []string{"apacheconf", "aconf", "apache"}, @@ -14,7 +14,11 @@ var Apacheconf = internal.Register(MustNewLexer( MimeTypes: []string{"text/x-apacheconf"}, CaseInsensitive: true, }, - Rules{ + apacheconfRules, +)) + +func apacheconfRules() Rules { + return Rules{ "root": { {`\s+`, Text, nil}, {`(#.*?)$`, Comment, nil}, @@ -34,5 +38,5 @@ var Apacheconf = internal.Register(MustNewLexer( {`"([^"\\]*(?:\\.[^"\\]*)*)"`, LiteralStringDouble, nil}, {`[^\s"\\]+`, Text, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/a/apl.go b/vendor/github.com/alecthomas/chroma/lexers/a/apl.go index 820e13b6cb428..6cf1ea05dfa52 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/a/apl.go +++ b/vendor/github.com/alecthomas/chroma/lexers/a/apl.go @@ -6,14 +6,18 @@ import ( ) // Apl lexer. -var Apl = internal.Register(MustNewLexer( +var Apl = internal.Register(MustNewLazyLexer( &Config{ Name: "APL", Aliases: []string{"apl"}, Filenames: []string{"*.apl"}, MimeTypes: []string{}, }, - Rules{ + aplRules, +)) + +func aplRules() Rules { + return Rules{ "root": { {`\s+`, Text, nil}, {`[⍝#].*$`, CommentSingle, nil}, @@ -32,5 +36,5 @@ var Apl = internal.Register(MustNewLexer( {`[⍺⍵⍶⍹∇:]`, NameBuiltinPseudo, nil}, {`[{}]`, KeywordType, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/a/applescript.go b/vendor/github.com/alecthomas/chroma/lexers/a/applescript.go index db83ed0e01ed1..b6a53c5ff8246 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/a/applescript.go +++ b/vendor/github.com/alecthomas/chroma/lexers/a/applescript.go @@ -6,7 +6,7 @@ import ( ) // Applescript lexer. -var Applescript = internal.Register(MustNewLexer( +var Applescript = internal.Register(MustNewLazyLexer( &Config{ Name: "AppleScript", Aliases: []string{"applescript"}, @@ -14,7 +14,11 @@ var Applescript = internal.Register(MustNewLexer( MimeTypes: []string{}, DotAll: true, }, - Rules{ + applescriptRules, +)) + +func applescriptRules() Rules { + return Rules{ "root": { {`\s+`, Text, nil}, {`¬\n`, LiteralStringEscape, nil}, @@ -51,5 +55,5 @@ var Applescript = internal.Register(MustNewLexer( {`[^*(]+`, CommentMultiline, nil}, {`[*(]`, CommentMultiline, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/a/arduino.go b/vendor/github.com/alecthomas/chroma/lexers/a/arduino.go index b0cd8c95d46a2..0edbe3f9343cf 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/a/arduino.go +++ b/vendor/github.com/alecthomas/chroma/lexers/a/arduino.go @@ -6,7 +6,7 @@ import ( ) // Arduino lexer. -var Arduino = internal.Register(MustNewLexer( +var Arduino = internal.Register(MustNewLazyLexer( &Config{ Name: "Arduino", Aliases: []string{"arduino"}, @@ -14,7 +14,11 @@ var Arduino = internal.Register(MustNewLexer( MimeTypes: []string{"text/x-arduino"}, EnsureNL: true, }, - Rules{ + arduinoRules, +)) + +func arduinoRules() Rules { + return Rules{ "statements": { {Words(``, `\b`, `catch`, `const_cast`, `delete`, `dynamic_cast`, `explicit`, `export`, `friend`, `mutable`, `namespace`, `new`, `operator`, `private`, `protected`, `public`, `reinterpret_cast`, `restrict`, `static_cast`, `template`, `this`, `throw`, `throws`, `try`, `typeid`, `typename`, `using`, `virtual`, `constexpr`, `nullptr`, `decltype`, `thread_local`, `alignas`, `alignof`, `static_assert`, `noexcept`, `override`, `final`), Keyword, nil}, {`char(16_t|32_t)\b`, KeywordType, nil}, @@ -106,5 +110,5 @@ var Arduino = internal.Register(MustNewLexer( {`^\s*#endif.*?(?|\n\x1a]))(?:(?:[^\n\x1a^]|\^[\n\x1a]?[\w\W])*)`, CommentSingle, nil}, {`(?=((?:(?<=^[^:])|^[^:]?)[\t\v\f\r ,;=\xa0]*)(:))`, Text, Push("follow")}, @@ -190,5 +194,5 @@ var Batchfile = internal.Register(MustNewLexer( {`else(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a])`, Keyword, Pop(1)}, Default(Pop(1)), }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/b/bibtex.go b/vendor/github.com/alecthomas/chroma/lexers/b/bibtex.go index 1d76b1d800ddb..d6a0ae3347d58 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/b/bibtex.go +++ b/vendor/github.com/alecthomas/chroma/lexers/b/bibtex.go @@ -6,7 +6,7 @@ import ( ) // Bibtex lexer. -var Bibtex = internal.Register(MustNewLexer( +var Bibtex = internal.Register(MustNewLazyLexer( &Config{ Name: "BibTeX", Aliases: []string{"bib", "bibtex"}, @@ -15,7 +15,11 @@ var Bibtex = internal.Register(MustNewLexer( NotMultiline: true, CaseInsensitive: true, }, - Rules{ + bibtexRules, +)) + +func bibtexRules() Rules { + return Rules{ "root": { Include("whitespace"), {`@comment`, Comment, nil}, @@ -72,5 +76,5 @@ var Bibtex = internal.Register(MustNewLexer( "whitespace": { {`\s+`, Text, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/b/blitz.go b/vendor/github.com/alecthomas/chroma/lexers/b/blitz.go index 5d5ffc8525e2a..119f96b09845a 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/b/blitz.go +++ b/vendor/github.com/alecthomas/chroma/lexers/b/blitz.go @@ -6,7 +6,7 @@ import ( ) // Blitzbasic lexer. -var Blitzbasic = internal.Register(MustNewLexer( +var Blitzbasic = internal.Register(MustNewLazyLexer( &Config{ Name: "BlitzBasic", Aliases: []string{"blitzbasic", "b3d", "bplus"}, @@ -14,7 +14,11 @@ var Blitzbasic = internal.Register(MustNewLexer( MimeTypes: []string{"text/x-bb"}, CaseInsensitive: true, }, - Rules{ + blitzbasicRules, +)) + +func blitzbasicRules() Rules { + return Rules{ "root": { {`[ \t]+`, Text, nil}, {`;.*?\n`, CommentSingle, nil}, @@ -44,5 +48,5 @@ var Blitzbasic = internal.Register(MustNewLexer( {`"C?`, LiteralStringDouble, Pop(1)}, {`[^"]+`, LiteralStringDouble, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/b/bnf.go b/vendor/github.com/alecthomas/chroma/lexers/b/bnf.go index 5123a45aa0433..dcf7360c1d09e 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/b/bnf.go +++ b/vendor/github.com/alecthomas/chroma/lexers/b/bnf.go @@ -6,19 +6,23 @@ import ( ) // Bnf lexer. -var Bnf = internal.Register(MustNewLexer( +var Bnf = internal.Register(MustNewLazyLexer( &Config{ Name: "BNF", Aliases: []string{"bnf"}, Filenames: []string{"*.bnf"}, MimeTypes: []string{"text/x-bnf"}, }, - Rules{ + bnfRules, +)) + +func bnfRules() Rules { + return Rules{ "root": { {`(<)([ -;=?-~]+)(>)`, ByGroups(Punctuation, NameClass, Punctuation), nil}, {`::=`, Operator, nil}, {`[^<>:]+`, Text, nil}, {`.`, Text, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/b/brainfuck.go b/vendor/github.com/alecthomas/chroma/lexers/b/brainfuck.go index 6fac5f5e8cbac..d35e9c6db974a 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/b/brainfuck.go +++ b/vendor/github.com/alecthomas/chroma/lexers/b/brainfuck.go @@ -6,14 +6,18 @@ import ( ) // Brainfuck lexer. -var Brainfuck = internal.Register(MustNewLexer( +var Brainfuck = internal.Register(MustNewLazyLexer( &Config{ Name: "Brainfuck", Aliases: []string{"brainfuck", "bf"}, Filenames: []string{"*.bf", "*.b"}, MimeTypes: []string{"application/x-brainfuck"}, }, - Rules{ + brainfuckRules, +)) + +func brainfuckRules() Rules { + return Rules{ "common": { {`[.,]+`, NameTag, nil}, {`[+-]+`, NameBuiltin, nil}, @@ -30,5 +34,5 @@ var Brainfuck = internal.Register(MustNewLexer( {`\]`, Keyword, Pop(1)}, Include("common"), }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/c/c.go b/vendor/github.com/alecthomas/chroma/lexers/c/c.go index df2c0faac99ba..a81e7a88e0082 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/c/c.go +++ b/vendor/github.com/alecthomas/chroma/lexers/c/c.go @@ -6,14 +6,19 @@ import ( ) // C lexer. -var C = internal.Register(MustNewLexer( +var C = internal.Register(MustNewLazyLexer( &Config{ Name: "C", Aliases: []string{"c"}, Filenames: []string{"*.c", "*.h", "*.idc"}, MimeTypes: []string{"text/x-chdr", "text/x-csrc"}, + EnsureNL: true, }, - Rules{ + cRules, +)) + +func cRules() Rules { + return Rules{ "whitespace": { {`^#if\s+0`, CommentPreproc, Push("if0")}, {`^#`, CommentPreproc, Push("macro")}, @@ -87,5 +92,5 @@ var C = internal.Register(MustNewLexer( {`^\s*#endif.*?(?\]`, NameDecorator, Push("matcher")}, - // These cannot have matchers but may have things that look like - // matchers in their arguments, so we just parse as a subdirective. - {`try_files`, Keyword, Push("subdirective")}, - // These are special, they can nest more directives - {`handle_errors|handle|route|handle_path|not`, Keyword, Push("nested_directive")}, - // Any other directive - {`[^\s#]+`, Keyword, Push("directive")}, - Include("base"), - }, - "matcher": { - {`\{`, Punctuation, Push("block")}, - // Not can be one-liner - {`not`, Keyword, Push("deep_not_matcher")}, - // Any other same-line matcher - {`[^\s#]+`, Keyword, Push("arguments")}, - // Terminators - {`\n`, Text, Pop(1)}, - {`\}`, Punctuation, Pop(1)}, - Include("base"), - }, - "block": { - {`\}`, Punctuation, Pop(2)}, - // Not can be one-liner - {`not`, Keyword, Push("not_matcher")}, - // Any other subdirective - {`[^\s#]+`, Keyword, Push("subdirective")}, - Include("base"), - }, - "nested_block": { - {`\}`, Punctuation, Pop(2)}, - // Matcher definition - {`@[^\s]+(?=\s)`, NameDecorator, Push("matcher")}, - // Something that starts with literally < is probably a docs stub - {`\<[^#]+\>`, Keyword, Push("nested_directive")}, - // Any other directive - {`[^\s#]+`, Keyword, Push("nested_directive")}, - Include("base"), - }, - "not_matcher": { - {`\}`, Punctuation, Pop(2)}, - {`\{(?=\s)`, Punctuation, Push("block")}, - {`[^\s#]+`, Keyword, Push("arguments")}, - {`\s+`, Text, nil}, - }, - "deep_not_matcher": { - {`\}`, Punctuation, Pop(2)}, - {`\{(?=\s)`, Punctuation, Push("block")}, - {`[^\s#]+`, Keyword, Push("deep_subdirective")}, - {`\s+`, Text, nil}, - }, - "directive": { - {`\{(?=\s)`, Punctuation, Push("block")}, - Include("matcher_token"), - Include("comments_pop_1"), - {`\n`, Text, Pop(1)}, - Include("base"), - }, - "nested_directive": { - {`\{(?=\s)`, Punctuation, Push("nested_block")}, - Include("matcher_token"), - Include("comments_pop_1"), - {`\n`, Text, Pop(1)}, - Include("base"), - }, - "subdirective": { - {`\{(?=\s)`, Punctuation, Push("block")}, - Include("comments_pop_1"), - {`\n`, Text, Pop(1)}, - Include("base"), - }, - "arguments": { - {`\{(?=\s)`, Punctuation, Push("block")}, - Include("comments_pop_2"), - {`\\\n`, Text, nil}, // Skip escaped newlines - {`\n`, Text, Pop(2)}, - Include("base"), - }, - "deep_subdirective": { - {`\{(?=\s)`, Punctuation, Push("block")}, - Include("comments_pop_3"), - {`\n`, Text, Pop(3)}, - Include("base"), - }, - "matcher_token": { - {`@[^\s]+`, NameDecorator, Push("arguments")}, // Named matcher - {`/[^\s]+`, NameDecorator, Push("arguments")}, // Path matcher - {`\*`, NameDecorator, Push("arguments")}, // Wildcard path matcher - {`\[\\]`, NameDecorator, Push("arguments")}, // Matcher token stub for docs - }, - "comments": { - {`^#.*\n`, CommentSingle, nil}, // Comment at start of line - {`\s+#.*\n`, CommentSingle, nil}, // Comment preceded by whitespace - }, - "comments_pop_1": { - {`^#.*\n`, CommentSingle, Pop(1)}, // Comment at start of line - {`\s+#.*\n`, CommentSingle, Pop(1)}, // Comment preceded by whitespace - }, - "comments_pop_2": { - {`^#.*\n`, CommentSingle, Pop(2)}, // Comment at start of line - {`\s+#.*\n`, CommentSingle, Pop(2)}, // Comment preceded by whitespace - }, - "comments_pop_3": { - {`^#.*\n`, CommentSingle, Pop(3)}, // Comment at start of line - {`\s+#.*\n`, CommentSingle, Pop(3)}, // Comment preceded by whitespace - }, - "base": { - Include("comments"), - {`(on|off|first|last|before|after|internal|strip_prefix|strip_suffix|replace)\b`, NameConstant, nil}, - {`(https?://)?([a-z0-9.-]+)(:)([0-9]+)`, ByGroups(Name, Name, Punctuation, LiteralNumberInteger), nil}, - {`[a-z-]+/[a-z-+]+`, LiteralString, nil}, - {`[0-9]+[km]?\b`, LiteralNumberInteger, nil}, - {`\{[\w+.\$-]+\}`, LiteralStringEscape, nil}, // Placeholder - {`\[(?=[^#{}$]+\])`, Punctuation, nil}, - {`\]|\|`, Punctuation, nil}, - {`[^\s#{}$\]]+`, LiteralString, nil}, - {`/[^\s#]*`, Name, nil}, - {`\s+`, Text, nil}, - }, +func caddyfileCommonRules() Rules { + return Rules{ + "site_block_common": { + // Import keyword + {`(import)(\s+)([^\s]+)`, ByGroups(Keyword, Text, NameVariableMagic), nil}, + // Matcher definition + {`@[^\s]+(?=\s)`, NameDecorator, Push("matcher")}, + // Matcher token stub for docs + {`\[\\]`, NameDecorator, Push("matcher")}, + // These cannot have matchers but may have things that look like + // matchers in their arguments, so we just parse as a subdirective. + {`try_files`, Keyword, Push("subdirective")}, + // These are special, they can nest more directives + {`handle_errors|handle|route|handle_path|not`, Keyword, Push("nested_directive")}, + // Any other directive + {`[^\s#]+`, Keyword, Push("directive")}, + Include("base"), + }, + "matcher": { + {`\{`, Punctuation, Push("block")}, + // Not can be one-liner + {`not`, Keyword, Push("deep_not_matcher")}, + // Any other same-line matcher + {`[^\s#]+`, Keyword, Push("arguments")}, + // Terminators + {`\n`, Text, Pop(1)}, + {`\}`, Punctuation, Pop(1)}, + Include("base"), + }, + "block": { + {`\}`, Punctuation, Pop(2)}, + // Not can be one-liner + {`not`, Keyword, Push("not_matcher")}, + // Any other subdirective + {`[^\s#]+`, Keyword, Push("subdirective")}, + Include("base"), + }, + "nested_block": { + {`\}`, Punctuation, Pop(2)}, + // Matcher definition + {`@[^\s]+(?=\s)`, NameDecorator, Push("matcher")}, + // Something that starts with literally < is probably a docs stub + {`\<[^#]+\>`, Keyword, Push("nested_directive")}, + // Any other directive + {`[^\s#]+`, Keyword, Push("nested_directive")}, + Include("base"), + }, + "not_matcher": { + {`\}`, Punctuation, Pop(2)}, + {`\{(?=\s)`, Punctuation, Push("block")}, + {`[^\s#]+`, Keyword, Push("arguments")}, + {`\s+`, Text, nil}, + }, + "deep_not_matcher": { + {`\}`, Punctuation, Pop(2)}, + {`\{(?=\s)`, Punctuation, Push("block")}, + {`[^\s#]+`, Keyword, Push("deep_subdirective")}, + {`\s+`, Text, nil}, + }, + "directive": { + {`\{(?=\s)`, Punctuation, Push("block")}, + Include("matcher_token"), + Include("comments_pop_1"), + {`\n`, Text, Pop(1)}, + Include("base"), + }, + "nested_directive": { + {`\{(?=\s)`, Punctuation, Push("nested_block")}, + Include("matcher_token"), + Include("comments_pop_1"), + {`\n`, Text, Pop(1)}, + Include("base"), + }, + "subdirective": { + {`\{(?=\s)`, Punctuation, Push("block")}, + Include("comments_pop_1"), + {`\n`, Text, Pop(1)}, + Include("base"), + }, + "arguments": { + {`\{(?=\s)`, Punctuation, Push("block")}, + Include("comments_pop_2"), + {`\\\n`, Text, nil}, // Skip escaped newlines + {`\n`, Text, Pop(2)}, + Include("base"), + }, + "deep_subdirective": { + {`\{(?=\s)`, Punctuation, Push("block")}, + Include("comments_pop_3"), + {`\n`, Text, Pop(3)}, + Include("base"), + }, + "matcher_token": { + {`@[^\s]+`, NameDecorator, Push("arguments")}, // Named matcher + {`/[^\s]+`, NameDecorator, Push("arguments")}, // Path matcher + {`\*`, NameDecorator, Push("arguments")}, // Wildcard path matcher + {`\[\\]`, NameDecorator, Push("arguments")}, // Matcher token stub for docs + }, + "comments": { + {`^#.*\n`, CommentSingle, nil}, // Comment at start of line + {`\s+#.*\n`, CommentSingle, nil}, // Comment preceded by whitespace + }, + "comments_pop_1": { + {`^#.*\n`, CommentSingle, Pop(1)}, // Comment at start of line + {`\s+#.*\n`, CommentSingle, Pop(1)}, // Comment preceded by whitespace + }, + "comments_pop_2": { + {`^#.*\n`, CommentSingle, Pop(2)}, // Comment at start of line + {`\s+#.*\n`, CommentSingle, Pop(2)}, // Comment preceded by whitespace + }, + "comments_pop_3": { + {`^#.*\n`, CommentSingle, Pop(3)}, // Comment at start of line + {`\s+#.*\n`, CommentSingle, Pop(3)}, // Comment preceded by whitespace + }, + "base": { + Include("comments"), + {`(on|off|first|last|before|after|internal|strip_prefix|strip_suffix|replace)\b`, NameConstant, nil}, + {`(https?://)?([a-z0-9.-]+)(:)([0-9]+)`, ByGroups(Name, Name, Punctuation, LiteralNumberInteger), nil}, + {`[a-z-]+/[a-z-+]+`, LiteralString, nil}, + {`[0-9]+[km]?\b`, LiteralNumberInteger, nil}, + {`\{[\w+.\$-]+\}`, LiteralStringEscape, nil}, // Placeholder + {`\[(?=[^#{}$]+\])`, Punctuation, nil}, + {`\]|\|`, Punctuation, nil}, + {`[^\s#{}$\]]+`, LiteralString, nil}, + {`/[^\s#]*`, Name, nil}, + {`\s+`, Text, nil}, + }, + } } // Caddyfile lexer. -var Caddyfile = internal.Register(MustNewLexer( +var Caddyfile = internal.Register(MustNewLazyLexer( &Config{ Name: "Caddyfile", Aliases: []string{"caddyfile", "caddy"}, Filenames: []string{"Caddyfile*"}, MimeTypes: []string{}, }, - Rules{ + caddyfileRules, +)) + +func caddyfileRules() Rules { + return Rules{ "root": { Include("comments"), // Global options block @@ -186,21 +192,25 @@ var Caddyfile = internal.Register(MustNewLexer( {`\}`, Punctuation, Pop(2)}, Include("site_block_common"), }, - }.Merge(caddyfileCommon), -)) + }.Merge(caddyfileCommonRules()) +} // Caddyfile directive-only lexer. -var CaddyfileDirectives = internal.Register(MustNewLexer( +var CaddyfileDirectives = internal.Register(MustNewLazyLexer( &Config{ Name: "Caddyfile Directives", Aliases: []string{"caddyfile-directives", "caddyfile-d", "caddy-d"}, Filenames: []string{}, MimeTypes: []string{}, }, - Rules{ + caddyfileDirectivesRules, +)) + +func caddyfileDirectivesRules() Rules { + return Rules{ // Same as "site_block" in Caddyfile "root": { Include("site_block_common"), }, - }.Merge(caddyfileCommon), -)) + }.Merge(caddyfileCommonRules()) +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/c/capnproto.go b/vendor/github.com/alecthomas/chroma/lexers/c/capnproto.go index 0f9d03c6d3f4d..ec0d892c0fa52 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/c/capnproto.go +++ b/vendor/github.com/alecthomas/chroma/lexers/c/capnproto.go @@ -6,14 +6,18 @@ import ( ) // Cap'N'Proto Proto lexer. -var CapNProto = internal.Register(MustNewLexer( +var CapNProto = internal.Register(MustNewLazyLexer( &Config{ Name: "Cap'n Proto", Aliases: []string{"capnp"}, Filenames: []string{"*.capnp"}, MimeTypes: []string{}, }, - Rules{ + capNProtoRules, +)) + +func capNProtoRules() Rules { + return Rules{ "root": { {`#.*?$`, CommentSingle, nil}, {`@[0-9a-zA-Z]*`, NameDecorator, nil}, @@ -57,5 +61,5 @@ var CapNProto = internal.Register(MustNewLexer( {`[])]`, NameAttribute, Pop(1)}, Default(Pop(1)), }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/c/ceylon.go b/vendor/github.com/alecthomas/chroma/lexers/c/ceylon.go index 07324ca1fca76..9d424d4b28183 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/c/ceylon.go +++ b/vendor/github.com/alecthomas/chroma/lexers/c/ceylon.go @@ -6,7 +6,7 @@ import ( ) // Ceylon lexer. -var Ceylon = internal.Register(MustNewLexer( +var Ceylon = internal.Register(MustNewLazyLexer( &Config{ Name: "Ceylon", Aliases: []string{"ceylon"}, @@ -14,7 +14,11 @@ var Ceylon = internal.Register(MustNewLexer( MimeTypes: []string{"text/x-ceylon"}, DotAll: true, }, - Rules{ + ceylonRules, +)) + +func ceylonRules() Rules { + return Rules{ "root": { {`^(\s*(?:[a-zA-Z_][\w.\[\]]*\s+)+?)([a-zA-Z_]\w*)(\s*)(\()`, ByGroups(UsingSelf("root"), NameFunction, Text, Operator), nil}, {`[^\S\n]+`, Text, nil}, @@ -59,5 +63,5 @@ var Ceylon = internal.Register(MustNewLexer( {`\*/`, CommentMultiline, Pop(1)}, {`[*/]`, CommentMultiline, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/c/cfengine3.go b/vendor/github.com/alecthomas/chroma/lexers/c/cfengine3.go index f96252fa4687e..f3050346f9e0f 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/c/cfengine3.go +++ b/vendor/github.com/alecthomas/chroma/lexers/c/cfengine3.go @@ -6,14 +6,18 @@ import ( ) // Cfengine3 lexer. -var Cfengine3 = internal.Register(MustNewLexer( +var Cfengine3 = internal.Register(MustNewLazyLexer( &Config{ Name: "CFEngine3", Aliases: []string{"cfengine3", "cf3"}, Filenames: []string{"*.cf"}, MimeTypes: []string{}, }, - Rules{ + cfengine3Rules, +)) + +func cfengine3Rules() Rules { + return Rules{ "root": { {`#.*?\n`, Comment, nil}, {`(body)(\s+)(\S+)(\s+)(control)`, ByGroups(Keyword, Text, Keyword, Text, Keyword), nil}, @@ -52,5 +56,5 @@ var Cfengine3 = internal.Register(MustNewLexer( {`\w+`, NameVariable, nil}, {`\s+`, Text, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/c/chaiscript.go b/vendor/github.com/alecthomas/chroma/lexers/c/chaiscript.go index d2aa50db680fb..58db9aac9fd6b 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/c/chaiscript.go +++ b/vendor/github.com/alecthomas/chroma/lexers/c/chaiscript.go @@ -6,7 +6,7 @@ import ( ) // Chaiscript lexer. -var Chaiscript = internal.Register(MustNewLexer( +var Chaiscript = internal.Register(MustNewLazyLexer( &Config{ Name: "ChaiScript", Aliases: []string{"chai", "chaiscript"}, @@ -14,7 +14,11 @@ var Chaiscript = internal.Register(MustNewLexer( MimeTypes: []string{"text/x-chaiscript", "application/x-chaiscript"}, DotAll: true, }, - Rules{ + chaiscriptRules, +)) + +func chaiscriptRules() Rules { + return Rules{ "commentsandwhitespace": { {`\s+`, Text, nil}, {`//.*?\n`, CommentSingle, nil}, @@ -59,5 +63,5 @@ var Chaiscript = internal.Register(MustNewLexer( {`[^\\"$]+`, LiteralStringDouble, nil}, {`"`, LiteralStringDouble, Pop(1)}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/c/cheetah.go b/vendor/github.com/alecthomas/chroma/lexers/c/cheetah.go index b2cb9c406634a..bd5fb9d9cc75d 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/c/cheetah.go +++ b/vendor/github.com/alecthomas/chroma/lexers/c/cheetah.go @@ -7,14 +7,18 @@ import ( ) // Cheetah lexer. -var Cheetah = internal.Register(MustNewLexer( +var Cheetah = internal.Register(MustNewLazyLexer( &Config{ Name: "Cheetah", Aliases: []string{"cheetah", "spitfire"}, Filenames: []string{"*.tmpl", "*.spt"}, MimeTypes: []string{"application/x-cheetah", "application/x-spitfire"}, }, - Rules{ + cheetahRules, +)) + +func cheetahRules() Rules { + return Rules{ "root": { {`(##[^\n]*)$`, ByGroups(Comment), nil}, {`#[*](.|\n)*?[*]#`, Comment, nil}, @@ -33,5 +37,5 @@ var Cheetah = internal.Register(MustNewLexer( `, Other, nil}, {`\s+`, Text, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/c/cl.go b/vendor/github.com/alecthomas/chroma/lexers/c/cl.go index fdc97297641ce..bb162734ccaec 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/c/cl.go +++ b/vendor/github.com/alecthomas/chroma/lexers/c/cl.go @@ -230,7 +230,7 @@ var ( ) // Common Lisp lexer. -var CommonLisp = internal.Register(TypeRemappingLexer(MustNewLexer( +var CommonLisp = internal.Register(TypeRemappingLexer(MustNewLazyLexer( &Config{ Name: "Common Lisp", Aliases: []string{"common-lisp", "cl", "lisp"}, @@ -238,7 +238,19 @@ var CommonLisp = internal.Register(TypeRemappingLexer(MustNewLexer( MimeTypes: []string{"text/x-common-lisp"}, CaseInsensitive: true, }, - Rules{ + commonLispRules, +), TypeMapping{ + {NameVariable, NameFunction, clBuiltinFunctions}, + {NameVariable, Keyword, clSpecialForms}, + {NameVariable, NameBuiltin, clMacros}, + {NameVariable, Keyword, clLambdaListKeywords}, + {NameVariable, Keyword, clDeclarations}, + {NameVariable, KeywordType, clBuiltinTypes}, + {NameVariable, NameClass, clBuiltinClasses}, +})) + +func commonLispRules() Rules { + return Rules{ "root": { Default(Push("body")), }, @@ -294,13 +306,5 @@ var CommonLisp = internal.Register(TypeRemappingLexer(MustNewLexer( {`\(`, Punctuation, Push("body")}, {`\)`, Punctuation, Pop(1)}, }, - }, -), TypeMapping{ - {NameVariable, NameFunction, clBuiltinFunctions}, - {NameVariable, Keyword, clSpecialForms}, - {NameVariable, NameBuiltin, clMacros}, - {NameVariable, Keyword, clLambdaListKeywords}, - {NameVariable, Keyword, clDeclarations}, - {NameVariable, KeywordType, clBuiltinTypes}, - {NameVariable, NameClass, clBuiltinClasses}, -})) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/c/clojure.go b/vendor/github.com/alecthomas/chroma/lexers/c/clojure.go index e63752a5dd06f..f99f906c208ea 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/c/clojure.go +++ b/vendor/github.com/alecthomas/chroma/lexers/c/clojure.go @@ -6,14 +6,18 @@ import ( ) // Clojure lexer. -var Clojure = internal.Register(MustNewLexer( +var Clojure = internal.Register(MustNewLazyLexer( &Config{ Name: "Clojure", Aliases: []string{"clojure", "clj"}, Filenames: []string{"*.clj"}, MimeTypes: []string{"text/x-clojure", "application/x-clojure"}, }, - Rules{ + clojureRules, +)) + +func clojureRules() Rules { + return Rules{ "root": { {`;.*$`, CommentSingle, nil}, {`[,\s]+`, Text, nil}, @@ -34,5 +38,5 @@ var Clojure = internal.Register(MustNewLexer( {`(\{|\})`, Punctuation, nil}, {`(\(|\))`, Punctuation, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/c/cmake.go b/vendor/github.com/alecthomas/chroma/lexers/c/cmake.go index 163f17d9af5d2..0e0708db49f87 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/c/cmake.go +++ b/vendor/github.com/alecthomas/chroma/lexers/c/cmake.go @@ -6,14 +6,18 @@ import ( ) // Cmake lexer. -var Cmake = internal.Register(MustNewLexer( +var Cmake = internal.Register(MustNewLazyLexer( &Config{ Name: "CMake", Aliases: []string{"cmake"}, Filenames: []string{"*.cmake", "CMakeLists.txt"}, MimeTypes: []string{"text/x-cmake"}, }, - Rules{ + cmakeRules, +)) + +func cmakeRules() Rules { + return Rules{ "root": { {`\b(\w+)([ \t]*)(\()`, ByGroups(NameBuiltin, Text, Punctuation), Push("args")}, Include("keywords"), @@ -40,5 +44,5 @@ var Cmake = internal.Register(MustNewLexer( {`[ \t]+`, Text, nil}, {`#.*\n`, Comment, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/c/cobol.go b/vendor/github.com/alecthomas/chroma/lexers/c/cobol.go index e9ae0bb7fb4bb..8b2f6d9bc7810 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/c/cobol.go +++ b/vendor/github.com/alecthomas/chroma/lexers/c/cobol.go @@ -6,7 +6,7 @@ import ( ) // Cobol lexer. -var Cobol = internal.Register(MustNewLexer( +var Cobol = internal.Register(MustNewLazyLexer( &Config{ Name: "COBOL", Aliases: []string{"cobol"}, @@ -14,7 +14,11 @@ var Cobol = internal.Register(MustNewLexer( MimeTypes: []string{"text/x-cobol"}, CaseInsensitive: true, }, - Rules{ + cobolRules, +)) + +func cobolRules() Rules { + return Rules{ "root": { Include("comment"), Include("strings"), @@ -47,5 +51,5 @@ var Cobol = internal.Register(MustNewLexer( {`[+-]?\d*\.\d+(E[-+]?\d+)?`, LiteralNumberFloat, nil}, {`[+-]?\d+\.\d*(E[-+]?\d+)?`, LiteralNumberFloat, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/c/coffee.go b/vendor/github.com/alecthomas/chroma/lexers/c/coffee.go index e402b8f2afb8d..381a8fed86dfa 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/c/coffee.go +++ b/vendor/github.com/alecthomas/chroma/lexers/c/coffee.go @@ -6,7 +6,7 @@ import ( ) // Coffeescript lexer. -var Coffeescript = internal.Register(MustNewLexer( +var Coffeescript = internal.Register(MustNewLazyLexer( &Config{ Name: "CoffeeScript", Aliases: []string{"coffee-script", "coffeescript", "coffee"}, @@ -15,7 +15,11 @@ var Coffeescript = internal.Register(MustNewLexer( NotMultiline: true, DotAll: true, }, - Rules{ + coffeescriptRules, +)) + +func coffeescriptRules() Rules { + return Rules{ "commentsandwhitespace": { {`\s+`, Text, nil}, {`###[^#].*?###`, CommentMultiline, nil}, @@ -87,5 +91,5 @@ var Coffeescript = internal.Register(MustNewLexer( {`#|\\.|\'|"`, LiteralString, nil}, Include("strings"), }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/c/coldfusion.go b/vendor/github.com/alecthomas/chroma/lexers/c/coldfusion.go index 2f12472c08012..bc28bc3baf2e1 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/c/coldfusion.go +++ b/vendor/github.com/alecthomas/chroma/lexers/c/coldfusion.go @@ -6,7 +6,7 @@ import ( ) // Cfstatement lexer. -var Cfstatement = internal.Register(MustNewLexer( +var Cfstatement = internal.Register(MustNewLazyLexer( &Config{ Name: "cfstatement", Aliases: []string{"cfs"}, @@ -15,7 +15,11 @@ var Cfstatement = internal.Register(MustNewLexer( NotMultiline: true, CaseInsensitive: true, }, - Rules{ + cfstatementRules, +)) + +func cfstatementRules() Rules { + return Rules{ "root": { {`//.*?\n`, CommentSingle, nil}, {`/\*(?:.|\n)*?\*/`, CommentMultiline, nil}, @@ -44,5 +48,5 @@ var Cfstatement = internal.Register(MustNewLexer( {`#`, LiteralStringDouble, nil}, {`"`, LiteralStringDouble, Pop(1)}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/c/coq.go b/vendor/github.com/alecthomas/chroma/lexers/c/coq.go index e69a5c16ce9a6..e0103ef66bcb6 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/c/coq.go +++ b/vendor/github.com/alecthomas/chroma/lexers/c/coq.go @@ -6,14 +6,18 @@ import ( ) // Coq lexer. -var Coq = internal.Register(MustNewLexer( +var Coq = internal.Register(MustNewLazyLexer( &Config{ Name: "Coq", Aliases: []string{"coq"}, Filenames: []string{"*.v"}, MimeTypes: []string{"text/x-coq"}, }, - Rules{ + coqRules, +)) + +func coqRules() Rules { + return Rules{ "root": { {`\s+`, Text, nil}, {`false|true|\(\)|\[\]`, NameBuiltinPseudo, nil}, @@ -59,5 +63,5 @@ var Coq = internal.Register(MustNewLexer( {`[a-z][a-z0-9_\']*`, Name, Pop(1)}, Default(Pop(1)), }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/c/cpp.go b/vendor/github.com/alecthomas/chroma/lexers/c/cpp.go index 104be24090b85..3f4a1fde05cd6 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/c/cpp.go +++ b/vendor/github.com/alecthomas/chroma/lexers/c/cpp.go @@ -6,7 +6,7 @@ import ( ) // CPP lexer. -var CPP = internal.Register(MustNewLexer( +var CPP = internal.Register(MustNewLazyLexer( &Config{ Name: "C++", Aliases: []string{"cpp", "c++"}, @@ -14,7 +14,11 @@ var CPP = internal.Register(MustNewLexer( MimeTypes: []string{"text/x-c++hdr", "text/x-c++src"}, EnsureNL: true, }, - Rules{ + cppRules, +)) + +func cppRules() Rules { + return Rules{ "statements": { {Words(``, `\b`, `catch`, `const_cast`, `delete`, `dynamic_cast`, `explicit`, `export`, `friend`, `mutable`, `namespace`, `new`, `operator`, `private`, `protected`, `public`, `reinterpret_cast`, `restrict`, `static_cast`, `template`, `this`, `throw`, `throws`, `try`, `typeid`, `typename`, `using`, `virtual`, `constexpr`, `nullptr`, `decltype`, `thread_local`, `alignas`, `alignof`, `static_assert`, `noexcept`, `override`, `final`, `concept`, `requires`, `consteval`, `co_await`, `co_return`, `co_yield`), Keyword, nil}, {`(enum)\b(\s+)(class)\b(\s*)`, ByGroups(Keyword, Text, Keyword, Text), Push("classname")}, @@ -102,5 +106,5 @@ var CPP = internal.Register(MustNewLexer( {`^\s*#endif.*?(?=~!@#%^&|`?-]+", Operator, nil}, - {`(?s)(java|javascript)(\s+)(AS)(\s+)('|\$\$)(.*?)(\5)`, + { + `(?s)(java|javascript)(\s+)(AS)(\s+)('|\$\$)(.*?)(\5)`, UsingByGroup( internal.Get, 1, 6, @@ -65,5 +70,5 @@ var CassandraCQL = internal.Register(MustNewLexer( {`[^\$]+`, LiteralStringHeredoc, nil}, {`\$\$`, LiteralStringHeredoc, Pop(1)}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/c/crystal.go b/vendor/github.com/alecthomas/chroma/lexers/c/crystal.go index 69e053c737f19..f06830d2950a0 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/c/crystal.go +++ b/vendor/github.com/alecthomas/chroma/lexers/c/crystal.go @@ -6,7 +6,7 @@ import ( ) // Crystal lexer. -var Crystal = internal.Register(MustNewLexer( +var Crystal = internal.Register(MustNewLazyLexer( &Config{ Name: "Crystal", Aliases: []string{"cr", "crystal"}, @@ -14,7 +14,11 @@ var Crystal = internal.Register(MustNewLexer( MimeTypes: []string{"text/x-crystal"}, DotAll: true, }, - Rules{ + crystalRules, +)) + +func crystalRules() Rules { + return Rules{ "root": { {`#.*?$`, CommentSingle, nil}, {Words(``, `\b`, `abstract`, `asm`, `as`, `begin`, `break`, `case`, `do`, `else`, `elsif`, `end`, `ensure`, `extend`, `ifdef`, `if`, `include`, `instance_sizeof`, `next`, `of`, `pointerof`, `private`, `protected`, `rescue`, `return`, `require`, `sizeof`, `super`, `then`, `typeof`, `unless`, `until`, `when`, `while`, `with`, `yield`), Keyword, nil}, @@ -258,5 +262,5 @@ var Crystal = internal.Register(MustNewLexer( {`[\\#<>]`, LiteralStringRegex, nil}, {`[^\\#<>]+`, LiteralStringRegex, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/c/csharp.go b/vendor/github.com/alecthomas/chroma/lexers/c/csharp.go index c6a5f468ac12a..725bcb86104ad 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/c/csharp.go +++ b/vendor/github.com/alecthomas/chroma/lexers/c/csharp.go @@ -6,7 +6,7 @@ import ( ) // CSharp lexer. -var CSharp = internal.Register(MustNewLexer( +var CSharp = internal.Register(MustNewLazyLexer( &Config{ Name: "C#", Aliases: []string{"csharp", "c#"}, @@ -15,7 +15,11 @@ var CSharp = internal.Register(MustNewLexer( DotAll: true, EnsureNL: true, }, - Rules{ + cSharpRules, +)) + +func cSharpRules() Rules { + return Rules{ "root": { {`^\s*\[.*?\]`, NameAttribute, nil}, {`[^\S\n]+`, Text, nil}, @@ -29,7 +33,7 @@ var CSharp = internal.Register(MustNewLexer( {`\$@?"(""|[^"])*"`, LiteralString, nil}, {`"(\\\\|\\"|[^"\n])*["\n]`, LiteralString, nil}, {`'\\.'|'[^\\]'`, LiteralStringChar, nil}, - {`[0-9](\.[0-9]*)?([eE][+-][0-9]+)?[flFLdD]?|0[xX][0-9a-fA-F]+[Ll]?`, LiteralNumber, nil}, + {`0[xX][0-9a-fA-F]+[Ll]?|[0-9_](\.[0-9]*)?([eE][+-]?[0-9]+)?[flFLdD]?`, LiteralNumber, nil}, {`#[ \t]*(if|endif|else|elif|define|undef|line|error|warning|region|endregion|pragma)\b.*?\n`, CommentPreproc, nil}, {`\b(extern)(\s+)(alias)\b`, ByGroups(Keyword, Text, Keyword), nil}, {`(abstract|as|async|await|base|break|by|case|catch|checked|const|continue|default|delegate|do|else|enum|event|explicit|extern|false|finally|fixed|for|foreach|goto|if|implicit|in|interface|internal|is|let|lock|new|null|on|operator|out|override|params|private|protected|public|readonly|ref|return|sealed|sizeof|stackalloc|static|switch|this|throw|true|try|typeof|unchecked|unsafe|virtual|void|while|get|set|new|partial|yield|add|remove|value|alias|ascending|descending|from|group|into|orderby|select|thenby|where|join|equals)\b`, Keyword, nil}, @@ -47,5 +51,5 @@ var CSharp = internal.Register(MustNewLexer( {`(?=\()`, Text, Pop(1)}, {`(@?[_a-zA-Z]\w*|\.)+`, NameNamespace, Pop(1)}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/c/css.go b/vendor/github.com/alecthomas/chroma/lexers/c/css.go index fedc809bfc2cb..9f3a01d5455dd 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/c/css.go +++ b/vendor/github.com/alecthomas/chroma/lexers/c/css.go @@ -6,14 +6,18 @@ import ( ) // CSS lexer. -var CSS = internal.Register(MustNewLexer( +var CSS = internal.Register(MustNewLazyLexer( &Config{ Name: "CSS", Aliases: []string{"css"}, Filenames: []string{"*.css"}, MimeTypes: []string{"text/css"}, }, - Rules{ + cssRules, +)) + +func cssRules() Rules { + return Rules{ "root": { Include("basics"), }, @@ -39,6 +43,18 @@ var CSS = internal.Register(MustNewLexer( Include("basics"), {`\}`, Punctuation, Pop(2)}, }, + "atparenthesis": { + Include("common-values"), + {`/\*(?:.|\n)*?\*/`, Comment, nil}, + Include("numeric-values"), + {`[*+/-]`, Operator, nil}, + {`[,]`, Punctuation, nil}, + {`"(\\\\|\\"|[^"])*"`, LiteralStringDouble, nil}, + {`'(\\\\|\\'|[^'])*'`, LiteralStringSingle, nil}, + {`[a-zA-Z_-]\w*`, Name, nil}, + {`\(`, Punctuation, Push("atparenthesis")}, + {`\)`, Punctuation, Pop(1)}, + }, "content": { {`\s+`, Text, nil}, {`\}`, Punctuation, Pop(1)}, @@ -73,6 +89,7 @@ var CSS = internal.Register(MustNewLexer( {`"(\\\\|\\"|[^"])*"`, LiteralStringDouble, nil}, {`'(\\\\|\\'|[^'])*'`, LiteralStringSingle, nil}, {`[a-zA-Z_-]\w*`, Name, nil}, + {`\(`, Punctuation, Push("atparenthesis")}, {`\)`, Punctuation, Pop(1)}, }, "common-values": { @@ -100,5 +117,5 @@ var CSS = internal.Register(MustNewLexer( {`%`, KeywordType, nil}, Default(Pop(1)), }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/c/cython.go b/vendor/github.com/alecthomas/chroma/lexers/c/cython.go index 701e2b79121d1..0cce20410d4b2 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/c/cython.go +++ b/vendor/github.com/alecthomas/chroma/lexers/c/cython.go @@ -6,14 +6,18 @@ import ( ) // Cython lexer. -var Cython = internal.Register(MustNewLexer( +var Cython = internal.Register(MustNewLazyLexer( &Config{ Name: "Cython", Aliases: []string{"cython", "pyx", "pyrex"}, Filenames: []string{"*.pyx", "*.pxd", "*.pxi"}, MimeTypes: []string{"text/x-cython", "application/x-cython"}, }, - Rules{ + cythonRules, +)) + +func cythonRules() Rules { + return Rules{ "root": { {`\n`, Text, nil}, {`^(\s*)("""(?:.|\n)*?""")`, ByGroups(Text, LiteralStringDoc), nil}, @@ -131,5 +135,5 @@ var Cython = internal.Register(MustNewLexer( Include("strings"), Include("nl"), }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/circular/php.go b/vendor/github.com/alecthomas/chroma/lexers/circular/php.go index 2107cb7a6dc8e..517fb69077188 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/circular/php.go +++ b/vendor/github.com/alecthomas/chroma/lexers/circular/php.go @@ -6,7 +6,7 @@ import ( ) // PHP lexer for pure PHP code (not embedded in HTML). -var PHP = internal.Register(MustNewLexer( +var PHP = internal.Register(MustNewLazyLexer( &Config{ Name: "PHP", Aliases: []string{"php", "php3", "php4", "php5"}, @@ -16,65 +16,71 @@ var PHP = internal.Register(MustNewLexer( CaseInsensitive: true, EnsureNL: true, }, - phpCommonRules.Rename("php", "root"), + phpRules, )) -var phpCommonRules = Rules{ - "php": { - {`\?>`, CommentPreproc, Pop(1)}, - {`(<<<)([\'"]?)((?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*)(\2\n.*?\n\s*)(\3)(;?)(\n)`, ByGroups(LiteralString, LiteralString, LiteralStringDelimiter, LiteralString, LiteralStringDelimiter, Punctuation, Text), nil}, - {`\s+`, Text, nil}, - {`#.*?\n`, CommentSingle, nil}, - {`//.*?\n`, CommentSingle, nil}, - {`/\*\*/`, CommentMultiline, nil}, - {`/\*\*.*?\*/`, LiteralStringDoc, nil}, - {`/\*.*?\*/`, CommentMultiline, nil}, - {`(->|::)(\s*)((?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*)`, ByGroups(Operator, Text, NameAttribute), nil}, - {`[~!%^&*+=|:.<>/@-]+`, Operator, nil}, - {`\?`, Operator, nil}, - {`[\[\]{}();,]+`, Punctuation, nil}, - {`(class)(\s+)`, ByGroups(Keyword, Text), Push("classname")}, - {`(function)(\s*)(?=\()`, ByGroups(Keyword, Text), nil}, - {`(function)(\s+)(&?)(\s*)`, ByGroups(Keyword, Text, Operator, Text), Push("functionname")}, - {`(const)(\s+)((?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*)`, ByGroups(Keyword, Text, NameConstant), nil}, - {`(and|E_PARSE|old_function|E_ERROR|or|as|E_WARNING|parent|eval|PHP_OS|break|exit|case|extends|PHP_VERSION|cfunction|FALSE|print|for|require|continue|foreach|require_once|declare|return|default|static|do|switch|die|stdClass|echo|else|TRUE|elseif|var|empty|if|xor|enddeclare|include|virtual|endfor|include_once|while|endforeach|global|endif|list|endswitch|new|endwhile|not|array|E_ALL|NULL|final|php_user_filter|interface|implements|public|private|protected|abstract|clone|try|catch|throw|this|use|namespace|trait|yield|finally)\b`, Keyword, nil}, - {`(true|false|null)\b`, KeywordConstant, nil}, - Include("magicconstants"), - {`\$\{\$+(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*\}`, NameVariable, nil}, - {`\$+(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*`, NameVariable, nil}, - {`(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*`, NameOther, nil}, - {`(\d+\.\d*|\d*\.\d+)(e[+-]?[0-9]+)?`, LiteralNumberFloat, nil}, - {`\d+e[+-]?[0-9]+`, LiteralNumberFloat, nil}, - {`0[0-7]+`, LiteralNumberOct, nil}, - {`0x[a-f0-9]+`, LiteralNumberHex, nil}, - {`\d+`, LiteralNumberInteger, nil}, - {`0b[01]+`, LiteralNumberBin, nil}, - {`'([^'\\]*(?:\\.[^'\\]*)*)'`, LiteralStringSingle, nil}, - {"`([^`\\\\]*(?:\\\\.[^`\\\\]*)*)`", LiteralStringBacktick, nil}, - {`"`, LiteralStringDouble, Push("string")}, - }, - "magicfuncs": { - {Words(``, `\b`, `__construct`, `__destruct`, `__call`, `__callStatic`, `__get`, `__set`, `__isset`, `__unset`, `__sleep`, `__wakeup`, `__toString`, `__invoke`, `__set_state`, `__clone`, `__debugInfo`), NameFunctionMagic, nil}, - }, - "magicconstants": { - {Words(``, `\b`, `__LINE__`, `__FILE__`, `__DIR__`, `__FUNCTION__`, `__CLASS__`, `__TRAIT__`, `__METHOD__`, `__NAMESPACE__`), NameConstant, nil}, - }, - "classname": { - {`(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*`, NameClass, Pop(1)}, - }, - "functionname": { - Include("magicfuncs"), - {`(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*`, NameFunction, Pop(1)}, - Default(Pop(1)), - }, - "string": { - {`"`, LiteralStringDouble, Pop(1)}, - {`[^{$"\\]+`, LiteralStringDouble, nil}, - {`\\([nrt"$\\]|[0-7]{1,3}|x[0-9a-f]{1,2})`, LiteralStringEscape, nil}, - {`\$(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*(\[\S+?\]|->(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*)?`, LiteralStringInterpol, nil}, - {`(\{\$\{)(.*?)(\}\})`, ByGroups(LiteralStringInterpol, UsingSelf("root"), LiteralStringInterpol), nil}, - {`(\{)(\$.*?)(\})`, ByGroups(LiteralStringInterpol, UsingSelf("root"), LiteralStringInterpol), nil}, - {`(\$\{)(\S+)(\})`, ByGroups(LiteralStringInterpol, NameVariable, LiteralStringInterpol), nil}, - {`[${\\]`, LiteralStringDouble, nil}, - }, +func phpRules() Rules { + return phpCommonRules().Rename("php", "root") +} + +func phpCommonRules() Rules { + return Rules{ + "php": { + {`\?>`, CommentPreproc, Pop(1)}, + {`(<<<)([\'"]?)((?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*)(\2\n.*?\n\s*)(\3)(;?)(\n)`, ByGroups(LiteralString, LiteralString, LiteralStringDelimiter, LiteralString, LiteralStringDelimiter, Punctuation, Text), nil}, + {`\s+`, Text, nil}, + {`#.*?\n`, CommentSingle, nil}, + {`//.*?\n`, CommentSingle, nil}, + {`/\*\*/`, CommentMultiline, nil}, + {`/\*\*.*?\*/`, LiteralStringDoc, nil}, + {`/\*.*?\*/`, CommentMultiline, nil}, + {`(->|::)(\s*)((?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*)`, ByGroups(Operator, Text, NameAttribute), nil}, + {`[~!%^&*+=|:.<>/@-]+`, Operator, nil}, + {`\?`, Operator, nil}, + {`[\[\]{}();,]+`, Punctuation, nil}, + {`(class)(\s+)`, ByGroups(Keyword, Text), Push("classname")}, + {`(function)(\s*)(?=\()`, ByGroups(Keyword, Text), nil}, + {`(function)(\s+)(&?)(\s*)`, ByGroups(Keyword, Text, Operator, Text), Push("functionname")}, + {`(const)(\s+)((?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*)`, ByGroups(Keyword, Text, NameConstant), nil}, + {`(and|E_PARSE|old_function|E_ERROR|or|as|E_WARNING|parent|eval|PHP_OS|break|exit|case|extends|PHP_VERSION|cfunction|FALSE|print|for|require|continue|foreach|require_once|declare|return|default|static|do|switch|die|stdClass|echo|else|TRUE|elseif|var|empty|if|xor|enddeclare|include|virtual|endfor|include_once|while|endforeach|global|endif|list|endswitch|new|endwhile|not|array|E_ALL|NULL|final|php_user_filter|interface|implements|public|private|protected|abstract|clone|try|catch|throw|this|use|namespace|trait|yield|finally)\b`, Keyword, nil}, + {`(true|false|null)\b`, KeywordConstant, nil}, + Include("magicconstants"), + {`\$\{\$+(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*\}`, NameVariable, nil}, + {`\$+(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*`, NameVariable, nil}, + {`(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*`, NameOther, nil}, + {`(\d+\.\d*|\d*\.\d+)(e[+-]?[0-9]+)?`, LiteralNumberFloat, nil}, + {`\d+e[+-]?[0-9]+`, LiteralNumberFloat, nil}, + {`0[0-7]+`, LiteralNumberOct, nil}, + {`0x[a-f0-9_]+`, LiteralNumberHex, nil}, + {`[\d_]+`, LiteralNumberInteger, nil}, + {`0b[01]+`, LiteralNumberBin, nil}, + {`'([^'\\]*(?:\\.[^'\\]*)*)'`, LiteralStringSingle, nil}, + {"`([^`\\\\]*(?:\\\\.[^`\\\\]*)*)`", LiteralStringBacktick, nil}, + {`"`, LiteralStringDouble, Push("string")}, + }, + "magicfuncs": { + {Words(``, `\b`, `__construct`, `__destruct`, `__call`, `__callStatic`, `__get`, `__set`, `__isset`, `__unset`, `__sleep`, `__wakeup`, `__toString`, `__invoke`, `__set_state`, `__clone`, `__debugInfo`), NameFunctionMagic, nil}, + }, + "magicconstants": { + {Words(``, `\b`, `__LINE__`, `__FILE__`, `__DIR__`, `__FUNCTION__`, `__CLASS__`, `__TRAIT__`, `__METHOD__`, `__NAMESPACE__`), NameConstant, nil}, + }, + "classname": { + {`(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*`, NameClass, Pop(1)}, + }, + "functionname": { + Include("magicfuncs"), + {`(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*`, NameFunction, Pop(1)}, + Default(Pop(1)), + }, + "string": { + {`"`, LiteralStringDouble, Pop(1)}, + {`[^{$"\\]+`, LiteralStringDouble, nil}, + {`\\([nrt"$\\]|[0-7]{1,3}|x[0-9a-f]{1,2})`, LiteralStringEscape, nil}, + {`\$(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*(\[\S+?\]|->(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*)?`, LiteralStringInterpol, nil}, + {`(\{\$\{)(.*?)(\}\})`, ByGroups(LiteralStringInterpol, UsingSelf("root"), LiteralStringInterpol), nil}, + {`(\{)(\$.*?)(\})`, ByGroups(LiteralStringInterpol, UsingSelf("root"), LiteralStringInterpol), nil}, + {`(\$\{)(\S+)(\})`, ByGroups(LiteralStringInterpol, NameVariable, LiteralStringInterpol), nil}, + {`[${\\]`, LiteralStringDouble, nil}, + }, + } } diff --git a/vendor/github.com/alecthomas/chroma/lexers/circular/phtml.go b/vendor/github.com/alecthomas/chroma/lexers/circular/phtml.go index b9bffd3541195..f0824b5c74b32 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/circular/phtml.go +++ b/vendor/github.com/alecthomas/chroma/lexers/circular/phtml.go @@ -9,7 +9,7 @@ import ( ) // PHTML lexer is PHP in HTML. -var PHTML = internal.Register(DelegatingLexer(h.HTML, MustNewLexer( +var PHTML = internal.Register(DelegatingLexer(h.HTML, MustNewLazyLexer( &Config{ Name: "PHTML", Aliases: []string{"phtml"}, @@ -19,16 +19,20 @@ var PHTML = internal.Register(DelegatingLexer(h.HTML, MustNewLexer( CaseInsensitive: true, EnsureNL: true, }, - Rules{ - "root": { - {`<\?(php)?`, CommentPreproc, Push("php")}, - {`[^<]+`, Other, nil}, - {`<`, Other, nil}, - }, - }.Merge(phpCommonRules), + phtmlRules, ).SetAnalyser(func(text string) float32 { if strings.Contains(text, "\s|()?+*,]+`, NameAttribute, nil}, {`>`, Keyword, Pop(1)}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/d/dylan.go b/vendor/github.com/alecthomas/chroma/lexers/d/dylan.go new file mode 100644 index 0000000000000..0e51f453bc8a6 --- /dev/null +++ b/vendor/github.com/alecthomas/chroma/lexers/d/dylan.go @@ -0,0 +1,74 @@ +package d + +import ( + . "github.com/alecthomas/chroma" // nolint + "github.com/alecthomas/chroma/lexers/internal" +) + +// Dylan lexer. +var Dylan = internal.Register(MustNewLexer( + &Config{ + Name: "Dylan", + Aliases: []string{"dylan"}, + Filenames: []string{"*.dylan", "*.dyl", "*.intr"}, + MimeTypes: []string{"text/x-dylan"}, + CaseInsensitive: true, + }, + Rules{ + "root": { + {`\s+`, Whitespace, nil}, + {`//.*?\n`, CommentSingle, nil}, + {`([a-z0-9-]+:)([ \t]*)(.*(?:\n[ \t].+)*)`, ByGroups(NameAttribute, Whitespace, LiteralString), nil}, + Default(Push("code")), + }, + "code": { + {`\s+`, Whitespace, nil}, + {`//.*?\n`, CommentSingle, nil}, + {`/\*`, CommentMultiline, Push("comment")}, + {`"`, LiteralString, Push("string")}, + {`'(\\.|\\[0-7]{1,3}|\\x[a-f0-9]{1,2}|[^\\\'\n])'`, LiteralStringChar, nil}, + {`#b[01]+`, LiteralNumberBin, nil}, + {`#o[0-7]+`, LiteralNumberOct, nil}, + {`[-+]?(\d*\.\d+([ed][-+]?\d+)?|\d+(\.\d*)?e[-+]?\d+)`, LiteralNumberFloat, nil}, + {`[-+]?\d+`, LiteralNumberInteger, nil}, + {`#x[0-9a-f]+`, LiteralNumberHex, nil}, + + {`(\?\\?)([\w!&*<>|^$%@+~?/=-]+)(:)(token|name|variable|expression|body|case-body|\*)`, + ByGroups(Operator, NameVariable, Operator, NameBuiltin), nil}, + {`(\?)(:)(token|name|variable|expression|body|case-body|\*)`, + ByGroups(Operator, Operator, NameVariable), nil}, + {`(\?\\?)([\w!&*<>|^$%@+~?/=-]+)`, ByGroups(Operator, NameVariable), nil}, + + {`(=>|::|#\(|#\[|##|\?\?|\?=|\?|[(){}\[\],.;])`, Punctuation, nil}, + {`:=`, Operator, nil}, + {`#[tf]`, Literal, nil}, + {`#"`, LiteralStringSymbol, Push("symbol")}, + {`#[a-z0-9-]+`, Keyword, nil}, + {`#(all-keys|include|key|next|rest)`, Keyword, nil}, + {`[\w!&*<>|^$%@+~?/=-]+:`, KeywordConstant, nil}, + {`<[\w!&*<>|^$%@+~?/=-]+>`, NameClass, nil}, + {`\*[\w!&*<>|^$%@+~?/=-]+\*`, NameVariableGlobal, nil}, + {`\$[\w!&*<>|^$%@+~?/=-]+`, NameConstant, nil}, + {`(let|method|function)([ \t]+)([\w!&*<>|^$%@+~?/=-]+)`, ByGroups(NameBuiltin, Whitespace, NameVariable), nil}, + {`(error|signal|return|break)`, NameException, nil}, + {`(\\?)([\w!&*<>|^$%@+~?/=-]+)`, ByGroups(Operator, Name), nil}, + }, + "comment": { + {`[^*/]`, CommentMultiline, nil}, + {`/\*`, CommentMultiline, Push()}, + {`\*/`, CommentMultiline, Pop(1)}, + {`[*/]`, CommentMultiline, nil}, + }, + "symbol": { + {`"`, LiteralStringSymbol, Pop(1)}, + {`[^\\"]+`, LiteralStringSymbol, nil}, + }, + "string": { + {`"`, LiteralString, Pop(1)}, + {`\\([\\abfnrtv"\']|x[a-f0-9]{2,4}|[0-7]{1,3})`, LiteralStringEscape, nil}, + {`[^\\"\n]+`, LiteralString, nil}, + {`\\\n`, LiteralString, nil}, + {`\\`, LiteralString, nil}, + }, + }, +)) diff --git a/vendor/github.com/alecthomas/chroma/lexers/e/ebnf.go b/vendor/github.com/alecthomas/chroma/lexers/e/ebnf.go index 42a3a37933c68..5ccdd38ea3280 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/e/ebnf.go +++ b/vendor/github.com/alecthomas/chroma/lexers/e/ebnf.go @@ -6,14 +6,18 @@ import ( ) // Ebnf lexer. -var Ebnf = internal.Register(MustNewLexer( +var Ebnf = internal.Register(MustNewLazyLexer( &Config{ Name: "EBNF", Aliases: []string{"ebnf"}, Filenames: []string{"*.ebnf"}, MimeTypes: []string{"text/x-ebnf"}, }, - Rules{ + ebnfRules, +)) + +func ebnfRules() Rules { + return Rules{ "root": { Include("whitespace"), Include("comment_start"), @@ -47,5 +51,5 @@ var Ebnf = internal.Register(MustNewLexer( "identifier": { {`([a-zA-Z][\w \-]*)`, Keyword, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/e/elixir.go b/vendor/github.com/alecthomas/chroma/lexers/e/elixir.go index 11dc8443c7de1..f283f846bef0d 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/e/elixir.go +++ b/vendor/github.com/alecthomas/chroma/lexers/e/elixir.go @@ -6,14 +6,18 @@ import ( ) // Elixir lexer. -var Elixir = internal.Register(MustNewLexer( +var Elixir = internal.Register(MustNewLazyLexer( &Config{ Name: "Elixir", Aliases: []string{"elixir", "ex", "exs"}, Filenames: []string{"*.ex", "*.exs"}, MimeTypes: []string{"text/x-elixir"}, }, - Rules{ + elixirRules, +)) + +func elixirRules() Rules { + return Rules{ "root": { {`\s+`, Text, nil}, {`#.*$`, CommentSingle, nil}, @@ -273,5 +277,5 @@ var Elixir = internal.Register(MustNewLexer( {`\\.`, LiteralStringOther, nil}, {`'[a-zA-Z]*`, LiteralStringOther, Pop(1)}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/e/elm.go b/vendor/github.com/alecthomas/chroma/lexers/e/elm.go index a71c6270e893f..0fb6689537b66 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/e/elm.go +++ b/vendor/github.com/alecthomas/chroma/lexers/e/elm.go @@ -6,14 +6,18 @@ import ( ) // Elm lexer. -var Elm = internal.Register(MustNewLexer( +var Elm = internal.Register(MustNewLazyLexer( &Config{ Name: "Elm", Aliases: []string{"elm"}, Filenames: []string{"*.elm"}, MimeTypes: []string{"text/x-elm"}, }, - Rules{ + elmRules, +)) + +func elmRules() Rules { + return Rules{ "root": { {`\{-`, CommentMultiline, Push("comment")}, {`--.*`, CommentSingle, nil}, @@ -55,5 +59,5 @@ var Elm = internal.Register(MustNewLexer( {`\|\]`, NameEntity, Pop(1)}, {`.*\n`, NameEntity, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/e/emacs.go b/vendor/github.com/alecthomas/chroma/lexers/e/emacs.go index 78ffda1229650..51c49101dc406 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/e/emacs.go +++ b/vendor/github.com/alecthomas/chroma/lexers/e/emacs.go @@ -522,14 +522,24 @@ var ( ) // EmacsLisp lexer. -var EmacsLisp = internal.Register(TypeRemappingLexer(MustNewLexer( +var EmacsLisp = internal.Register(TypeRemappingLexer(MustNewLazyLexer( &Config{ Name: "EmacsLisp", Aliases: []string{"emacs", "elisp", "emacs-lisp"}, Filenames: []string{"*.el"}, MimeTypes: []string{"text/x-elisp", "application/x-elisp"}, }, - Rules{ + emacsLispRules, +), TypeMapping{ + {NameVariable, NameFunction, emacsBuiltinFunction}, + {NameVariable, NameBuiltin, emacsSpecialForms}, + {NameVariable, NameException, emacsErrorKeywords}, + {NameVariable, NameBuiltin, append(emacsBuiltinFunctionHighlighted, emacsMacros...)}, + {NameVariable, KeywordPseudo, emacsLambdaListKeywords}, +})) + +func emacsLispRules() Rules { + return Rules{ "root": { Default(Push("body")), }, @@ -572,11 +582,5 @@ var EmacsLisp = internal.Register(TypeRemappingLexer(MustNewLexer( {`\\\n`, LiteralString, nil}, {`"`, LiteralString, Pop(1)}, }, - }, -), TypeMapping{ - {NameVariable, NameFunction, emacsBuiltinFunction}, - {NameVariable, NameBuiltin, emacsSpecialForms}, - {NameVariable, NameException, emacsErrorKeywords}, - {NameVariable, NameBuiltin, append(emacsBuiltinFunctionHighlighted, emacsMacros...)}, - {NameVariable, KeywordPseudo, emacsLambdaListKeywords}, -})) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/e/erlang.go b/vendor/github.com/alecthomas/chroma/lexers/e/erlang.go index 63cd59a415745..5f5c9caadc702 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/e/erlang.go +++ b/vendor/github.com/alecthomas/chroma/lexers/e/erlang.go @@ -6,14 +6,18 @@ import ( ) // Erlang lexer. -var Erlang = internal.Register(MustNewLexer( +var Erlang = internal.Register(MustNewLazyLexer( &Config{ Name: "Erlang", Aliases: []string{"erlang"}, Filenames: []string{"*.erl", "*.hrl", "*.es", "*.escript"}, MimeTypes: []string{"text/x-erlang"}, }, - Rules{ + erlangRules, +)) + +func erlangRules() Rules { + return Rules{ "root": { {`\s+`, Text, nil}, {`%.*\n`, Comment, nil}, @@ -62,5 +66,5 @@ var Erlang = internal.Register(MustNewLexer( {`,`, Punctuation, Pop(1)}, {`(?=\})`, Punctuation, Pop(1)}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/f/factor.go b/vendor/github.com/alecthomas/chroma/lexers/f/factor.go index 26c0d5624c33d..d88beb231d177 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/f/factor.go +++ b/vendor/github.com/alecthomas/chroma/lexers/f/factor.go @@ -6,14 +6,18 @@ import ( ) // Factor lexer. -var Factor = internal.Register(MustNewLexer( +var Factor = internal.Register(MustNewLazyLexer( &Config{ Name: "Factor", Aliases: []string{"factor"}, Filenames: []string{"*.factor"}, MimeTypes: []string{"text/x-factor"}, }, - Rules{ + factorRules, +)) + +func factorRules() Rules { + return Rules{ "root": { {`#!.*$`, CommentPreproc, nil}, Default(Push("base")), @@ -111,5 +115,5 @@ var Factor = internal.Register(MustNewLexer( {`;\s`, Keyword, Pop(1)}, {`\S+`, NameFunction, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/f/fish.go b/vendor/github.com/alecthomas/chroma/lexers/f/fish.go index 185fc92b527b6..3678cfa2b9fa5 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/f/fish.go +++ b/vendor/github.com/alecthomas/chroma/lexers/f/fish.go @@ -6,14 +6,18 @@ import ( ) // Fish lexer. -var Fish = internal.Register(MustNewLexer( +var Fish = internal.Register(MustNewLazyLexer( &Config{ Name: "Fish", Aliases: []string{"fish", "fishshell"}, Filenames: []string{"*.fish", "*.load"}, MimeTypes: []string{"application/x-fish"}, }, - Rules{ + fishRules, +)) + +func fishRules() Rules { + return Rules{ "root": { Include("basic"), Include("data"), @@ -61,5 +65,5 @@ var Fish = internal.Register(MustNewLexer( {`\d+`, LiteralNumber, nil}, Include("root"), }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/f/forth.go b/vendor/github.com/alecthomas/chroma/lexers/f/forth.go index 47de6365f20fa..8d66708dccf45 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/f/forth.go +++ b/vendor/github.com/alecthomas/chroma/lexers/f/forth.go @@ -6,7 +6,7 @@ import ( ) // Forth lexer. -var Forth = internal.Register(MustNewLexer( +var Forth = internal.Register(MustNewLazyLexer( &Config{ Name: "Forth", Aliases: []string{"forth"}, @@ -14,7 +14,11 @@ var Forth = internal.Register(MustNewLexer( MimeTypes: []string{"application/x-forth"}, CaseInsensitive: true, }, - Rules{ + forthRules, +)) + +func forthRules() Rules { + return Rules{ "root": { {`\s+`, Text, nil}, {`\\.*?\n`, CommentSingle, nil}, @@ -36,5 +40,5 @@ var Forth = internal.Register(MustNewLexer( "stringdef": { {`[^"]+`, LiteralString, Pop(1)}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/f/fortran.go b/vendor/github.com/alecthomas/chroma/lexers/f/fortran.go index 6c57afa6e76d9..af4a969ceec82 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/f/fortran.go +++ b/vendor/github.com/alecthomas/chroma/lexers/f/fortran.go @@ -6,7 +6,7 @@ import ( ) // Fortran lexer. -var Fortran = internal.Register(MustNewLexer( +var Fortran = internal.Register(MustNewLazyLexer( &Config{ Name: "Fortran", Aliases: []string{"fortran"}, @@ -14,7 +14,11 @@ var Fortran = internal.Register(MustNewLexer( MimeTypes: []string{"text/x-fortran"}, CaseInsensitive: true, }, - Rules{ + fortranRules, +)) + +func fortranRules() Rules { + return Rules{ "root": { {`^#.*\n`, CommentPreproc, nil}, {`!.*\n`, Comment, nil}, @@ -43,5 +47,5 @@ var Fortran = internal.Register(MustNewLexer( {`[+-]?\d*\.\d+([ed][-+]?\d+)?(_[a-z]\w+)?`, LiteralNumberFloat, nil}, {`[+-]?\d+\.\d*([ed][-+]?\d+)?(_[a-z]\w+)?`, LiteralNumberFloat, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/f/fsharp.go b/vendor/github.com/alecthomas/chroma/lexers/f/fsharp.go index d00f63dd7df90..44fced4d5b372 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/f/fsharp.go +++ b/vendor/github.com/alecthomas/chroma/lexers/f/fsharp.go @@ -6,14 +6,18 @@ import ( ) // Fsharp lexer. -var Fsharp = internal.Register(MustNewLexer( +var Fsharp = internal.Register(MustNewLazyLexer( &Config{ Name: "FSharp", Aliases: []string{"fsharp"}, Filenames: []string{"*.fs", "*.fsi"}, MimeTypes: []string{"text/x-fsharp"}, }, - Rules{ + fsharpRules, +)) + +func fsharpRules() Rules { + return Rules{ "escape-sequence": { {`\\[\\"\'ntbrafv]`, LiteralStringEscape, nil}, {`\\[0-9]{3}`, LiteralStringEscape, nil}, @@ -90,5 +94,5 @@ var Fsharp = internal.Register(MustNewLexer( {`"""B?`, LiteralString, Pop(1)}, {`"`, LiteralString, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/g/gas.go b/vendor/github.com/alecthomas/chroma/lexers/g/gas.go index a922806857236..1f733f6ac17a7 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/g/gas.go +++ b/vendor/github.com/alecthomas/chroma/lexers/g/gas.go @@ -6,14 +6,18 @@ import ( ) // Gas lexer. -var Gas = internal.Register(MustNewLexer( +var Gas = internal.Register(MustNewLazyLexer( &Config{ Name: "GAS", Aliases: []string{"gas", "asm"}, Filenames: []string{"*.s", "*.S"}, MimeTypes: []string{"text/x-gas"}, }, - Rules{ + gasRules, +)) + +func gasRules() Rules { + return Rules{ "root": { Include("whitespace"), {`(?:[a-zA-Z$_][\w$.@-]*|\.[\w$.@-]+):`, NameLabel, nil}, @@ -51,5 +55,5 @@ var Gas = internal.Register(MustNewLexer( "punctuation": { {`[-*,.()\[\]!:]+`, Punctuation, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/g/gdscript.go b/vendor/github.com/alecthomas/chroma/lexers/g/gdscript.go index bfe30637ccb21..2b6af973fbee4 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/g/gdscript.go +++ b/vendor/github.com/alecthomas/chroma/lexers/g/gdscript.go @@ -6,14 +6,18 @@ import ( ) // GDScript lexer. -var GDScript = internal.Register(MustNewLexer( +var GDScript = internal.Register(MustNewLazyLexer( &Config{ Name: "GDScript", Aliases: []string{"gdscript", "gd"}, Filenames: []string{"*.gd"}, MimeTypes: []string{"text/x-gdscript", "application/x-gdscript"}, }, - Rules{ + gdscriptRules, +)) + +func gdscriptRules() Rules { + return Rules{ "root": { {`\n`, Text, nil}, {`^(\s*)([rRuUbB]{,2})("""(?:.|\n)*?""")`, ByGroups(Text, LiteralStringAffix, LiteralStringDoc), nil}, @@ -120,5 +124,5 @@ var GDScript = internal.Register(MustNewLexer( Include("strings-single"), {`\n`, LiteralStringSingle, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/g/genshi.go b/vendor/github.com/alecthomas/chroma/lexers/g/genshi.go index 0d3663a0a92a3..dc4d4b17ded75 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/g/genshi.go +++ b/vendor/github.com/alecthomas/chroma/lexers/g/genshi.go @@ -7,14 +7,18 @@ import ( ) // Genshi Text lexer. -var GenshiText = internal.Register(MustNewLexer( +var GenshiText = internal.Register(MustNewLazyLexer( &Config{ Name: "Genshi Text", Aliases: []string{"genshitext"}, Filenames: []string{}, MimeTypes: []string{"application/x-genshi-text", "text/x-genshi"}, }, - Rules{ + genshiTextRules, +)) + +func genshiTextRules() Rules { + return Rules{ "root": { {`[^#$\s]+`, Other, nil}, {`^(\s*)(##.*)$`, ByGroups(Text, Comment), nil}, @@ -33,11 +37,11 @@ var GenshiText = internal.Register(MustNewLexer( {`(?)`, ByGroups(CommentPreproc, Using(Python), CommentPreproc), nil}, - {`<\s*(script|style)\s*.*?>.*?<\s*/\1\s*>`, Other, nil}, - {`<\s*py:[a-zA-Z0-9]+`, NameTag, Push("pytag")}, - {`<\s*[a-zA-Z0-9:.]+`, NameTag, Push("tag")}, - Include("variable"), - {`[<$]`, Other, nil}, - }, - "pytag": { - {`\s+`, Text, nil}, - {`[\w:-]+\s*=`, NameAttribute, Push("pyattr")}, - {`/?\s*>`, NameTag, Pop(1)}, - }, - "pyattr": { - {`(")(.*?)(")`, ByGroups(LiteralString, Using(Python), LiteralString), Pop(1)}, - {`(')(.*?)(')`, ByGroups(LiteralString, Using(Python), LiteralString), Pop(1)}, - {`[^\s>]+`, LiteralString, Pop(1)}, - }, - "tag": { - {`\s+`, Text, nil}, - {`py:[\w-]+\s*=`, NameAttribute, Push("pyattr")}, - {`[\w:-]+\s*=`, NameAttribute, Push("attr")}, - {`/?\s*>`, NameTag, Pop(1)}, - }, - "attr": { - {`"`, LiteralString, Push("attr-dstring")}, - {`'`, LiteralString, Push("attr-sstring")}, - {`[^\s>]*`, LiteralString, Pop(1)}, - }, - "attr-dstring": { - {`"`, LiteralString, Pop(1)}, - Include("strings"), - {`'`, LiteralString, nil}, - }, - "attr-sstring": { - {`'`, LiteralString, Pop(1)}, - Include("strings"), - {`'`, LiteralString, nil}, - }, - "strings": { - {`[^"'$]+`, LiteralString, nil}, - Include("variable"), - }, - "variable": { - {`(?)`, ByGroups(CommentPreproc, Using(Python), CommentPreproc), nil}, + {`<\s*(script|style)\s*.*?>.*?<\s*/\1\s*>`, Other, nil}, + {`<\s*py:[a-zA-Z0-9]+`, NameTag, Push("pytag")}, + {`<\s*[a-zA-Z0-9:.]+`, NameTag, Push("tag")}, + Include("variable"), + {`[<$]`, Other, nil}, + }, + "pytag": { + {`\s+`, Text, nil}, + {`[\w:-]+\s*=`, NameAttribute, Push("pyattr")}, + {`/?\s*>`, NameTag, Pop(1)}, + }, + "pyattr": { + {`(")(.*?)(")`, ByGroups(LiteralString, Using(Python), LiteralString), Pop(1)}, + {`(')(.*?)(')`, ByGroups(LiteralString, Using(Python), LiteralString), Pop(1)}, + {`[^\s>]+`, LiteralString, Pop(1)}, + }, + "tag": { + {`\s+`, Text, nil}, + {`py:[\w-]+\s*=`, NameAttribute, Push("pyattr")}, + {`[\w:-]+\s*=`, NameAttribute, Push("attr")}, + {`/?\s*>`, NameTag, Pop(1)}, + }, + "attr": { + {`"`, LiteralString, Push("attr-dstring")}, + {`'`, LiteralString, Push("attr-sstring")}, + {`[^\s>]*`, LiteralString, Pop(1)}, + }, + "attr-dstring": { + {`"`, LiteralString, Pop(1)}, + Include("strings"), + {`'`, LiteralString, nil}, + }, + "attr-sstring": { + {`'`, LiteralString, Pop(1)}, + Include("strings"), + {`'`, LiteralString, nil}, + }, + "strings": { + {`[^"'$]+`, LiteralString, nil}, + Include("variable"), + }, + "variable": { + {`(?=!()\[\]{}.,;:]`, Punctuation, nil}, {`[^\W\d]\w*`, NameOther, nil}, }, - }, -).SetAnalyser(func(text string) float32 { - if strings.Contains(text, "fmt.") && strings.Contains(text, "package ") { - return 0.5 - } - if strings.Contains(text, "package ") { - return 0.1 } - return 0.0 -})) +} -var goTemplateRules = Rules{ - "root": { - {`{{(- )?/\*(.|\n)*?\*/( -)?}}`, CommentMultiline, nil}, - {`{{[-]?`, CommentPreproc, Push("template")}, - {`[^{]+`, Other, nil}, - {`{`, Other, nil}, - }, - "template": { - {`[-]?}}`, CommentPreproc, Pop(1)}, - {`(?=}})`, CommentPreproc, Pop(1)}, // Terminate the pipeline - {`\(`, Operator, Push("subexpression")}, - {`"(\\\\|\\"|[^"])*"`, LiteralString, nil}, - Include("expression"), - }, - "subexpression": { - {`\)`, Operator, Pop(1)}, - Include("expression"), - }, - "expression": { - {`\s+`, Whitespace, nil}, - {`\(`, Operator, Push("subexpression")}, - {`(range|if|else|while|with|template|end|true|false|nil|and|call|html|index|js|len|not|or|print|printf|println|urlquery|eq|ne|lt|le|gt|ge)\b`, Keyword, nil}, - {`\||:?=|,`, Operator, nil}, - {`[$]?[^\W\d]\w*`, NameOther, nil}, - {`[$]?\.(?:[^\W\d]\w*)?`, NameAttribute, nil}, - {`"(\\\\|\\"|[^"])*"`, LiteralString, nil}, - {`-?\d+i`, LiteralNumber, nil}, - {`-?\d+\.\d*([Ee][-+]\d+)?i`, LiteralNumber, nil}, - {`\.\d+([Ee][-+]\d+)?i`, LiteralNumber, nil}, - {`-?\d+[Ee][-+]\d+i`, LiteralNumber, nil}, - {`-?\d+(\.\d+[eE][+\-]?\d+|\.\d*|[eE][+\-]?\d+)`, LiteralNumberFloat, nil}, - {`-?\.\d+([eE][+\-]?\d+)?`, LiteralNumberFloat, nil}, - {`-?0[0-7]+`, LiteralNumberOct, nil}, - {`-?0[xX][0-9a-fA-F]+`, LiteralNumberHex, nil}, - {`-?(0|[1-9][0-9]*)`, LiteralNumberInteger, nil}, - {`'(\\['"\\abfnrtv]|\\x[0-9a-fA-F]{2}|\\[0-7]{1,3}|\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8}|[^\\])'`, LiteralStringChar, nil}, - {"`[^`]*`", LiteralString, nil}, - }, +func goTemplateRules() Rules { + return Rules{ + "root": { + {`{{(- )?/\*(.|\n)*?\*/( -)?}}`, CommentMultiline, nil}, + {`{{[-]?`, CommentPreproc, Push("template")}, + {`[^{]+`, Other, nil}, + {`{`, Other, nil}, + }, + "template": { + {`[-]?}}`, CommentPreproc, Pop(1)}, + {`(?=}})`, CommentPreproc, Pop(1)}, // Terminate the pipeline + {`\(`, Operator, Push("subexpression")}, + {`"(\\\\|\\"|[^"])*"`, LiteralString, nil}, + Include("expression"), + }, + "subexpression": { + {`\)`, Operator, Pop(1)}, + Include("expression"), + }, + "expression": { + {`\s+`, Whitespace, nil}, + {`\(`, Operator, Push("subexpression")}, + {`(range|if|else|while|with|template|end|true|false|nil|and|call|html|index|js|len|not|or|print|printf|println|urlquery|eq|ne|lt|le|gt|ge)\b`, Keyword, nil}, + {`\||:?=|,`, Operator, nil}, + {`[$]?[^\W\d]\w*`, NameOther, nil}, + {`\$|[$]?\.(?:[^\W\d]\w*)?`, NameAttribute, nil}, + {`"(\\\\|\\"|[^"])*"`, LiteralString, nil}, + {`-?\d+i`, LiteralNumber, nil}, + {`-?\d+\.\d*([Ee][-+]\d+)?i`, LiteralNumber, nil}, + {`\.\d+([Ee][-+]\d+)?i`, LiteralNumber, nil}, + {`-?\d+[Ee][-+]\d+i`, LiteralNumber, nil}, + {`-?\d+(\.\d+[eE][+\-]?\d+|\.\d*|[eE][+\-]?\d+)`, LiteralNumberFloat, nil}, + {`-?\.\d+([eE][+\-]?\d+)?`, LiteralNumberFloat, nil}, + {`-?0[0-7]+`, LiteralNumberOct, nil}, + {`-?0[xX][0-9a-fA-F]+`, LiteralNumberHex, nil}, + {`-?(0|[1-9][0-9]*)`, LiteralNumberInteger, nil}, + {`'(\\['"\\abfnrtv]|\\x[0-9a-fA-F]{2}|\\[0-7]{1,3}|\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8}|[^\\])'`, LiteralStringChar, nil}, + {"`[^`]*`", LiteralString, nil}, + }, + } } -var GoHTMLTemplate = internal.Register(DelegatingLexer(h.HTML, MustNewLexer( +var GoHTMLTemplate = internal.Register(DelegatingLexer(h.HTML, MustNewLazyLexer( &Config{ Name: "Go HTML Template", Aliases: []string{"go-html-template"}, @@ -106,7 +112,7 @@ var GoHTMLTemplate = internal.Register(DelegatingLexer(h.HTML, MustNewLexer( goTemplateRules, ))) -var GoTextTemplate = internal.Register(MustNewLexer( +var GoTextTemplate = internal.Register(MustNewLazyLexer( &Config{ Name: "Go Text Template", Aliases: []string{"go-text-template"}, diff --git a/vendor/github.com/alecthomas/chroma/lexers/g/graphql.go b/vendor/github.com/alecthomas/chroma/lexers/g/graphql.go index a57e693aba07d..7d465cd4c1794 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/g/graphql.go +++ b/vendor/github.com/alecthomas/chroma/lexers/g/graphql.go @@ -6,13 +6,17 @@ import ( ) // Go lexer. -var Graphql = internal.Register(MustNewLexer( +var Graphql = internal.Register(MustNewLazyLexer( &Config{ Name: "GraphQL", Aliases: []string{"graphql", "graphqls", "gql"}, Filenames: []string{"*.graphql", "*.graphqls"}, }, - Rules{ + graphqlRules, +)) + +func graphqlRules() Rules { + return Rules{ "root": { {`(query|mutation|subscription|fragment|scalar|implements|interface|union|enum|input|type)`, KeywordDeclaration, Push("type")}, {`(on|extend|schema|directive|\.\.\.)`, KeywordDeclaration, nil}, @@ -41,5 +45,5 @@ var Graphql = internal.Register(MustNewLexer( {`[^\W\d]\w*`, NameClass, Pop(1)}, Include("root"), }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/g/groovy.go b/vendor/github.com/alecthomas/chroma/lexers/g/groovy.go index a395415a9a956..8d37bd87ed777 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/g/groovy.go +++ b/vendor/github.com/alecthomas/chroma/lexers/g/groovy.go @@ -6,7 +6,7 @@ import ( ) // Groovy lexer. -var Groovy = internal.Register(MustNewLexer( +var Groovy = internal.Register(MustNewLazyLexer( &Config{ Name: "Groovy", Aliases: []string{"groovy"}, @@ -14,7 +14,11 @@ var Groovy = internal.Register(MustNewLexer( MimeTypes: []string{"text/x-groovy"}, DotAll: true, }, - Rules{ + groovyRules, +)) + +func groovyRules() Rules { + return Rules{ "root": { {`#!(.*?)$`, CommentPreproc, Push("base")}, Default(Push("base")), @@ -54,5 +58,5 @@ var Groovy = internal.Register(MustNewLexer( "import": { {`[\w.]+\*?`, NameNamespace, Pop(1)}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/h/handlebars.go b/vendor/github.com/alecthomas/chroma/lexers/h/handlebars.go index 07072da58231c..d34ef3a98fbe2 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/h/handlebars.go +++ b/vendor/github.com/alecthomas/chroma/lexers/h/handlebars.go @@ -6,14 +6,18 @@ import ( ) // Handlebars lexer. -var Handlebars = internal.Register(MustNewLexer( +var Handlebars = internal.Register(MustNewLazyLexer( &Config{ Name: "Handlebars", - Aliases: []string{"handlebars"}, - Filenames: []string{"*.handlebars"}, + Aliases: []string{"handlebars", "hbs"}, + Filenames: []string{"*.handlebars", "*.hbs"}, MimeTypes: []string{}, }, - Rules{ + handlebarsRules, +)) + +func handlebarsRules() Rules { + return Rules{ "root": { {`[^{]+`, Other, nil}, {`\{\{!.*\}\}`, Comment, nil}, @@ -52,5 +56,5 @@ var Handlebars = internal.Register(MustNewLexer( {`:?'(\\\\|\\'|[^'])*'`, LiteralStringSingle, nil}, {`[0-9](\.[0-9]*)?(eE[+-][0-9])?[flFLdD]?|0[xX][0-9a-fA-F]+[Ll]?`, LiteralNumber, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/h/haskell.go b/vendor/github.com/alecthomas/chroma/lexers/h/haskell.go index b018eab484f39..9780481aa2899 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/h/haskell.go +++ b/vendor/github.com/alecthomas/chroma/lexers/h/haskell.go @@ -6,14 +6,18 @@ import ( ) // Haskell lexer. -var Haskell = internal.Register(MustNewLexer( +var Haskell = internal.Register(MustNewLazyLexer( &Config{ Name: "Haskell", Aliases: []string{"haskell", "hs"}, Filenames: []string{"*.hs"}, MimeTypes: []string{"text/x-haskell"}, }, - Rules{ + haskellRules, +)) + +func haskellRules() Rules { + return Rules{ "root": { {`\s+`, Text, nil}, {`--(?![!#$%&*+./<=>?@^|_~:\\]).*?$`, CommentSingle, nil}, @@ -95,5 +99,5 @@ var Haskell = internal.Register(MustNewLexer( {`\d+`, LiteralStringEscape, Pop(1)}, {`\s+\\`, LiteralStringEscape, Pop(1)}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/h/haxe.go b/vendor/github.com/alecthomas/chroma/lexers/h/haxe.go index 59585431940f4..cc8c693efedcd 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/h/haxe.go +++ b/vendor/github.com/alecthomas/chroma/lexers/h/haxe.go @@ -6,7 +6,7 @@ import ( ) // Haxe lexer. -var Haxe = internal.Register(MustNewLexer( +var Haxe = internal.Register(MustNewLazyLexer( &Config{ Name: "Haxe", Aliases: []string{"hx", "haxe", "hxsl"}, @@ -14,7 +14,11 @@ var Haxe = internal.Register(MustNewLexer( MimeTypes: []string{"text/haxe", "text/x-haxe", "text/x-hx"}, DotAll: true, }, - Rules{ + haxeRules, +)) + +func haxeRules() Rules { + return Rules{ "root": { Include("spaces"), Include("meta"), @@ -609,8 +613,8 @@ var Haxe = internal.Register(MustNewLexer( {`\}`, Punctuation, Pop(1)}, {`,`, Punctuation, Push("#pop", "object")}, }, - }, -)) + } +} func haxePreProcMutator(state *LexerState) error { stack, ok := state.Get("haxe-pre-proc").([][]string) diff --git a/vendor/github.com/alecthomas/chroma/lexers/h/hcl.go b/vendor/github.com/alecthomas/chroma/lexers/h/hcl.go index ce7064b58d4d0..7206fba0868e5 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/h/hcl.go +++ b/vendor/github.com/alecthomas/chroma/lexers/h/hcl.go @@ -6,14 +6,18 @@ import ( ) // HCL lexer. -var HCL = internal.Register(MustNewLexer( +var HCL = internal.Register(MustNewLazyLexer( &Config{ Name: "HCL", Aliases: []string{"hcl"}, Filenames: []string{"*.hcl"}, MimeTypes: []string{"application/x-hcl"}, }, - Rules{ + hclRules, +)) + +func hclRules() Rules { + return Rules{ "root": { Include("string"), Include("punctuation"), @@ -65,5 +69,5 @@ var HCL = internal.Register(MustNewLexer( {`\s+`, Text, nil}, {`\\\n`, Text, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/h/hexdump.go b/vendor/github.com/alecthomas/chroma/lexers/h/hexdump.go index 8b7e7bdda6409..089353749622b 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/h/hexdump.go +++ b/vendor/github.com/alecthomas/chroma/lexers/h/hexdump.go @@ -6,14 +6,18 @@ import ( ) // Hexdump lexer. -var Hexdump = internal.Register(MustNewLexer( +var Hexdump = internal.Register(MustNewLazyLexer( &Config{ Name: "Hexdump", Aliases: []string{"hexdump"}, Filenames: []string{}, MimeTypes: []string{}, }, - Rules{ + hexdumpRules, +)) + +func hexdumpRules() Rules { + return Rules{ "root": { {`\n`, Text, nil}, Include("offset"), @@ -63,5 +67,5 @@ var Hexdump = internal.Register(MustNewLexer( {`\s`, Text, nil}, {`^\*`, Punctuation, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/hlb.go b/vendor/github.com/alecthomas/chroma/lexers/h/hlb.go similarity index 95% rename from vendor/github.com/alecthomas/chroma/lexers/hlb.go rename to vendor/github.com/alecthomas/chroma/lexers/h/hlb.go index 4dcf8ed3ed78f..6c5f637715755 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/hlb.go +++ b/vendor/github.com/alecthomas/chroma/lexers/h/hlb.go @@ -1,4 +1,4 @@ -package lexers +package h import ( . "github.com/alecthomas/chroma" // nolint @@ -6,14 +6,18 @@ import ( ) // HLB lexer. -var HLB = internal.Register(MustNewLexer( +var HLB = internal.Register(MustNewLazyLexer( &Config{ Name: "HLB", Aliases: []string{"hlb"}, Filenames: []string{"*.hlb"}, MimeTypes: []string{}, }, - Rules{ + hlbRules, +)) + +func hlbRules() Rules { + return Rules{ "root": { {`(#.*)`, ByGroups(CommentSingle), nil}, {`((\b(0(b|B|o|O|x|X)[a-fA-F0-9]+)\b)|(\b(0|[1-9][0-9]*)\b))`, ByGroups(LiteralNumber), nil}, @@ -50,5 +54,5 @@ var HLB = internal.Register(MustNewLexer( {`(\n|\r|\r\n)`, Text, nil}, {`.`, Text, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/h/html.go b/vendor/github.com/alecthomas/chroma/lexers/h/html.go index 07fc27ef558a7..b9ca1e1d6f62f 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/h/html.go +++ b/vendor/github.com/alecthomas/chroma/lexers/h/html.go @@ -8,7 +8,7 @@ import ( ) // HTML lexer. -var HTML = internal.Register(MustNewLexer( +var HTML = internal.Register(MustNewLazyLexer( &Config{ Name: "HTML", Aliases: []string{"html"}, @@ -18,7 +18,11 @@ var HTML = internal.Register(MustNewLexer( DotAll: true, CaseInsensitive: true, }, - Rules{ + htmlRules, +)) + +func htmlRules() Rules { + return Rules{ "root": { {`[^<&]+`, Text, nil}, {`&\S*?;`, NameEntity, nil}, @@ -55,5 +59,5 @@ var HTML = internal.Register(MustNewLexer( {`'.*?'`, LiteralString, Pop(1)}, {`[^\s>]+`, LiteralString, Pop(1)}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/h/http.go b/vendor/github.com/alecthomas/chroma/lexers/h/http.go index 135ec73fdabc9..1a0c138c4ac3b 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/h/http.go +++ b/vendor/github.com/alecthomas/chroma/lexers/h/http.go @@ -8,7 +8,7 @@ import ( ) // HTTP lexer. -var HTTP = internal.Register(httpBodyContentTypeLexer(MustNewLexer( +var HTTP = internal.Register(httpBodyContentTypeLexer(MustNewLazyLexer( &Config{ Name: "HTTP", Aliases: []string{"http"}, @@ -17,7 +17,11 @@ var HTTP = internal.Register(httpBodyContentTypeLexer(MustNewLexer( NotMultiline: true, DotAll: true, }, - Rules{ + httpRules, +))) + +func httpRules() Rules { + return Rules{ "root": { {`(GET|POST|PUT|DELETE|HEAD|OPTIONS|TRACE|PATCH|CONNECT)( +)([^ ]+)( +)(HTTP)(/)([12]\.[01])(\r?\n|\Z)`, ByGroups(NameFunction, Text, NameNamespace, Text, KeywordReserved, Operator, LiteralNumber, Text), Push("headers")}, {`(HTTP)(/)([12]\.[01])( +)(\d{3})( +)([^\r\n]+)(\r?\n|\Z)`, ByGroups(KeywordReserved, Operator, LiteralNumber, Text, LiteralNumber, Text, NameException, Text), Push("headers")}, @@ -30,8 +34,8 @@ var HTTP = internal.Register(httpBodyContentTypeLexer(MustNewLexer( "content": { {`.+`, EmitterFunc(httpContentBlock), nil}, }, - }, -))) + } +} func httpContentBlock(groups []string, lexer Lexer) Iterator { tokens := []Token{ diff --git a/vendor/github.com/alecthomas/chroma/lexers/h/hy.go b/vendor/github.com/alecthomas/chroma/lexers/h/hy.go index 17385e86fe455..7a078979938f7 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/h/hy.go +++ b/vendor/github.com/alecthomas/chroma/lexers/h/hy.go @@ -6,14 +6,18 @@ import ( ) // Hy lexer. -var Hy = internal.Register(MustNewLexer( +var Hy = internal.Register(MustNewLazyLexer( &Config{ Name: "Hy", Aliases: []string{"hylang"}, Filenames: []string{"*.hy"}, MimeTypes: []string{"text/x-hy", "application/x-hy"}, }, - Rules{ + hyRules, +)) + +func hyRules() Rules { + return Rules{ "root": { {`;.*$`, CommentSingle, nil}, {`[,\s]+`, Text, nil}, @@ -47,5 +51,5 @@ var Hy = internal.Register(MustNewLexer( {`(??@^|_~:\\]).*?)$`, ByGroups(Text, CommentSingle), nil}, @@ -76,5 +80,5 @@ var Idris = internal.Register(MustNewLexer( {`\d+`, LiteralStringEscape, Pop(1)}, {`\s+\\`, LiteralStringEscape, Pop(1)}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/i/igor.go b/vendor/github.com/alecthomas/chroma/lexers/i/igor.go index d704a4fbd907d..bbb1d7230bf6a 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/i/igor.go +++ b/vendor/github.com/alecthomas/chroma/lexers/i/igor.go @@ -6,7 +6,7 @@ import ( ) // Igor lexer. -var Igor = internal.Register(MustNewLexer( +var Igor = internal.Register(MustNewLazyLexer( &Config{ Name: "Igor", Aliases: []string{"igor", "igorpro"}, @@ -14,7 +14,11 @@ var Igor = internal.Register(MustNewLexer( MimeTypes: []string{"text/ipf"}, CaseInsensitive: true, }, - Rules{ + igorRules, +)) + +func igorRules() Rules { + return Rules{ "root": { {`//.*$`, CommentSingle, nil}, {`"([^"\\]|\\.)*"`, LiteralString, nil}, @@ -28,5 +32,5 @@ var Igor = internal.Register(MustNewLexer( {`.`, Text, nil}, {`\n|\r`, Text, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/i/ini.go b/vendor/github.com/alecthomas/chroma/lexers/i/ini.go index e57f865055f46..46b2ce2385503 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/i/ini.go +++ b/vendor/github.com/alecthomas/chroma/lexers/i/ini.go @@ -6,14 +6,18 @@ import ( ) // Ini lexer. -var Ini = internal.Register(MustNewLexer( +var Ini = internal.Register(MustNewLazyLexer( &Config{ Name: "INI", Aliases: []string{"ini", "cfg", "dosini"}, Filenames: []string{"*.ini", "*.cfg", "*.inf", ".gitconfig", ".editorconfig"}, MimeTypes: []string{"text/x-ini", "text/inf"}, }, - Rules{ + iniRules, +)) + +func iniRules() Rules { + return Rules{ "root": { {`\s+`, Text, nil}, {`[;#].*`, CommentSingle, nil}, @@ -21,5 +25,5 @@ var Ini = internal.Register(MustNewLexer( {`(.*?)([ \t]*)(=)([ \t]*)(.*(?:\n[ \t].+)*)`, ByGroups(NameAttribute, Text, Operator, Text, LiteralString), nil}, {`(.+?)$`, NameAttribute, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/i/io.go b/vendor/github.com/alecthomas/chroma/lexers/i/io.go index 840feeaae1659..8b2e53a0742df 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/i/io.go +++ b/vendor/github.com/alecthomas/chroma/lexers/i/io.go @@ -6,14 +6,18 @@ import ( ) // Io lexer. -var Io = internal.Register(MustNewLexer( +var Io = internal.Register(MustNewLazyLexer( &Config{ Name: "Io", Aliases: []string{"io"}, Filenames: []string{"*.io"}, MimeTypes: []string{"text/x-iosrc"}, }, - Rules{ + ioRules, +)) + +func ioRules() Rules { + return Rules{ "root": { {`\n`, Text, nil}, {`\s+`, Text, nil}, @@ -36,5 +40,5 @@ var Io = internal.Register(MustNewLexer( {`\+/`, CommentMultiline, Pop(1)}, {`[+/]`, CommentMultiline, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/internal/api.go b/vendor/github.com/alecthomas/chroma/lexers/internal/api.go index 08ec6ff7f2e11..c10eb3a43c14b 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/internal/api.go +++ b/vendor/github.com/alecthomas/chroma/lexers/internal/api.go @@ -146,16 +146,19 @@ func Register(lexer chroma.Lexer) chroma.Lexer { return lexer } -// Used for the fallback lexer as well as the explicit plaintext lexer -var PlaintextRules = chroma.Rules{ - "root": []chroma.Rule{ - {`.+`, chroma.Text, nil}, - {`\n`, chroma.Text, nil}, - }, +// PlaintextRules is used for the fallback lexer as well as the explicit +// plaintext lexer. +func PlaintextRules() chroma.Rules { + return chroma.Rules{ + "root": []chroma.Rule{ + {`.+`, chroma.Text, nil}, + {`\n`, chroma.Text, nil}, + }, + } } // Fallback lexer if no other is found. -var Fallback chroma.Lexer = chroma.MustNewLexer(&chroma.Config{ +var Fallback chroma.Lexer = chroma.MustNewLazyLexer(&chroma.Config{ Name: "fallback", Filenames: []string{"*"}, }, PlaintextRules) diff --git a/vendor/github.com/alecthomas/chroma/lexers/j/j.go b/vendor/github.com/alecthomas/chroma/lexers/j/j.go index 686e53b975f6f..9a2a4e3c044da 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/j/j.go +++ b/vendor/github.com/alecthomas/chroma/lexers/j/j.go @@ -6,14 +6,18 @@ import ( ) // J lexer. -var J = internal.Register(MustNewLexer( +var J = internal.Register(MustNewLazyLexer( &Config{ Name: "J", Aliases: []string{"j"}, Filenames: []string{"*.ijs"}, MimeTypes: []string{"text/x-j"}, }, - Rules{ + jRules, +)) + +func jRules() Rules { + return Rules{ "root": { {`#!.*$`, CommentPreproc, nil}, {`NB\..*`, CommentSingle, nil}, @@ -69,5 +73,5 @@ var J = internal.Register(MustNewLexer( {`''`, LiteralString, nil}, {`'`, LiteralString, Pop(1)}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/j/java.go b/vendor/github.com/alecthomas/chroma/lexers/j/java.go index c6b9a762e40f9..48a9d9fee8412 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/j/java.go +++ b/vendor/github.com/alecthomas/chroma/lexers/j/java.go @@ -6,15 +6,20 @@ import ( ) // Java lexer. -var Java = internal.Register(MustNewLexer( +var Java = internal.Register(MustNewLazyLexer( &Config{ Name: "Java", Aliases: []string{"java"}, Filenames: []string{"*.java"}, MimeTypes: []string{"text/x-java"}, DotAll: true, + EnsureNL: true, }, - Rules{ + javaRules, +)) + +func javaRules() Rules { + return Rules{ "root": { {`[^\S\n]+`, Text, nil}, {`//.*?\n`, CommentSingle, nil}, @@ -47,5 +52,5 @@ var Java = internal.Register(MustNewLexer( "import": { {`[\w.]+\*?`, NameNamespace, Pop(1)}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/j/javascript.go b/vendor/github.com/alecthomas/chroma/lexers/j/javascript.go index ffa20c3b102f5..4dba577c118ce 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/j/javascript.go +++ b/vendor/github.com/alecthomas/chroma/lexers/j/javascript.go @@ -26,11 +26,11 @@ var JavascriptRules = Rules{ {`\A#! ?/.*?\n`, CommentHashbang, nil}, {`^(?=\s|/|`, `||`, `&&`, `>`, `<`, `>=`, `≥`, `<=`, `≤`, `==`, `===`, `≡`, `!=`, `≠`, `!==`, `≢`, `.>`, `.<`, `.>=`, `.≥`, `.<=`, `.≤`, `.==`, `.!=`, `.≠`, `.=`, `.!`, `<:`, `>:`, `∈`, `∉`, `∋`, `∌`, `⊆`, `⊈`, `⊂`, `⊄`, `⊊`, `|>`, `<|`, `:`, `+`, `-`, `.+`, `.-`, `|`, `∪`, `$`, `<<`, `>>`, `>>>`, `.<<`, `.>>`, `.>>>`, `*`, `/`, `./`, `÷`, `.÷`, `%`, `⋅`, `.%`, `.*`, `\`, `.\`, `&`, `∩`, `//`, `.//`, `^`, `.^`, `::`, `.`, `+`, `-`, `!`, `√`, `∛`, `∜`), Operator, nil}, + {`[\[\](),;]`, Punctuation, nil}, + {`((?:[a-zA-Z_¡-􏿿][a-zA-Z_0-9!¡-􏿿]*))(\s*)(:)((?:[a-zA-Z_¡-􏿿][a-zA-Z_0-9!¡-􏿿]*))`, ByGroups(Name, Text, Operator, Name), nil}, + {`(?\d.])(:(?:[a-zA-Z_¡-􏿿][a-zA-Z_0-9!¡-􏿿]*))`, LiteralStringSymbol, nil}, + {`(?<=::)(\s*)((?:[a-zA-Z_¡-􏿿][a-zA-Z_0-9!¡-􏿿]*))\b(?![(\[])`, ByGroups(Text, KeywordType), nil}, + {`((?:[a-zA-Z_¡-􏿿][a-zA-Z_0-9!¡-􏿿]*))(\s*)([<>]:)(\s*)((?:[a-zA-Z_¡-􏿿][a-zA-Z_0-9!¡-􏿿]*))\b(?![(\[])`, ByGroups(KeywordType, Text, Operator, Text, KeywordType), nil}, + {`([<>]:)(\s*)((?:[a-zA-Z_¡-􏿿][a-zA-Z_0-9!¡-􏿿]*))\b(?![(\[])`, ByGroups(Operator, Text, KeywordType), nil}, + {`\b((?:[a-zA-Z_¡-􏿿][a-zA-Z_0-9!¡-􏿿]*))(\s*)([<>]:)`, ByGroups(KeywordType, Text, Operator), nil}, + {Words(``, `[²³¹ʰʲʳʷʸˡˢˣᴬᴮᴰᴱᴳᴴᴵᴶᴷᴸᴹᴺᴼᴾᴿᵀᵁᵂᵃᵇᵈᵉᵍᵏᵐᵒᵖᵗᵘᵛᵝᵞᵟᵠᵡᵢᵣᵤᵥᵦᵧᵨᵩᵪᶜᶠᶥᶦᶫᶰᶸᶻᶿ′″‴‵‶‷⁗⁰ⁱ⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾ⁿ₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎ₐₑₒₓₕₖₗₘₙₚₛₜⱼⱽ]*`, `->`, `:=`, `$=`, `?`, `||`, `&&`, `:`, `$`, `::`, `=`, `+=`, `-=`, `*=`, `/=`, `//=`, `\=`, `^=`, `÷=`, `%=`, `<<=`, `>>=`, `>>>=`, `|=`, `&=`, `⊻=`, `≔`, `⩴`, `≕'`, `~`, `=>`, `→`, `↔`, `↚`, `↛`, `↞`, `↠`, `↢`, `↣`, `↦`, `↤`, `↮`, `⇎`, `⇍`, `⇏`, `⇐`, `⇒`, `⇔`, `⇴`, `⇶`, `⇷`, `⇸`, `⇹`, `⇺`, `⇻`, `⇼`, `⇽`, `⇾`, `⇿`, `⟵`, `⟶`, `⟷`, `⟹`, `⟺`, `⟻`, `⟼`, `⟽`, `⟾`, `⟿`, `⤀`, `⤁`, `⤂`, `⤃`, `⤄`, `⤅`, `⤆`, `⤇`, `⤌`, `⤍`, `⤎`, `⤏`, `⤐`, `⤑`, `⤔`, `⤕`, `⤖`, `⤗`, `⤘`, `⤝`, `⤞`, `⤟`, `⤠`, `⥄`, `⥅`, `⥆`, `⥇`, `⥈`, `⥊`, `⥋`, `⥎`, `⥐`, `⥒`, `⥓`, `⥖`, `⥗`, `⥚`, `⥛`, `⥞`, `⥟`, `⥢`, `⥤`, `⥦`, `⥧`, `⥨`, `⥩`, `⥪`, `⥫`, `⥬`, `⥭`, `⥰`, `⧴`, `⬱`, `⬰`, `⬲`, `⬳`, `⬴`, `⬵`, `⬶`, `⬷`, `⬸`, `⬹`, `⬺`, `⬻`, `⬼`, `⬽`, `⬾`, `⬿`, `⭀`, `⭁`, `⭂`, `⭃`, `⭄`, `⭇`, `⭈`, `⭉`, `⭊`, `⭋`, `⭌`, `←`, `→`, `⇜`, `⇝`, `↜`, `↝`, `↩`, `↪`, `↫`, `↬`, `↼`, `↽`, `⇀`, `⇁`, `⇄`, `⇆`, `⇇`, `⇉`, `⇋`, `⇌`, `⇚`, `⇛`, `⇠`, `⇢`, `↷`, `↶`, `↺`, `↻`, `-->`, `<--`, `<-->`, `>`, `<`, `>=`, `≥`, `<=`, `≤`, `==`, `===`, `≡`, `!=`, `≠`, `!==`, `≢`, `∈`, `∉`, `∋`, `∌`, `⊆`, `⊈`, `⊂`, `⊄`, `⊊`, `∝`, `∊`, `∍`, `∥`, `∦`, `∷`, `∺`, `∻`, `∽`, `∾`, `≁`, `≃`, `≂`, `≄`, `≅`, `≆`, `≇`, `≈`, `≉`, `≊`, `≋`, `≌`, `≍`, `≎`, `≐`, `≑`, `≒`, `≓`, `≖`, `≗`, `≘`, `≙`, `≚`, `≛`, `≜`, `≝`, `≞`, `≟`, `≣`, `≦`, `≧`, `≨`, `≩`, `≪`, `≫`, `≬`, `≭`, `≮`, `≯`, `≰`, `≱`, `≲`, `≳`, `≴`, `≵`, `≶`, `≷`, `≸`, `≹`, `≺`, `≻`, `≼`, `≽`, `≾`, `≿`, `⊀`, `⊁`, `⊃`, `⊅`, `⊇`, `⊉`, `⊋`, `⊏`, `⊐`, `⊑`, `⊒`, `⊜`, `⊩`, `⊬`, `⊮`, `⊰`, `⊱`, `⊲`, `⊳`, `⊴`, `⊵`, `⊶`, `⊷`, `⋍`, `⋐`, `⋑`, `⋕`, `⋖`, `⋗`, `⋘`, `⋙`, `⋚`, `⋛`, `⋜`, `⋝`, `⋞`, `⋟`, `⋠`, `⋡`, `⋢`, `⋣`, `⋤`, `⋥`, `⋦`, `⋧`, `⋨`, `⋩`, `⋪`, `⋫`, `⋬`, `⋭`, `⋲`, `⋳`, `⋴`, `⋵`, `⋶`, `⋷`, `⋸`, `⋹`, `⋺`, `⋻`, `⋼`, `⋽`, `⋾`, `⋿`, `⟈`, `⟉`, `⟒`, `⦷`, `⧀`, `⧁`, `⧡`, `⧣`, `⧤`, `⧥`, `⩦`, `⩧`, `⩪`, `⩫`, `⩬`, `⩭`, `⩮`, `⩯`, `⩰`, `⩱`, `⩲`, `⩳`, `⩵`, `⩶`, `⩷`, `⩸`, `⩹`, `⩺`, `⩻`, `⩼`, `⩽`, `⩾`, `⩿`, `⪀`, `⪁`, `⪂`, `⪃`, `⪄`, `⪅`, `⪆`, `⪇`, `⪈`, `⪉`, `⪊`, `⪋`, `⪌`, `⪍`, `⪎`, `⪏`, `⪐`, `⪑`, `⪒`, `⪓`, `⪔`, `⪕`, `⪖`, `⪗`, `⪘`, `⪙`, `⪚`, `⪛`, `⪜`, `⪝`, `⪞`, `⪟`, `⪠`, `⪡`, `⪢`, `⪣`, `⪤`, `⪥`, `⪦`, `⪧`, `⪨`, `⪩`, `⪪`, `⪫`, `⪬`, `⪭`, `⪮`, `⪯`, `⪰`, `⪱`, `⪲`, `⪳`, `⪴`, `⪵`, `⪶`, `⪷`, `⪸`, `⪹`, `⪺`, `⪻`, `⪼`, `⪽`, `⪾`, `⪿`, `⫀`, `⫁`, `⫂`, `⫃`, `⫄`, `⫅`, `⫆`, `⫇`, `⫈`, `⫉`, `⫊`, `⫋`, `⫌`, `⫍`, `⫎`, `⫏`, `⫐`, `⫑`, `⫒`, `⫓`, `⫔`, `⫕`, `⫖`, `⫗`, `⫘`, `⫙`, `⫷`, `⫸`, `⫹`, `⫺`, `⊢`, `⊣`, `⟂`, `<:`, `>:`, `<|`, `|>`, `…`, `⁝`, `⋮`, `⋱`, `⋰`, `⋯`, `+`, `-`, `¦`, `|`, `⊕`, `⊖`, `⊞`, `⊟`, `++`, `∪`, `∨`, `⊔`, `±`, `∓`, `∔`, `∸`, `≏`, `⊎`, `⊻`, `⊽`, `⋎`, `⋓`, `⧺`, `⧻`, `⨈`, `⨢`, `⨣`, `⨤`, `⨥`, `⨦`, `⨧`, `⨨`, `⨩`, `⨪`, `⨫`, `⨬`, `⨭`, `⨮`, `⨹`, `⨺`, `⩁`, `⩂`, `⩅`, `⩊`, `⩌`, `⩏`, `⩐`, `⩒`, `⩔`, `⩖`, `⩗`, `⩛`, `⩝`, `⩡`, `⩢`, `⩣`, `*`, `/`, `⌿`, `÷`, `%`, `&`, `⋅`, `∘`, `×`, `\`, `∩`, `∧`, `⊗`, `⊘`, `⊙`, `⊚`, `⊛`, `⊠`, `⊡`, `⊓`, `∗`, `∙`, `∤`, `⅋`, `≀`, `⊼`, `⋄`, `⋆`, `⋇`, `⋉`, `⋊`, `⋋`, `⋌`, `⋏`, `⋒`, `⟑`, `⦸`, `⦼`, `⦾`, `⦿`, `⧶`, `⧷`, `⨇`, `⨰`, `⨱`, `⨲`, `⨳`, `⨴`, `⨵`, `⨶`, `⨷`, `⨸`, `⨻`, `⨼`, `⨽`, `⩀`, `⩃`, `⩄`, `⩋`, `⩍`, `⩎`, `⩑`, `⩓`, `⩕`, `⩘`, `⩚`, `⩜`, `⩞`, `⩟`, `⩠`, `⫛`, `⊍`, `▷`, `⨝`, `⟕`, `⟖`, `⟗`, `⨟`, `//`, `>>`, `<<`, `>>>`, `^`, `↑`, `↓`, `⇵`, `⟰`, `⟱`, `⤈`, `⤉`, `⤊`, `⤋`, `⤒`, `⤓`, `⥉`, `⥌`, `⥍`, `⥏`, `⥑`, `⥔`, `⥕`, `⥘`, `⥙`, `⥜`, `⥝`, `⥠`, `⥡`, `⥣`, `⥥`, `⥮`, `⥯`, `↑`, `↓`, `!`, `¬`, `√`, `∛`, `∜`), Operator, nil}, + {Words(``, `[²³¹ʰʲʳʷʸˡˢˣᴬᴮᴰᴱᴳᴴᴵᴶᴷᴸᴹᴺᴼᴾᴿᵀᵁᵂᵃᵇᵈᵉᵍᵏᵐᵒᵖᵗᵘᵛᵝᵞᵟᵠᵡᵢᵣᵤᵥᵦᵧᵨᵩᵪᶜᶠᶥᶦᶫᶰᶸᶻᶿ′″‴‵‶‷⁗⁰ⁱ⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾ⁿ₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎ₐₑₒₓₕₖₗₘₙₚₛₜⱼⱽ]*`, `.=`, `.+=`, `.-=`, `.*=`, `./=`, `.//=`, `.\=`, `.^=`, `.÷=`, `.%=`, `.<<=`, `.>>=`, `.>>>=`, `.|=`, `.&=`, `.⊻=`, `.≔`, `.⩴`, `.≕'`, `.~`, `.=>`, `.→`, `.↔`, `.↚`, `.↛`, `.↞`, `.↠`, `.↢`, `.↣`, `.↦`, `.↤`, `.↮`, `.⇎`, `.⇍`, `.⇏`, `.⇐`, `.⇒`, `.⇔`, `.⇴`, `.⇶`, `.⇷`, `.⇸`, `.⇹`, `.⇺`, `.⇻`, `.⇼`, `.⇽`, `.⇾`, `.⇿`, `.⟵`, `.⟶`, `.⟷`, `.⟹`, `.⟺`, `.⟻`, `.⟼`, `.⟽`, `.⟾`, `.⟿`, `.⤀`, `.⤁`, `.⤂`, `.⤃`, `.⤄`, `.⤅`, `.⤆`, `.⤇`, `.⤌`, `.⤍`, `.⤎`, `.⤏`, `.⤐`, `.⤑`, `.⤔`, `.⤕`, `.⤖`, `.⤗`, `.⤘`, `.⤝`, `.⤞`, `.⤟`, `.⤠`, `.⥄`, `.⥅`, `.⥆`, `.⥇`, `.⥈`, `.⥊`, `.⥋`, `.⥎`, `.⥐`, `.⥒`, `.⥓`, `.⥖`, `.⥗`, `.⥚`, `.⥛`, `.⥞`, `.⥟`, `.⥢`, `.⥤`, `.⥦`, `.⥧`, `.⥨`, `.⥩`, `.⥪`, `.⥫`, `.⥬`, `.⥭`, `.⥰`, `.⧴`, `.⬱`, `.⬰`, `.⬲`, `.⬳`, `.⬴`, `.⬵`, `.⬶`, `.⬷`, `.⬸`, `.⬹`, `.⬺`, `.⬻`, `.⬼`, `.⬽`, `.⬾`, `.⬿`, `.⭀`, `.⭁`, `.⭂`, `.⭃`, `.⭄`, `.⭇`, `.⭈`, `.⭉`, `.⭊`, `.⭋`, `.⭌`, `.←`, `.→`, `.⇜`, `.⇝`, `.↜`, `.↝`, `.↩`, `.↪`, `.↫`, `.↬`, `.↼`, `.↽`, `.⇀`, `.⇁`, `.⇄`, `.⇆`, `.⇇`, `.⇉`, `.⇋`, `.⇌`, `.⇚`, `.⇛`, `.⇠`, `.⇢`, `.↷`, `.↶`, `.↺`, `.↻`, `.-->`, `.<--`, `.<-->`, `.>`, `.<`, `.>=`, `.≥`, `.<=`, `.≤`, `.==`, `.===`, `.≡`, `.!=`, `.≠`, `.!==`, `.≢`, `.∈`, `.∉`, `.∋`, `.∌`, `.⊆`, `.⊈`, `.⊂`, `.⊄`, `.⊊`, `.∝`, `.∊`, `.∍`, `.∥`, `.∦`, `.∷`, `.∺`, `.∻`, `.∽`, `.∾`, `.≁`, `.≃`, `.≂`, `.≄`, `.≅`, `.≆`, `.≇`, `.≈`, `.≉`, `.≊`, `.≋`, `.≌`, `.≍`, `.≎`, `.≐`, `.≑`, `.≒`, `.≓`, `.≖`, `.≗`, `.≘`, `.≙`, `.≚`, `.≛`, `.≜`, `.≝`, `.≞`, `.≟`, `.≣`, `.≦`, `.≧`, `.≨`, `.≩`, `.≪`, `.≫`, `.≬`, `.≭`, `.≮`, `.≯`, `.≰`, `.≱`, `.≲`, `.≳`, `.≴`, `.≵`, `.≶`, `.≷`, `.≸`, `.≹`, `.≺`, `.≻`, `.≼`, `.≽`, `.≾`, `.≿`, `.⊀`, `.⊁`, `.⊃`, `.⊅`, `.⊇`, `.⊉`, `.⊋`, `.⊏`, `.⊐`, `.⊑`, `.⊒`, `.⊜`, `.⊩`, `.⊬`, `.⊮`, `.⊰`, `.⊱`, `.⊲`, `.⊳`, `.⊴`, `.⊵`, `.⊶`, `.⊷`, `.⋍`, `.⋐`, `.⋑`, `.⋕`, `.⋖`, `.⋗`, `.⋘`, `.⋙`, `.⋚`, `.⋛`, `.⋜`, `.⋝`, `.⋞`, `.⋟`, `.⋠`, `.⋡`, `.⋢`, `.⋣`, `.⋤`, `.⋥`, `.⋦`, `.⋧`, `.⋨`, `.⋩`, `.⋪`, `.⋫`, `.⋬`, `.⋭`, `.⋲`, `.⋳`, `.⋴`, `.⋵`, `.⋶`, `.⋷`, `.⋸`, `.⋹`, `.⋺`, `.⋻`, `.⋼`, `.⋽`, `.⋾`, `.⋿`, `.⟈`, `.⟉`, `.⟒`, `.⦷`, `.⧀`, `.⧁`, `.⧡`, `.⧣`, `.⧤`, `.⧥`, `.⩦`, `.⩧`, `.⩪`, `.⩫`, `.⩬`, `.⩭`, `.⩮`, `.⩯`, `.⩰`, `.⩱`, `.⩲`, `.⩳`, `.⩵`, `.⩶`, `.⩷`, `.⩸`, `.⩹`, `.⩺`, `.⩻`, `.⩼`, `.⩽`, `.⩾`, `.⩿`, `.⪀`, `.⪁`, `.⪂`, `.⪃`, `.⪄`, `.⪅`, `.⪆`, `.⪇`, `.⪈`, `.⪉`, `.⪊`, `.⪋`, `.⪌`, `.⪍`, `.⪎`, `.⪏`, `.⪐`, `.⪑`, `.⪒`, `.⪓`, `.⪔`, `.⪕`, `.⪖`, `.⪗`, `.⪘`, `.⪙`, `.⪚`, `.⪛`, `.⪜`, `.⪝`, `.⪞`, `.⪟`, `.⪠`, `.⪡`, `.⪢`, `.⪣`, `.⪤`, `.⪥`, `.⪦`, `.⪧`, `.⪨`, `.⪩`, `.⪪`, `.⪫`, `.⪬`, `.⪭`, `.⪮`, `.⪯`, `.⪰`, `.⪱`, `.⪲`, `.⪳`, `.⪴`, `.⪵`, `.⪶`, `.⪷`, `.⪸`, `.⪹`, `.⪺`, `.⪻`, `.⪼`, `.⪽`, `.⪾`, `.⪿`, `.⫀`, `.⫁`, `.⫂`, `.⫃`, `.⫄`, `.⫅`, `.⫆`, `.⫇`, `.⫈`, `.⫉`, `.⫊`, `.⫋`, `.⫌`, `.⫍`, `.⫎`, `.⫏`, `.⫐`, `.⫑`, `.⫒`, `.⫓`, `.⫔`, `.⫕`, `.⫖`, `.⫗`, `.⫘`, `.⫙`, `.⫷`, `.⫸`, `.⫹`, `.⫺`, `.⊢`, `.⊣`, `.⟂`, `.<:`, `.>:`, `.<|`, `.|>`, `.…`, `.⁝`, `.⋮`, `.⋱`, `.⋰`, `.⋯`, `.+`, `.-`, `.¦`, `.|`, `.⊕`, `.⊖`, `.⊞`, `.⊟`, `.++`, `.∪`, `.∨`, `.⊔`, `.±`, `.∓`, `.∔`, `.∸`, `.≏`, `.⊎`, `.⊻`, `.⊽`, `.⋎`, `.⋓`, `.⧺`, `.⧻`, `.⨈`, `.⨢`, `.⨣`, `.⨤`, `.⨥`, `.⨦`, `.⨧`, `.⨨`, `.⨩`, `.⨪`, `.⨫`, `.⨬`, `.⨭`, `.⨮`, `.⨹`, `.⨺`, `.⩁`, `.⩂`, `.⩅`, `.⩊`, `.⩌`, `.⩏`, `.⩐`, `.⩒`, `.⩔`, `.⩖`, `.⩗`, `.⩛`, `.⩝`, `.⩡`, `.⩢`, `.⩣`, `.*`, `./`, `.⌿`, `.÷`, `.%`, `.&`, `.⋅`, `.∘`, `.×`, `.\`, `.∩`, `.∧`, `.⊗`, `.⊘`, `.⊙`, `.⊚`, `.⊛`, `.⊠`, `.⊡`, `.⊓`, `.∗`, `.∙`, `.∤`, `.⅋`, `.≀`, `.⊼`, `.⋄`, `.⋆`, `.⋇`, `.⋉`, `.⋊`, `.⋋`, `.⋌`, `.⋏`, `.⋒`, `.⟑`, `.⦸`, `.⦼`, `.⦾`, `.⦿`, `.⧶`, `.⧷`, `.⨇`, `.⨰`, `.⨱`, `.⨲`, `.⨳`, `.⨴`, `.⨵`, `.⨶`, `.⨷`, `.⨸`, `.⨻`, `.⨼`, `.⨽`, `.⩀`, `.⩃`, `.⩄`, `.⩋`, `.⩍`, `.⩎`, `.⩑`, `.⩓`, `.⩕`, `.⩘`, `.⩚`, `.⩜`, `.⩞`, `.⩟`, `.⩠`, `.⫛`, `.⊍`, `.▷`, `.⨝`, `.⟕`, `.⟖`, `.⟗`, `.⨟`, `.//`, `.>>`, `.<<`, `.>>>`, `.^`, `.↑`, `.↓`, `.⇵`, `.⟰`, `.⟱`, `.⤈`, `.⤉`, `.⤊`, `.⤋`, `.⤒`, `.⤓`, `.⥉`, `.⥌`, `.⥍`, `.⥏`, `.⥑`, `.⥔`, `.⥕`, `.⥘`, `.⥙`, `.⥜`, `.⥝`, `.⥠`, `.⥡`, `.⥣`, `.⥥`, `.⥮`, `.⥯`, `.↑`, `.↓`, `.!`, `.¬`, `.√`, `.∛`, `.∜`), Operator, nil}, + {Words(``, ``, `...`, `..`), Operator, nil}, {`'(\\.|\\[0-7]{1,3}|\\x[a-fA-F0-9]{1,3}|\\u[a-fA-F0-9]{1,4}|\\U[a-fA-F0-9]{1,6}|[^\\\'\n])'`, LiteralStringChar, nil}, - {`(?<=[.\w)\]])\'+`, Operator, nil}, - {`"""`, LiteralString, Push("tqstring")}, - {`"`, LiteralString, Push("string")}, - {`r"""`, LiteralStringRegex, Push("tqregex")}, - {`r"`, LiteralStringRegex, Push("regex")}, - {"`", LiteralStringBacktick, Push("command")}, - {`((?:[a-zA-Z_¡-￿]|[𐀀-􏿿])(?:[a-zA-Z_0-9¡-￿]|[𐀀-􏿿])*!*)(')?`, ByGroups(Name, Operator), nil}, - {`(@(?:[a-zA-Z_¡-￿]|[𐀀-􏿿])(?:[a-zA-Z_0-9¡-￿]|[𐀀-􏿿])*!*)(')?`, ByGroups(NameDecorator, Operator), nil}, - {`(\d+(_\d+)+\.\d*|\d*\.\d+(_\d+)+)([eEf][+-]?[0-9]+)?`, LiteralNumberFloat, nil}, - {`(\d+\.\d*|\d*\.\d+)([eEf][+-]?[0-9]+)?`, LiteralNumberFloat, nil}, - {`\d+(_\d+)+[eEf][+-]?[0-9]+`, LiteralNumberFloat, nil}, - {`\d+[eEf][+-]?[0-9]+`, LiteralNumberFloat, nil}, - {`0b[01]+(_[01]+)+`, LiteralNumberBin, nil}, - {`0b[01]+`, LiteralNumberBin, nil}, - {`0o[0-7]+(_[0-7]+)+`, LiteralNumberOct, nil}, - {`0o[0-7]+`, LiteralNumberOct, nil}, - {`0x[a-fA-F0-9]+(_[a-fA-F0-9]+)+`, LiteralNumberHex, nil}, - {`0x[a-fA-F0-9]+`, LiteralNumberHex, nil}, - {`\d+(_\d+)+`, LiteralNumberInteger, nil}, - {`\d+`, LiteralNumberInteger, nil}, + {`(?<=[.\w)\]])(\'[²³¹ʰʲʳʷʸˡˢˣᴬᴮᴰᴱᴳᴴᴵᴶᴷᴸᴹᴺᴼᴾᴿᵀᵁᵂᵃᵇᵈᵉᵍᵏᵐᵒᵖᵗᵘᵛᵝᵞᵟᵠᵡᵢᵣᵤᵥᵦᵧᵨᵩᵪᶜᶠᶥᶦᶫᶰᶸᶻᶿ′″‴‵‶‷⁗⁰ⁱ⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾ⁿ₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎ₐₑₒₓₕₖₗₘₙₚₛₜⱼⱽ]*)+`, Operator, nil}, + {`(raw)(""")`, ByGroups(LiteralStringAffix, LiteralString), Push("tqrawstring")}, + {`(raw)(")`, ByGroups(LiteralStringAffix, LiteralString), Push("rawstring")}, + {`(r)(""")`, ByGroups(LiteralStringAffix, LiteralStringRegex), Push("tqregex")}, + {`(r)(")`, ByGroups(LiteralStringAffix, LiteralStringRegex), Push("regex")}, + {`((?:[a-zA-Z_¡-􏿿][a-zA-Z_0-9!¡-􏿿]*))?(""")`, ByGroups(LiteralStringAffix, LiteralString), Push("tqstring")}, + {`((?:[a-zA-Z_¡-􏿿][a-zA-Z_0-9!¡-􏿿]*))?(")`, ByGroups(LiteralStringAffix, LiteralString), Push("string")}, + {"((?:[a-zA-Z_\u00a1-\U0010ffff][a-zA-Z_0-9!\u00a1-\U0010ffff]*))?(```)", ByGroups(LiteralStringAffix, LiteralStringBacktick), Push("tqcommand")}, + {"((?:[a-zA-Z_\u00a1-\U0010ffff][a-zA-Z_0-9!\u00a1-\U0010ffff]*))?(`)", ByGroups(LiteralStringAffix, LiteralStringBacktick), Push("command")}, + {`((?:[a-zA-Z_¡-􏿿][a-zA-Z_0-9!¡-􏿿]*))(\{)`, ByGroups(KeywordType, Punctuation), Push("curly")}, + {`(where)(\s+)((?:[a-zA-Z_¡-􏿿][a-zA-Z_0-9!¡-􏿿]*))`, ByGroups(Keyword, Text, KeywordType), nil}, + {`(\{)`, Punctuation, Push("curly")}, + {`(abstract[ \t]+type|primitive[ \t]+type|mutable[ \t]+struct|struct)([\s()]+)((?:[a-zA-Z_¡-􏿿][a-zA-Z_0-9!¡-􏿿]*))`, ByGroups(Keyword, Text, KeywordType), nil}, + {`@(?:[a-zA-Z_¡-􏿿][a-zA-Z_0-9!¡-􏿿]*)`, NameDecorator, nil}, + {Words(`@`, `[²³¹ʰʲʳʷʸˡˢˣᴬᴮᴰᴱᴳᴴᴵᴶᴷᴸᴹᴺᴼᴾᴿᵀᵁᵂᵃᵇᵈᵉᵍᵏᵐᵒᵖᵗᵘᵛᵝᵞᵟᵠᵡᵢᵣᵤᵥᵦᵧᵨᵩᵪᶜᶠᶥᶦᶫᶰᶸᶻᶿ′″‴‵‶‷⁗⁰ⁱ⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾ⁿ₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎ₐₑₒₓₕₖₗₘₙₚₛₜⱼⱽ]*`, `->`, `:=`, `$=`, `?`, `||`, `&&`, `:`, `$`, `::`, `..`, `.`, `=`, `+=`, `-=`, `*=`, `/=`, `//=`, `\=`, `^=`, `÷=`, `%=`, `<<=`, `>>=`, `>>>=`, `|=`, `&=`, `⊻=`, `≔`, `⩴`, `≕'`, `~`, `=>`, `→`, `↔`, `↚`, `↛`, `↞`, `↠`, `↢`, `↣`, `↦`, `↤`, `↮`, `⇎`, `⇍`, `⇏`, `⇐`, `⇒`, `⇔`, `⇴`, `⇶`, `⇷`, `⇸`, `⇹`, `⇺`, `⇻`, `⇼`, `⇽`, `⇾`, `⇿`, `⟵`, `⟶`, `⟷`, `⟹`, `⟺`, `⟻`, `⟼`, `⟽`, `⟾`, `⟿`, `⤀`, `⤁`, `⤂`, `⤃`, `⤄`, `⤅`, `⤆`, `⤇`, `⤌`, `⤍`, `⤎`, `⤏`, `⤐`, `⤑`, `⤔`, `⤕`, `⤖`, `⤗`, `⤘`, `⤝`, `⤞`, `⤟`, `⤠`, `⥄`, `⥅`, `⥆`, `⥇`, `⥈`, `⥊`, `⥋`, `⥎`, `⥐`, `⥒`, `⥓`, `⥖`, `⥗`, `⥚`, `⥛`, `⥞`, `⥟`, `⥢`, `⥤`, `⥦`, `⥧`, `⥨`, `⥩`, `⥪`, `⥫`, `⥬`, `⥭`, `⥰`, `⧴`, `⬱`, `⬰`, `⬲`, `⬳`, `⬴`, `⬵`, `⬶`, `⬷`, `⬸`, `⬹`, `⬺`, `⬻`, `⬼`, `⬽`, `⬾`, `⬿`, `⭀`, `⭁`, `⭂`, `⭃`, `⭄`, `⭇`, `⭈`, `⭉`, `⭊`, `⭋`, `⭌`, `←`, `→`, `⇜`, `⇝`, `↜`, `↝`, `↩`, `↪`, `↫`, `↬`, `↼`, `↽`, `⇀`, `⇁`, `⇄`, `⇆`, `⇇`, `⇉`, `⇋`, `⇌`, `⇚`, `⇛`, `⇠`, `⇢`, `↷`, `↶`, `↺`, `↻`, `-->`, `<--`, `<-->`, `>`, `<`, `>=`, `≥`, `<=`, `≤`, `==`, `===`, `≡`, `!=`, `≠`, `!==`, `≢`, `∈`, `∉`, `∋`, `∌`, `⊆`, `⊈`, `⊂`, `⊄`, `⊊`, `∝`, `∊`, `∍`, `∥`, `∦`, `∷`, `∺`, `∻`, `∽`, `∾`, `≁`, `≃`, `≂`, `≄`, `≅`, `≆`, `≇`, `≈`, `≉`, `≊`, `≋`, `≌`, `≍`, `≎`, `≐`, `≑`, `≒`, `≓`, `≖`, `≗`, `≘`, `≙`, `≚`, `≛`, `≜`, `≝`, `≞`, `≟`, `≣`, `≦`, `≧`, `≨`, `≩`, `≪`, `≫`, `≬`, `≭`, `≮`, `≯`, `≰`, `≱`, `≲`, `≳`, `≴`, `≵`, `≶`, `≷`, `≸`, `≹`, `≺`, `≻`, `≼`, `≽`, `≾`, `≿`, `⊀`, `⊁`, `⊃`, `⊅`, `⊇`, `⊉`, `⊋`, `⊏`, `⊐`, `⊑`, `⊒`, `⊜`, `⊩`, `⊬`, `⊮`, `⊰`, `⊱`, `⊲`, `⊳`, `⊴`, `⊵`, `⊶`, `⊷`, `⋍`, `⋐`, `⋑`, `⋕`, `⋖`, `⋗`, `⋘`, `⋙`, `⋚`, `⋛`, `⋜`, `⋝`, `⋞`, `⋟`, `⋠`, `⋡`, `⋢`, `⋣`, `⋤`, `⋥`, `⋦`, `⋧`, `⋨`, `⋩`, `⋪`, `⋫`, `⋬`, `⋭`, `⋲`, `⋳`, `⋴`, `⋵`, `⋶`, `⋷`, `⋸`, `⋹`, `⋺`, `⋻`, `⋼`, `⋽`, `⋾`, `⋿`, `⟈`, `⟉`, `⟒`, `⦷`, `⧀`, `⧁`, `⧡`, `⧣`, `⧤`, `⧥`, `⩦`, `⩧`, `⩪`, `⩫`, `⩬`, `⩭`, `⩮`, `⩯`, `⩰`, `⩱`, `⩲`, `⩳`, `⩵`, `⩶`, `⩷`, `⩸`, `⩹`, `⩺`, `⩻`, `⩼`, `⩽`, `⩾`, `⩿`, `⪀`, `⪁`, `⪂`, `⪃`, `⪄`, `⪅`, `⪆`, `⪇`, `⪈`, `⪉`, `⪊`, `⪋`, `⪌`, `⪍`, `⪎`, `⪏`, `⪐`, `⪑`, `⪒`, `⪓`, `⪔`, `⪕`, `⪖`, `⪗`, `⪘`, `⪙`, `⪚`, `⪛`, `⪜`, `⪝`, `⪞`, `⪟`, `⪠`, `⪡`, `⪢`, `⪣`, `⪤`, `⪥`, `⪦`, `⪧`, `⪨`, `⪩`, `⪪`, `⪫`, `⪬`, `⪭`, `⪮`, `⪯`, `⪰`, `⪱`, `⪲`, `⪳`, `⪴`, `⪵`, `⪶`, `⪷`, `⪸`, `⪹`, `⪺`, `⪻`, `⪼`, `⪽`, `⪾`, `⪿`, `⫀`, `⫁`, `⫂`, `⫃`, `⫄`, `⫅`, `⫆`, `⫇`, `⫈`, `⫉`, `⫊`, `⫋`, `⫌`, `⫍`, `⫎`, `⫏`, `⫐`, `⫑`, `⫒`, `⫓`, `⫔`, `⫕`, `⫖`, `⫗`, `⫘`, `⫙`, `⫷`, `⫸`, `⫹`, `⫺`, `⊢`, `⊣`, `⟂`, `<:`, `>:`, `<|`, `|>`, `…`, `⁝`, `⋮`, `⋱`, `⋰`, `⋯`, `+`, `-`, `¦`, `|`, `⊕`, `⊖`, `⊞`, `⊟`, `++`, `∪`, `∨`, `⊔`, `±`, `∓`, `∔`, `∸`, `≏`, `⊎`, `⊻`, `⊽`, `⋎`, `⋓`, `⧺`, `⧻`, `⨈`, `⨢`, `⨣`, `⨤`, `⨥`, `⨦`, `⨧`, `⨨`, `⨩`, `⨪`, `⨫`, `⨬`, `⨭`, `⨮`, `⨹`, `⨺`, `⩁`, `⩂`, `⩅`, `⩊`, `⩌`, `⩏`, `⩐`, `⩒`, `⩔`, `⩖`, `⩗`, `⩛`, `⩝`, `⩡`, `⩢`, `⩣`, `*`, `/`, `⌿`, `÷`, `%`, `&`, `⋅`, `∘`, `×`, `\`, `∩`, `∧`, `⊗`, `⊘`, `⊙`, `⊚`, `⊛`, `⊠`, `⊡`, `⊓`, `∗`, `∙`, `∤`, `⅋`, `≀`, `⊼`, `⋄`, `⋆`, `⋇`, `⋉`, `⋊`, `⋋`, `⋌`, `⋏`, `⋒`, `⟑`, `⦸`, `⦼`, `⦾`, `⦿`, `⧶`, `⧷`, `⨇`, `⨰`, `⨱`, `⨲`, `⨳`, `⨴`, `⨵`, `⨶`, `⨷`, `⨸`, `⨻`, `⨼`, `⨽`, `⩀`, `⩃`, `⩄`, `⩋`, `⩍`, `⩎`, `⩑`, `⩓`, `⩕`, `⩘`, `⩚`, `⩜`, `⩞`, `⩟`, `⩠`, `⫛`, `⊍`, `▷`, `⨝`, `⟕`, `⟖`, `⟗`, `⨟`, `//`, `>>`, `<<`, `>>>`, `^`, `↑`, `↓`, `⇵`, `⟰`, `⟱`, `⤈`, `⤉`, `⤊`, `⤋`, `⤒`, `⤓`, `⥉`, `⥌`, `⥍`, `⥏`, `⥑`, `⥔`, `⥕`, `⥘`, `⥙`, `⥜`, `⥝`, `⥠`, `⥡`, `⥣`, `⥥`, `⥮`, `⥯`, `↑`, `↓`, `!`, `¬`, `√`, `∛`, `∜`), NameDecorator, nil}, + {Words(``, `\b`, `baremodule`, `begin`, `break`, `catch`, `ccall`, `const`, `continue`, `do`, `else`, `elseif`, `end`, `export`, `finally`, `for`, `function`, `global`, `if`, `import`, `in`, `isa`, `let`, `local`, `macro`, `module`, `quote`, `return`, `try`, `using`, `where`, `while`), Keyword, nil}, + {Words(``, `\b`, `AbstractArray`, `AbstractChannel`, `AbstractChar`, `AbstractDict`, `AbstractDisplay`, `AbstractFloat`, `AbstractIrrational`, `AbstractMatch`, `AbstractMatrix`, `AbstractPattern`, `AbstractRange`, `AbstractSet`, `AbstractString`, `AbstractUnitRange`, `AbstractVecOrMat`, `AbstractVector`, `Any`, `ArgumentError`, `Array`, `AssertionError`, `BigFloat`, `BigInt`, `BitArray`, `BitMatrix`, `BitSet`, `BitVector`, `Bool`, `BoundsError`, `CapturedException`, `CartesianIndex`, `CartesianIndices`, `Cchar`, `Cdouble`, `Cfloat`, `Channel`, `Char`, `Cint`, `Cintmax_t`, `Clong`, `Clonglong`, `Cmd`, `Colon`, `Complex`, `ComplexF16`, `ComplexF32`, `ComplexF64`, `ComposedFunction`, `CompositeException`, `Condition`, `Cptrdiff_t`, `Cshort`, `Csize_t`, `Cssize_t`, `Cstring`, `Cuchar`, `Cuint`, `Cuintmax_t`, `Culong`, `Culonglong`, `Cushort`, `Cvoid`, `Cwchar_t`, `Cwstring`, `DataType`, `DenseArray`, `DenseMatrix`, `DenseVecOrMat`, `DenseVector`, `Dict`, `DimensionMismatch`, `Dims`, `DivideError`, `DomainError`, `EOFError`, `Enum`, `ErrorException`, `Exception`, `ExponentialBackOff`, `Expr`, `Float16`, `Float32`, `Float64`, `Function`, `GlobalRef`, `HTML`, `IO`, `IOBuffer`, `IOContext`, `IOStream`, `IdDict`, `IndexCartesian`, `IndexLinear`, `IndexStyle`, `InexactError`, `InitError`, `Int`, `Int128`, `Int16`, `Int32`, `Int64`, `Int8`, `Integer`, `InterruptException`, `InvalidStateException`, `Irrational`, `KeyError`, `LinRange`, `LineNumberNode`, `LinearIndices`, `LoadError`, `MIME`, `Matrix`, `Method`, `MethodError`, `Missing`, `MissingException`, `Module`, `NTuple`, `NamedTuple`, `Nothing`, `Number`, `OrdinalRange`, `OutOfMemoryError`, `OverflowError`, `Pair`, `PartialQuickSort`, `PermutedDimsArray`, `Pipe`, `ProcessFailedException`, `Ptr`, `QuoteNode`, `Rational`, `RawFD`, `ReadOnlyMemoryError`, `Real`, `ReentrantLock`, `Ref`, `Regex`, `RegexMatch`, `RoundingMode`, `SegmentationFault`, `Set`, `Signed`, `Some`, `StackOverflowError`, `StepRange`, `StepRangeLen`, `StridedArray`, `StridedMatrix`, `StridedVecOrMat`, `StridedVector`, `String`, `StringIndexError`, `SubArray`, `SubString`, `SubstitutionString`, `Symbol`, `SystemError`, `Task`, `TaskFailedException`, `Text`, `TextDisplay`, `Timer`, `Tuple`, `Type`, `TypeError`, `TypeVar`, `UInt`, `UInt128`, `UInt16`, `UInt32`, `UInt64`, `UInt8`, `UndefInitializer`, `UndefKeywordError`, `UndefRefError`, `UndefVarError`, `Union`, `UnionAll`, `UnitRange`, `Unsigned`, `Val`, `Vararg`, `VecElement`, `VecOrMat`, `Vector`, `VersionNumber`, `WeakKeyDict`, `WeakRef`), KeywordType, nil}, + {Words(``, `\b`, `ARGS`, `C_NULL`, `DEPOT_PATH`, `ENDIAN_BOM`, `ENV`, `Inf`, `Inf16`, `Inf32`, `Inf64`, `InsertionSort`, `LOAD_PATH`, `MergeSort`, `NaN`, `NaN16`, `NaN32`, `NaN64`, `PROGRAM_FILE`, `QuickSort`, `RoundDown`, `RoundFromZero`, `RoundNearest`, `RoundNearestTiesAway`, `RoundNearestTiesUp`, `RoundToZero`, `RoundUp`, `VERSION`, `devnull`, `false`, `im`, `missing`, `nothing`, `pi`, `stderr`, `stdin`, `stdout`, `true`, `undef`, `π`, `ℯ`), NameBuiltin, nil}, + {`(?:[a-zA-Z_¡-􏿿][a-zA-Z_0-9!¡-􏿿]*)`, Name, nil}, + {`(\d+((_\d+)+)?\.(?!\.)(\d+((_\d+)+)?)?|\.\d+((_\d+)+)?)([eEf][+-]?[0-9]+)?`, LiteralNumberFloat, nil}, + {`\d+((_\d+)+)?[eEf][+-]?[0-9]+`, LiteralNumberFloat, nil}, + {`0x[a-fA-F0-9]+((_[a-fA-F0-9]+)+)?(\.([a-fA-F0-9]+((_[a-fA-F0-9]+)+)?)?)?p[+-]?\d+`, LiteralNumberFloat, nil}, + {`0b[01]+((_[01]+)+)?`, LiteralNumberBin, nil}, + {`0o[0-7]+((_[0-7]+)+)?`, LiteralNumberOct, nil}, + {`0x[a-fA-F0-9]+((_[a-fA-F0-9]+)+)?`, LiteralNumberHex, nil}, + {`\d+((_\d+)+)?`, LiteralNumberInteger, nil}, + {Words(``, ``, `.`), Operator, nil}, }, "blockcomment": { {`[^=#]`, CommentMultiline, nil}, @@ -56,40 +68,67 @@ var Julia = internal.Register(MustNewLexer( {`=#`, CommentMultiline, Pop(1)}, {`[=#]`, CommentMultiline, nil}, }, - "string": { + "curly": { + {`\{`, Punctuation, Push()}, + {`\}`, Punctuation, Pop(1)}, + {`(?:[a-zA-Z_¡-􏿿][a-zA-Z_0-9!¡-􏿿]*)`, KeywordType, nil}, + Include("root"), + }, + "tqrawstring": { + {`"""`, LiteralString, Pop(1)}, + {`([^"]|"[^"][^"])+`, LiteralString, nil}, + }, + "rawstring": { {`"`, LiteralString, Pop(1)}, - {`\\([\\"\'$nrbtfav]|(x|u|U)[a-fA-F0-9]+|\d+)`, LiteralStringEscape, nil}, - {`\$(?:[a-zA-Z_¡-￿]|[𐀀-􏿿])(?:[a-zA-Z_0-9¡-￿]|[𐀀-􏿿])*!*`, LiteralStringInterpol, nil}, + {`\\"`, LiteralStringEscape, nil}, + {`([^"\\]|\\[^"])+`, LiteralString, nil}, + }, + "interp": { + {`\$(?:[a-zA-Z_¡-􏿿][a-zA-Z_0-9!¡-􏿿]*)`, LiteralStringInterpol, nil}, {`(\$)(\()`, ByGroups(LiteralStringInterpol, Punctuation), Push("in-intp")}, + }, + "in-intp": { + {`\(`, Punctuation, Push()}, + {`\)`, Punctuation, Pop(1)}, + Include("root"), + }, + "string": { + {`(")((?:[a-zA-Z_¡-􏿿][a-zA-Z_0-9!¡-􏿿]*)|\d+)?`, ByGroups(LiteralString, LiteralStringAffix), Pop(1)}, + {`\\([\\"\'$nrbtfav]|(x|u|U)[a-fA-F0-9]+|\d+)`, LiteralStringEscape, nil}, + Include("interp"), {`%[-#0 +]*([0-9]+|[*])?(\.([0-9]+|[*]))?[hlL]?[E-GXc-giorsux%]`, LiteralStringInterpol, nil}, - {`.|\s`, LiteralString, nil}, + {`[^"$%\\]+`, LiteralString, nil}, + {`.`, LiteralString, nil}, }, "tqstring": { - {`"""`, LiteralString, Pop(1)}, + {`(""")((?:[a-zA-Z_¡-􏿿][a-zA-Z_0-9!¡-􏿿]*)|\d+)?`, ByGroups(LiteralString, LiteralStringAffix), Pop(1)}, {`\\([\\"\'$nrbtfav]|(x|u|U)[a-fA-F0-9]+|\d+)`, LiteralStringEscape, nil}, - {`\$(?:[a-zA-Z_¡-￿]|[𐀀-􏿿])(?:[a-zA-Z_0-9¡-￿]|[𐀀-􏿿])*!*`, LiteralStringInterpol, nil}, - {`(\$)(\()`, ByGroups(LiteralStringInterpol, Punctuation), Push("in-intp")}, - {`.|\s`, LiteralString, nil}, + Include("interp"), + {`[^"$%\\]+`, LiteralString, nil}, + {`.`, LiteralString, nil}, }, "regex": { - {`"`, LiteralStringRegex, Pop(1)}, + {`(")([imsxa]*)?`, ByGroups(LiteralStringRegex, LiteralStringAffix), Pop(1)}, {`\\"`, LiteralStringRegex, nil}, - {`.|\s`, LiteralStringRegex, nil}, + {`[^\\"]+`, LiteralStringRegex, nil}, }, "tqregex": { - {`"""`, LiteralStringRegex, Pop(1)}, - {`.|\s`, LiteralStringRegex, nil}, + {`(""")([imsxa]*)?`, ByGroups(LiteralStringRegex, LiteralStringAffix), Pop(1)}, + {`[^"]+`, LiteralStringRegex, nil}, }, "command": { - {"`", LiteralStringBacktick, Pop(1)}, - {`\$(?:[a-zA-Z_¡-￿]|[𐀀-􏿿])(?:[a-zA-Z_0-9¡-￿]|[𐀀-􏿿])*!*`, LiteralStringInterpol, nil}, - {`(\$)(\()`, ByGroups(LiteralStringInterpol, Punctuation), Push("in-intp")}, - {`.|\s`, LiteralStringBacktick, nil}, + {"(`)((?:[a-zA-Z_\u00a1-\U0010ffff][a-zA-Z_0-9!\u00a1-\U0010ffff]*)|\\d+)?", ByGroups(LiteralStringBacktick, LiteralStringAffix), Pop(1)}, + {"\\\\[`$]", LiteralStringEscape, nil}, + Include("interp"), + {"[^\\\\`$]+", LiteralStringBacktick, nil}, + {`.`, LiteralStringBacktick, nil}, }, - "in-intp": { - {`\(`, Punctuation, Push()}, - {`\)`, Punctuation, Pop(1)}, - Include("root"), + "tqcommand": { + {"(```)((?:[a-zA-Z_\u00a1-\U0010ffff][a-zA-Z_0-9!\u00a1-\U0010ffff]*)|\\d+)?", ByGroups(LiteralStringBacktick, LiteralStringAffix), Pop(1)}, + {`\\\$`, LiteralStringEscape, nil}, + Include("interp"), + {"[^\\\\`$]+", LiteralStringBacktick, nil}, + {`.`, LiteralStringBacktick, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/j/jungle.go b/vendor/github.com/alecthomas/chroma/lexers/j/jungle.go index 83d46a48df785..5dbda9fbad69a 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/j/jungle.go +++ b/vendor/github.com/alecthomas/chroma/lexers/j/jungle.go @@ -5,14 +5,18 @@ import ( "github.com/alecthomas/chroma/lexers/internal" ) -var Jungle = internal.Register(MustNewLexer( +var Jungle = internal.Register(MustNewLazyLexer( &Config{ Name: "Jungle", Aliases: []string{"jungle"}, Filenames: []string{"*.jungle"}, MimeTypes: []string{"text/x-jungle"}, }, - Rules{ + jungleRules, +)) + +func jungleRules() Rules { + return Rules{ "root": { {`[^\S\n]+`, Text, nil}, {`\n`, Text, nil}, @@ -46,5 +50,5 @@ var Jungle = internal.Register(MustNewLexer( {`[a-zA-Z_]\w*`, Name, nil}, Default(Pop(1)), }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/k/kotlin.go b/vendor/github.com/alecthomas/chroma/lexers/k/kotlin.go index 17cdfa6294538..671f85e0a42b4 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/k/kotlin.go +++ b/vendor/github.com/alecthomas/chroma/lexers/k/kotlin.go @@ -5,10 +5,8 @@ import ( "github.com/alecthomas/chroma/lexers/internal" ) -var kotlinIdentifier = "_A-Z\u00c0-\u00d6\u00d8-\u00de\u0100\u0102\u0104\u0106\u0108\u010a\u010c\u010e\u0110\u0112\u0114\u0116\u0118\u011a\u011c\u011e\u0120\u0122\u0124\u0126\u0128\u012a\u012c\u012e\u0130\u0132\u0134\u0136\u0139\u013b\u013d\u013f\u0141\u0143\u0145\u0147\u014a\u014c\u014e\u0150\u0152\u0154\u0156\u0158\u015a\u015c\u015e\u0160\u0162\u0164\u0166\u0168\u016a\u016c\u016e\u0170\u0172\u0174\u0176\u0178-\u0179\u017b\u017d\u0181-\u0182\u0184\u0186-\u0187\u0189-\u018b\u018e-\u0191\u0193-\u0194\u0196-\u0198\u019c-\u019d\u019f-\u01a0\u01a2\u01a4\u01a6-\u01a7\u01a9\u01ac\u01ae-\u01af\u01b1-\u01b3\u01b5\u01b7-\u01b8\u01bc\u01c4\u01c7\u01ca\u01cd\u01cf\u01d1\u01d3\u01d5\u01d7\u01d9\u01db\u01de\u01e0\u01e2\u01e4\u01e6\u01e8\u01ea\u01ec\u01ee\u01f1\u01f4\u01f6-\u01f8\u01fa\u01fc\u01fe\u0200\u0202\u0204\u0206\u0208\u020a\u020c\u020e\u0210\u0212\u0214\u0216\u0218\u021a\u021c\u021e\u0220\u0222\u0224\u0226\u0228\u022a\u022c\u022e\u0230\u0232\u023a-\u023b\u023d-\u023e\u0241\u0243-\u0246\u0248\u024a\u024c\u024e\u0370\u0372\u0376\u0386\u0388-\u038a\u038c\u038e-\u038f\u0391-\u03a1\u03a3-\u03ab\u03cf\u03d2-\u03d4\u03d8\u03da\u03dc\u03de\u03e0\u03e2\u03e4\u03e6\u03e8\u03ea\u03ec\u03ee\u03f4\u03f7\u03f9-\u03fa\u03fd-\u042f\u0460\u0462\u0464\u0466\u0468\u046a\u046c\u046e\u0470\u0472\u0474\u0476\u0478\u047a\u047c\u047e\u0480\u048a\u048c\u048e\u0490\u0492\u0494\u0496\u0498\u049a\u049c\u049e\u04a0\u04a2\u04a4\u04a6\u04a8\u04aa\u04ac\u04ae\u04b0\u04b2\u04b4\u04b6\u04b8\u04ba\u04bc\u04be\u04c0-\u04c1\u04c3\u04c5\u04c7\u04c9\u04cb\u04cd\u04d0\u04d2\u04d4\u04d6\u04d8\u04da\u04dc\u04de\u04e0\u04e2\u04e4\u04e6\u04e8\u04ea\u04ec\u04ee\u04f0\u04f2\u04f4\u04f6\u04f8\u04fa\u04fc\u04fe\u0500\u0502\u0504\u0506\u0508\u050a\u050c\u050e\u0510\u0512\u0514\u0516\u0518\u051a\u051c\u051e\u0520\u0522\u0524\u0526\u0531-\u0556\u10a0-\u10c5\u10c7\u10cd\u1e00\u1e02\u1e04\u1e06\u1e08\u1e0a\u1e0c\u1e0e\u1e10\u1e12\u1e14\u1e16\u1e18\u1e1a\u1e1c\u1e1e\u1e20\u1e22\u1e24\u1e26\u1e28\u1e2a\u1e2c\u1e2e\u1e30\u1e32\u1e34\u1e36\u1e38\u1e3a\u1e3c\u1e3e\u1e40\u1e42\u1e44\u1e46\u1e48\u1e4a\u1e4c\u1e4e\u1e50\u1e52\u1e54\u1e56\u1e58\u1e5a\u1e5c\u1e5e\u1e60\u1e62\u1e64\u1e66\u1e68\u1e6a\u1e6c\u1e6e\u1e70\u1e72\u1e74\u1e76\u1e78\u1e7a\u1e7c\u1e7e\u1e80\u1e82\u1e84\u1e86\u1e88\u1e8a\u1e8c\u1e8e\u1e90\u1e92\u1e94\u1e9e\u1ea0\u1ea2\u1ea4\u1ea6\u1ea8\u1eaa\u1eac\u1eae\u1eb0\u1eb2\u1eb4\u1eb6\u1eb8\u1eba\u1ebc\u1ebe\u1ec0\u1ec2\u1ec4\u1ec6\u1ec8\u1eca\u1ecc\u1ece\u1ed0\u1ed2\u1ed4\u1ed6\u1ed8\u1eda\u1edc\u1ede\u1ee0\u1ee2\u1ee4\u1ee6\u1ee8\u1eea\u1eec\u1eee\u1ef0\u1ef2\u1ef4\u1ef6\u1ef8\u1efa\u1efc\u1efe\u1f08-\u1f0f\u1f18-\u1f1d\u1f28-\u1f2f\u1f38-\u1f3f\u1f48-\u1f4d\u1f59\u1f5b\u1f5d\u1f5f\u1f68-\u1f6f\u1fb8-\u1fbb\u1fc8-\u1fcb\u1fd8-\u1fdb\u1fe8-\u1fec\u1ff8-\u1ffb\u2102\u2107\u210b-\u210d\u2110-\u2112\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u2130-\u2133\u213e-\u213f\u2145\u2183\u2c00-\u2c2e\u2c60\u2c62-\u2c64\u2c67\u2c69\u2c6b\u2c6d-\u2c70\u2c72\u2c75\u2c7e-\u2c80\u2c82\u2c84\u2c86\u2c88\u2c8a\u2c8c\u2c8e\u2c90\u2c92\u2c94\u2c96\u2c98\u2c9a\u2c9c\u2c9e\u2ca0\u2ca2\u2ca4\u2ca6\u2ca8\u2caa\u2cac\u2cae\u2cb0\u2cb2\u2cb4\u2cb6\u2cb8\u2cba\u2cbc\u2cbe\u2cc0\u2cc2\u2cc4\u2cc6\u2cc8\u2cca\u2ccc\u2cce\u2cd0\u2cd2\u2cd4\u2cd6\u2cd8\u2cda\u2cdc\u2cde\u2ce0\u2ce2\u2ceb\u2ced\u2cf2\ua640\ua642\ua644\ua646\ua648\ua64a\ua64c\ua64e\ua650\ua652\ua654\ua656\ua658\ua65a\ua65c\ua65e\ua660\ua662\ua664\ua666\ua668\ua66a\ua66c\ua680\ua682\ua684\ua686\ua688\ua68a\ua68c\ua68e\ua690\ua692\ua694\ua696\ua722\ua724\ua726\ua728\ua72a\ua72c\ua72e\ua732\ua734\ua736\ua738\ua73a\ua73c\ua73e\ua740\ua742\ua744\ua746\ua748\ua74a\ua74c\ua74e\ua750\ua752\ua754\ua756\ua758\ua75a\ua75c\ua75e\ua760\ua762\ua764\ua766\ua768\ua76a\ua76c\ua76e\ua779\ua77b\ua77d-\ua77e\ua780\ua782\ua784\ua786\ua78b\ua78d\ua790\ua792\ua7a0\ua7a2\ua7a4\ua7a6\ua7a8\ua7aa\uff21-\uff3aa-z\u00b5\u00df-\u00f6\u00f8-\u00ff\u0101\u0103\u0105\u0107\u0109\u010b\u010d\u010f\u0111\u0113\u0115\u0117\u0119\u011b\u011d\u011f\u0121\u0123\u0125\u0127\u0129\u012b\u012d\u012f\u0131\u0133\u0135\u0137-\u0138\u013a\u013c\u013e\u0140\u0142\u0144\u0146\u0148-\u0149\u014b\u014d\u014f\u0151\u0153\u0155\u0157\u0159\u015b\u015d\u015f\u0161\u0163\u0165\u0167\u0169\u016b\u016d\u016f\u0171\u0173\u0175\u0177\u017a\u017c\u017e-\u0180\u0183\u0185\u0188\u018c-\u018d\u0192\u0195\u0199-\u019b\u019e\u01a1\u01a3\u01a5\u01a8\u01aa-\u01ab\u01ad\u01b0\u01b4\u01b6\u01b9-\u01ba\u01bd-\u01bf\u01c6\u01c9\u01cc\u01ce\u01d0\u01d2\u01d4\u01d6\u01d8\u01da\u01dc-\u01dd\u01df\u01e1\u01e3\u01e5\u01e7\u01e9\u01eb\u01ed\u01ef-\u01f0\u01f3\u01f5\u01f9\u01fb\u01fd\u01ff\u0201\u0203\u0205\u0207\u0209\u020b\u020d\u020f\u0211\u0213\u0215\u0217\u0219\u021b\u021d\u021f\u0221\u0223\u0225\u0227\u0229\u022b\u022d\u022f\u0231\u0233-\u0239\u023c\u023f-\u0240\u0242\u0247\u0249\u024b\u024d\u024f-\u0293\u0295-\u02af\u0371\u0373\u0377\u037b-\u037d\u0390\u03ac-\u03ce\u03d0-\u03d1\u03d5-\u03d7\u03d9\u03db\u03dd\u03df\u03e1\u03e3\u03e5\u03e7\u03e9\u03eb\u03ed\u03ef-\u03f3\u03f5\u03f8\u03fb-\u03fc\u0430-\u045f\u0461\u0463\u0465\u0467\u0469\u046b\u046d\u046f\u0471\u0473\u0475\u0477\u0479\u047b\u047d\u047f\u0481\u048b\u048d\u048f\u0491\u0493\u0495\u0497\u0499\u049b\u049d\u049f\u04a1\u04a3\u04a5\u04a7\u04a9\u04ab\u04ad\u04af\u04b1\u04b3\u04b5\u04b7\u04b9\u04bb\u04bd\u04bf\u04c2\u04c4\u04c6\u04c8\u04ca\u04cc\u04ce-\u04cf\u04d1\u04d3\u04d5\u04d7\u04d9\u04db\u04dd\u04df\u04e1\u04e3\u04e5\u04e7\u04e9\u04eb\u04ed\u04ef\u04f1\u04f3\u04f5\u04f7\u04f9\u04fb\u04fd\u04ff\u0501\u0503\u0505\u0507\u0509\u050b\u050d\u050f\u0511\u0513\u0515\u0517\u0519\u051b\u051d\u051f\u0521\u0523\u0525\u0527\u0561-\u0587\u1d00-\u1d2b\u1d6b-\u1d77\u1d79-\u1d9a\u1e01\u1e03\u1e05\u1e07\u1e09\u1e0b\u1e0d\u1e0f\u1e11\u1e13\u1e15\u1e17\u1e19\u1e1b\u1e1d\u1e1f\u1e21\u1e23\u1e25\u1e27\u1e29\u1e2b\u1e2d\u1e2f\u1e31\u1e33\u1e35\u1e37\u1e39\u1e3b\u1e3d\u1e3f\u1e41\u1e43\u1e45\u1e47\u1e49\u1e4b\u1e4d\u1e4f\u1e51\u1e53\u1e55\u1e57\u1e59\u1e5b\u1e5d\u1e5f\u1e61\u1e63\u1e65\u1e67\u1e69\u1e6b\u1e6d\u1e6f\u1e71\u1e73\u1e75\u1e77\u1e79\u1e7b\u1e7d\u1e7f\u1e81\u1e83\u1e85\u1e87\u1e89\u1e8b\u1e8d\u1e8f\u1e91\u1e93\u1e95-\u1e9d\u1e9f\u1ea1\u1ea3\u1ea5\u1ea7\u1ea9\u1eab\u1ead\u1eaf\u1eb1\u1eb3\u1eb5\u1eb7\u1eb9\u1ebb\u1ebd\u1ebf\u1ec1\u1ec3\u1ec5\u1ec7\u1ec9\u1ecb\u1ecd\u1ecf\u1ed1\u1ed3\u1ed5\u1ed7\u1ed9\u1edb\u1edd\u1edf\u1ee1\u1ee3\u1ee5\u1ee7\u1ee9\u1eeb\u1eed\u1eef\u1ef1\u1ef3\u1ef5\u1ef7\u1ef9\u1efb\u1efd\u1eff-\u1f07\u1f10-\u1f15\u1f20-\u1f27\u1f30-\u1f37\u1f40-\u1f45\u1f50-\u1f57\u1f60-\u1f67\u1f70-\u1f7d\u1f80-\u1f87\u1f90-\u1f97\u1fa0-\u1fa7\u1fb0-\u1fb4\u1fb6-\u1fb7\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fc7\u1fd0-\u1fd3\u1fd6-\u1fd7\u1fe0-\u1fe7\u1ff2-\u1ff4\u1ff6-\u1ff7\u210a\u210e-\u210f\u2113\u212f\u2134\u2139\u213c-\u213d\u2146-\u2149\u214e\u2184\u2c30-\u2c5e\u2c61\u2c65-\u2c66\u2c68\u2c6a\u2c6c\u2c71\u2c73-\u2c74\u2c76-\u2c7b\u2c81\u2c83\u2c85\u2c87\u2c89\u2c8b\u2c8d\u2c8f\u2c91\u2c93\u2c95\u2c97\u2c99\u2c9b\u2c9d\u2c9f\u2ca1\u2ca3\u2ca5\u2ca7\u2ca9\u2cab\u2cad\u2caf\u2cb1\u2cb3\u2cb5\u2cb7\u2cb9\u2cbb\u2cbd\u2cbf\u2cc1\u2cc3\u2cc5\u2cc7\u2cc9\u2ccb\u2ccd\u2ccf\u2cd1\u2cd3\u2cd5\u2cd7\u2cd9\u2cdb\u2cdd\u2cdf\u2ce1\u2ce3-\u2ce4\u2cec\u2cee\u2cf3\u2d00-\u2d25\u2d27\u2d2d\ua641\ua643\ua645\ua647\ua649\ua64b\ua64d\ua64f\ua651\ua653\ua655\ua657\ua659\ua65b\ua65d\ua65f\ua661\ua663\ua665\ua667\ua669\ua66b\ua66d\ua681\ua683\ua685\ua687\ua689\ua68b\ua68d\ua68f\ua691\ua693\ua695\ua697\ua723\ua725\ua727\ua729\ua72b\ua72d\ua72f-\ua731\ua733\ua735\ua737\ua739\ua73b\ua73d\ua73f\ua741\ua743\ua745\ua747\ua749\ua74b\ua74d\ua74f\ua751\ua753\ua755\ua757\ua759\ua75b\ua75d\ua75f\ua761\ua763\ua765\ua767\ua769\ua76b\ua76d\ua76f\ua771-\ua778\ua77a\ua77c\ua77f\ua781\ua783\ua785\ua787\ua78c\ua78e\ua791\ua793\ua7a1\ua7a3\ua7a5\ua7a7\ua7a9\ua7fa\ufb00-\ufb06\ufb13-\ufb17\uff41-\uff5a\u01c5\u01c8\u01cb\u01f2\u1f88-\u1f8f\u1f98-\u1f9f\u1fa8-\u1faf\u1fbc\u1fcc\u1ffc\u02b0-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0374\u037a\u0559\u0640\u06e5-\u06e6\u07f4-\u07f5\u07fa\u081a\u0824\u0828\u0971\u0e46\u0ec6\u10fc\u17d7\u1843\u1aa7\u1c78-\u1c7d\u1d2c-\u1d6a\u1d78\u1d9b-\u1dbf\u2071\u207f\u2090-\u209c\u2c7c-\u2c7d\u2d6f\u2e2f\u3005\u3031-\u3035\u303b\u309d-\u309e\u30fc-\u30fe\ua015\ua4f8-\ua4fd\ua60c\ua67f\ua717-\ua71f\ua770\ua788\ua7f8-\ua7f9\ua9cf\uaa70\uaadd\uaaf3-\uaaf4\uff70\uff9e-\uff9f\u16ee-\u16f0\u2160-\u2182\u2185-\u2188\u3007\u3021-\u3029\u3038-\u303a\ua6e6-\ua6ef][A-Z\u00c0-\u00d6\u00d8-\u00de\u0100\u0102\u0104\u0106\u0108\u010a\u010c\u010e\u0110\u0112\u0114\u0116\u0118\u011a\u011c\u011e\u0120\u0122\u0124\u0126\u0128\u012a\u012c\u012e\u0130\u0132\u0134\u0136\u0139\u013b\u013d\u013f\u0141\u0143\u0145\u0147\u014a\u014c\u014e\u0150\u0152\u0154\u0156\u0158\u015a\u015c\u015e\u0160\u0162\u0164\u0166\u0168\u016a\u016c\u016e\u0170\u0172\u0174\u0176\u0178-\u0179\u017b\u017d\u0181-\u0182\u0184\u0186-\u0187\u0189-\u018b\u018e-\u0191\u0193-\u0194\u0196-\u0198\u019c-\u019d\u019f-\u01a0\u01a2\u01a4\u01a6-\u01a7\u01a9\u01ac\u01ae-\u01af\u01b1-\u01b3\u01b5\u01b7-\u01b8\u01bc\u01c4\u01c7\u01ca\u01cd\u01cf\u01d1\u01d3\u01d5\u01d7\u01d9\u01db\u01de\u01e0\u01e2\u01e4\u01e6\u01e8\u01ea\u01ec\u01ee\u01f1\u01f4\u01f6-\u01f8\u01fa\u01fc\u01fe\u0200\u0202\u0204\u0206\u0208\u020a\u020c\u020e\u0210\u0212\u0214\u0216\u0218\u021a\u021c\u021e\u0220\u0222\u0224\u0226\u0228\u022a\u022c\u022e\u0230\u0232\u023a-\u023b\u023d-\u023e\u0241\u0243-\u0246\u0248\u024a\u024c\u024e\u0370\u0372\u0376\u0386\u0388-\u038a\u038c\u038e-\u038f\u0391-\u03a1\u03a3-\u03ab\u03cf\u03d2-\u03d4\u03d8\u03da\u03dc\u03de\u03e0\u03e2\u03e4\u03e6\u03e8\u03ea\u03ec\u03ee\u03f4\u03f7\u03f9-\u03fa\u03fd-\u042f\u0460\u0462\u0464\u0466\u0468\u046a\u046c\u046e\u0470\u0472\u0474\u0476\u0478\u047a\u047c\u047e\u0480\u048a\u048c\u048e\u0490\u0492\u0494\u0496\u0498\u049a\u049c\u049e\u04a0\u04a2\u04a4\u04a6\u04a8\u04aa\u04ac\u04ae\u04b0\u04b2\u04b4\u04b6\u04b8\u04ba\u04bc\u04be\u04c0-\u04c1\u04c3\u04c5\u04c7\u04c9\u04cb\u04cd\u04d0\u04d2\u04d4\u04d6\u04d8\u04da\u04dc\u04de\u04e0\u04e2\u04e4\u04e6\u04e8\u04ea\u04ec\u04ee\u04f0\u04f2\u04f4\u04f6\u04f8\u04fa\u04fc\u04fe\u0500\u0502\u0504\u0506\u0508\u050a\u050c\u050e\u0510\u0512\u0514\u0516\u0518\u051a\u051c\u051e\u0520\u0522\u0524\u0526\u0531-\u0556\u10a0-\u10c5\u10c7\u10cd\u1e00\u1e02\u1e04\u1e06\u1e08\u1e0a\u1e0c\u1e0e\u1e10\u1e12\u1e14\u1e16\u1e18\u1e1a\u1e1c\u1e1e\u1e20\u1e22\u1e24\u1e26\u1e28\u1e2a\u1e2c\u1e2e\u1e30\u1e32\u1e34\u1e36\u1e38\u1e3a\u1e3c\u1e3e\u1e40\u1e42\u1e44\u1e46\u1e48\u1e4a\u1e4c\u1e4e\u1e50\u1e52\u1e54\u1e56\u1e58\u1e5a\u1e5c\u1e5e\u1e60\u1e62\u1e64\u1e66\u1e68\u1e6a\u1e6c\u1e6e\u1e70\u1e72\u1e74\u1e76\u1e78\u1e7a\u1e7c\u1e7e\u1e80\u1e82\u1e84\u1e86\u1e88\u1e8a\u1e8c\u1e8e\u1e90\u1e92\u1e94\u1e9e\u1ea0\u1ea2\u1ea4\u1ea6\u1ea8\u1eaa\u1eac\u1eae\u1eb0\u1eb2\u1eb4\u1eb6\u1eb8\u1eba\u1ebc\u1ebe\u1ec0\u1ec2\u1ec4\u1ec6\u1ec8\u1eca\u1ecc\u1ece\u1ed0\u1ed2\u1ed4\u1ed6\u1ed8\u1eda\u1edc\u1ede\u1ee0\u1ee2\u1ee4\u1ee6\u1ee8\u1eea\u1eec\u1eee\u1ef0\u1ef2\u1ef4\u1ef6\u1ef8\u1efa\u1efc\u1efe\u1f08-\u1f0f\u1f18-\u1f1d\u1f28-\u1f2f\u1f38-\u1f3f\u1f48-\u1f4d\u1f59\u1f5b\u1f5d\u1f5f\u1f68-\u1f6f\u1fb8-\u1fbb\u1fc8-\u1fcb\u1fd8-\u1fdb\u1fe8-\u1fec\u1ff8-\u1ffb\u2102\u2107\u210b-\u210d\u2110-\u2112\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u2130-\u2133\u213e-\u213f\u2145\u2183\u2c00-\u2c2e\u2c60\u2c62-\u2c64\u2c67\u2c69\u2c6b\u2c6d-\u2c70\u2c72\u2c75\u2c7e-\u2c80\u2c82\u2c84\u2c86\u2c88\u2c8a\u2c8c\u2c8e\u2c90\u2c92\u2c94\u2c96\u2c98\u2c9a\u2c9c\u2c9e\u2ca0\u2ca2\u2ca4\u2ca6\u2ca8\u2caa\u2cac\u2cae\u2cb0\u2cb2\u2cb4\u2cb6\u2cb8\u2cba\u2cbc\u2cbe\u2cc0\u2cc2\u2cc4\u2cc6\u2cc8\u2cca\u2ccc\u2cce\u2cd0\u2cd2\u2cd4\u2cd6\u2cd8\u2cda\u2cdc\u2cde\u2ce0\u2ce2\u2ceb\u2ced\u2cf2\ua640\ua642\ua644\ua646\ua648\ua64a\ua64c\ua64e\ua650\ua652\ua654\ua656\ua658\ua65a\ua65c\ua65e\ua660\ua662\ua664\ua666\ua668\ua66a\ua66c\ua680\ua682\ua684\ua686\ua688\ua68a\ua68c\ua68e\ua690\ua692\ua694\ua696\ua722\ua724\ua726\ua728\ua72a\ua72c\ua72e\ua732\ua734\ua736\ua738\ua73a\ua73c\ua73e\ua740\ua742\ua744\ua746\ua748\ua74a\ua74c\ua74e\ua750\ua752\ua754\ua756\ua758\ua75a\ua75c\ua75e\ua760\ua762\ua764\ua766\ua768\ua76a\ua76c\ua76e\ua779\ua77b\ua77d-\ua77e\ua780\ua782\ua784\ua786\ua78b\ua78d\ua790\ua792\ua7a0\ua7a2\ua7a4\ua7a6\ua7a8\ua7aa\uff21-\uff3aa-z\u00b5\u00df-\u00f6\u00f8-\u00ff\u0101\u0103\u0105\u0107\u0109\u010b\u010d\u010f\u0111\u0113\u0115\u0117\u0119\u011b\u011d\u011f\u0121\u0123\u0125\u0127\u0129\u012b\u012d\u012f\u0131\u0133\u0135\u0137-\u0138\u013a\u013c\u013e\u0140\u0142\u0144\u0146\u0148-\u0149\u014b\u014d\u014f\u0151\u0153\u0155\u0157\u0159\u015b\u015d\u015f\u0161\u0163\u0165\u0167\u0169\u016b\u016d\u016f\u0171\u0173\u0175\u0177\u017a\u017c\u017e-\u0180\u0183\u0185\u0188\u018c-\u018d\u0192\u0195\u0199-\u019b\u019e\u01a1\u01a3\u01a5\u01a8\u01aa-\u01ab\u01ad\u01b0\u01b4\u01b6\u01b9-\u01ba\u01bd-\u01bf\u01c6\u01c9\u01cc\u01ce\u01d0\u01d2\u01d4\u01d6\u01d8\u01da\u01dc-\u01dd\u01df\u01e1\u01e3\u01e5\u01e7\u01e9\u01eb\u01ed\u01ef-\u01f0\u01f3\u01f5\u01f9\u01fb\u01fd\u01ff\u0201\u0203\u0205\u0207\u0209\u020b\u020d\u020f\u0211\u0213\u0215\u0217\u0219\u021b\u021d\u021f\u0221\u0223\u0225\u0227\u0229\u022b\u022d\u022f\u0231\u0233-\u0239\u023c\u023f-\u0240\u0242\u0247\u0249\u024b\u024d\u024f-\u0293\u0295-\u02af\u0371\u0373\u0377\u037b-\u037d\u0390\u03ac-\u03ce\u03d0-\u03d1\u03d5-\u03d7\u03d9\u03db\u03dd\u03df\u03e1\u03e3\u03e5\u03e7\u03e9\u03eb\u03ed\u03ef-\u03f3\u03f5\u03f8\u03fb-\u03fc\u0430-\u045f\u0461\u0463\u0465\u0467\u0469\u046b\u046d\u046f\u0471\u0473\u0475\u0477\u0479\u047b\u047d\u047f\u0481\u048b\u048d\u048f\u0491\u0493\u0495\u0497\u0499\u049b\u049d\u049f\u04a1\u04a3\u04a5\u04a7\u04a9\u04ab\u04ad\u04af\u04b1\u04b3\u04b5\u04b7\u04b9\u04bb\u04bd\u04bf\u04c2\u04c4\u04c6\u04c8\u04ca\u04cc\u04ce-\u04cf\u04d1\u04d3\u04d5\u04d7\u04d9\u04db\u04dd\u04df\u04e1\u04e3\u04e5\u04e7\u04e9\u04eb\u04ed\u04ef\u04f1\u04f3\u04f5\u04f7\u04f9\u04fb\u04fd\u04ff\u0501\u0503\u0505\u0507\u0509\u050b\u050d\u050f\u0511\u0513\u0515\u0517\u0519\u051b\u051d\u051f\u0521\u0523\u0525\u0527\u0561-\u0587\u1d00-\u1d2b\u1d6b-\u1d77\u1d79-\u1d9a\u1e01\u1e03\u1e05\u1e07\u1e09\u1e0b\u1e0d\u1e0f\u1e11\u1e13\u1e15\u1e17\u1e19\u1e1b\u1e1d\u1e1f\u1e21\u1e23\u1e25\u1e27\u1e29\u1e2b\u1e2d\u1e2f\u1e31\u1e33\u1e35\u1e37\u1e39\u1e3b\u1e3d\u1e3f\u1e41\u1e43\u1e45\u1e47\u1e49\u1e4b\u1e4d\u1e4f\u1e51\u1e53\u1e55\u1e57\u1e59\u1e5b\u1e5d\u1e5f\u1e61\u1e63\u1e65\u1e67\u1e69\u1e6b\u1e6d\u1e6f\u1e71\u1e73\u1e75\u1e77\u1e79\u1e7b\u1e7d\u1e7f\u1e81\u1e83\u1e85\u1e87\u1e89\u1e8b\u1e8d\u1e8f\u1e91\u1e93\u1e95-\u1e9d\u1e9f\u1ea1\u1ea3\u1ea5\u1ea7\u1ea9\u1eab\u1ead\u1eaf\u1eb1\u1eb3\u1eb5\u1eb7\u1eb9\u1ebb\u1ebd\u1ebf\u1ec1\u1ec3\u1ec5\u1ec7\u1ec9\u1ecb\u1ecd\u1ecf\u1ed1\u1ed3\u1ed5\u1ed7\u1ed9\u1edb\u1edd\u1edf\u1ee1\u1ee3\u1ee5\u1ee7\u1ee9\u1eeb\u1eed\u1eef\u1ef1\u1ef3\u1ef5\u1ef7\u1ef9\u1efb\u1efd\u1eff-\u1f07\u1f10-\u1f15\u1f20-\u1f27\u1f30-\u1f37\u1f40-\u1f45\u1f50-\u1f57\u1f60-\u1f67\u1f70-\u1f7d\u1f80-\u1f87\u1f90-\u1f97\u1fa0-\u1fa7\u1fb0-\u1fb4\u1fb6-\u1fb7\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fc7\u1fd0-\u1fd3\u1fd6-\u1fd7\u1fe0-\u1fe7\u1ff2-\u1ff4\u1ff6-\u1ff7\u210a\u210e-\u210f\u2113\u212f\u2134\u2139\u213c-\u213d\u2146-\u2149\u214e\u2184\u2c30-\u2c5e\u2c61\u2c65-\u2c66\u2c68\u2c6a\u2c6c\u2c71\u2c73-\u2c74\u2c76-\u2c7b\u2c81\u2c83\u2c85\u2c87\u2c89\u2c8b\u2c8d\u2c8f\u2c91\u2c93\u2c95\u2c97\u2c99\u2c9b\u2c9d\u2c9f\u2ca1\u2ca3\u2ca5\u2ca7\u2ca9\u2cab\u2cad\u2caf\u2cb1\u2cb3\u2cb5\u2cb7\u2cb9\u2cbb\u2cbd\u2cbf\u2cc1\u2cc3\u2cc5\u2cc7\u2cc9\u2ccb\u2ccd\u2ccf\u2cd1\u2cd3\u2cd5\u2cd7\u2cd9\u2cdb\u2cdd\u2cdf\u2ce1\u2ce3-\u2ce4\u2cec\u2cee\u2cf3\u2d00-\u2d25\u2d27\u2d2d\ua641\ua643\ua645\ua647\ua649\ua64b\ua64d\ua64f\ua651\ua653\ua655\ua657\ua659\ua65b\ua65d\ua65f\ua661\ua663\ua665\ua667\ua669\ua66b\ua66d\ua681\ua683\ua685\ua687\ua689\ua68b\ua68d\ua68f\ua691\ua693\ua695\ua697\ua723\ua725\ua727\ua729\ua72b\ua72d\ua72f-\ua731\ua733\ua735\ua737\ua739\ua73b\ua73d\ua73f\ua741\ua743\ua745\ua747\ua749\ua74b\ua74d\ua74f\ua751\ua753\ua755\ua757\ua759\ua75b\ua75d\ua75f\ua761\ua763\ua765\ua767\ua769\ua76b\ua76d\ua76f\ua771-\ua778\ua77a\ua77c\ua77f\ua781\ua783\ua785\ua787\ua78c\ua78e\ua791\ua793\ua7a1\ua7a3\ua7a5\ua7a7\ua7a9\ua7fa\ufb00-\ufb06\ufb13-\ufb17\uff41-\uff5a\u01c5\u01c8\u01cb\u01f2\u1f88-\u1f8f\u1f98-\u1f9f\u1fa8-\u1faf\u1fbc\u1fcc\u1ffc\u02b0-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0374\u037a\u0559\u0640\u06e5-\u06e6\u07f4-\u07f5\u07fa\u081a\u0824\u0828\u0971\u0e46\u0ec6\u10fc\u17d7\u1843\u1aa7\u1c78-\u1c7d\u1d2c-\u1d6a\u1d78\u1d9b-\u1dbf\u2071\u207f\u2090-\u209c\u2c7c-\u2c7d\u2d6f\u2e2f\u3005\u3031-\u3035\u303b\u309d-\u309e\u30fc-\u30fe\ua015\ua4f8-\ua4fd\ua60c\ua67f\ua717-\ua71f\ua770\ua788\ua7f8-\ua7f9\ua9cf\uaa70\uaadd\uaaf3-\uaaf4\uff70\uff9e-\uff9f\u16ee-\u16f0\u2160-\u2182\u2185-\u2188\u3007\u3021-\u3029\u3038-\u303a\ua6e6-\ua6ef0-9\u0660-\u0669\u06f0-\u06f9\u07c0-\u07c9\u0966-\u096f\u09e6-\u09ef\u0a66-\u0a6f\u0ae6-\u0aef\u0b66-\u0b6f\u0be6-\u0bef\u0c66-\u0c6f\u0ce6-\u0cef\u0d66-\u0d6f\u0e50-\u0e59\u0ed0-\u0ed9\u0f20-\u0f29\u1040-\u1049\u1090-\u1099\u17e0-\u17e9\u1810-\u1819\u1946-\u194f\u19d0-\u19d9\u1a80-\u1a89\u1a90-\u1a99\u1b50-\u1b59\u1bb0-\u1bb9\u1c40-\u1c49\u1c50-\u1c59\ua620-\ua629\ua8d0-\ua8d9\ua900-\ua909\ua9d0-\ua9d9\uaa50-\uaa59\uabf0-\uabf9\uff10-\uff19_\u203f-\u2040\u2054\ufe33-\ufe34\ufe4d-\ufe4f\uff3f\u00ad\u0600-\u0604\u061c\u06dd\u070f\u180e\u200b-\u200f\u202a-\u202e\u2060-\u2064\u2066-\u206f\ufeff\ufff9-\ufffb\u0300-\u036f\u0483-\u0487\u0591-\u05bd\u05bf\u05c1-\u05c2\u05c4-\u05c5\u05c7\u0610-\u061a\u064b-\u065f\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7-\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08e4-\u08fe\u0900-\u0902\u093a\u093c\u0941-\u0948\u094d\u0951-\u0957\u0962-\u0963\u0981\u09bc\u09c1-\u09c4\u09cd\u09e2-\u09e3\u0a01-\u0a02\u0a3c\u0a41-\u0a42\u0a47-\u0a48\u0a4b-\u0a4d\u0a51\u0a70-\u0a71\u0a75\u0a81-\u0a82\u0abc\u0ac1-\u0ac5\u0ac7-\u0ac8\u0acd\u0ae2-\u0ae3\u0b01\u0b3c\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b62-\u0b63\u0b82\u0bc0\u0bcd\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55-\u0c56\u0c62-\u0c63\u0cbc\u0cbf\u0cc6\u0ccc-\u0ccd\u0ce2-\u0ce3\u0d41-\u0d44\u0d4d\u0d62-\u0d63\u0dca\u0dd2-\u0dd4\u0dd6\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb-\u0ebc\u0ec8-\u0ecd\u0f18-\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039-\u103a\u103d-\u103e\u1058-\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085-\u1086\u108d\u109d\u135d-\u135f\u1712-\u1714\u1732-\u1734\u1752-\u1753\u1772-\u1773\u17b4-\u17b5\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927-\u1928\u1932\u1939-\u193b\u1a17-\u1a18\u1a1b\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80-\u1b81\u1ba2-\u1ba5\u1ba8-\u1ba9\u1bab\u1be6\u1be8-\u1be9\u1bed\u1bef-\u1bf1\u1c2c-\u1c33\u1c36-\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1cf4\u1dc0-\u1de6\u1dfc-\u1dff\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302d\u3099-\u309a\ua66f\ua674-\ua67d\ua69f\ua6f0-\ua6f1\ua802\ua806\ua80b\ua825-\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31-\uaa32\uaa35-\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7-\uaab8\uaabe-\uaabf\uaac1\uaaec-\uaaed\uaaf6\uabe5\uabe8\uabed\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\u0903\u093b\u093e-\u0940\u0949-\u094c\u094e-\u094f\u0982-\u0983\u09be-\u09c0\u09c7-\u09c8\u09cb-\u09cc\u09d7\u0a03\u0a3e-\u0a40\u0a83\u0abe-\u0ac0\u0ac9\u0acb-\u0acc\u0b02-\u0b03\u0b3e\u0b40\u0b47-\u0b48\u0b4b-\u0b4c\u0b57\u0bbe-\u0bbf\u0bc1-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcc\u0bd7\u0c01-\u0c03\u0c41-\u0c44\u0c82-\u0c83\u0cbe\u0cc0-\u0cc4\u0cc7-\u0cc8\u0cca-\u0ccb\u0cd5-\u0cd6\u0d02-\u0d03\u0d3e-\u0d40\u0d46-\u0d48\u0d4a-\u0d4c\u0d57\u0d82-\u0d83\u0dcf-\u0dd1\u0dd8-\u0ddf\u0df2-\u0df3\u0f3e-\u0f3f\u0f7f\u102b-\u102c\u1031\u1038\u103b-\u103c\u1056-\u1057\u1062-\u1064\u1067-\u106d\u1083-\u1084\u1087-\u108c\u108f\u109a-\u109c\u17b6\u17be-\u17c5\u17c7-\u17c8\u1923-\u1926\u1929-\u192b\u1930-\u1931\u1933-\u1938\u19b0-\u19c0\u19c8-\u19c9\u1a19-\u1a1a\u1a55\u1a57\u1a61\u1a63-\u1a64\u1a6d-\u1a72\u1b04\u1b35\u1b3b\u1b3d-\u1b41\u1b43-\u1b44\u1b82\u1ba1\u1ba6-\u1ba7\u1baa\u1bac-\u1bad\u1be7\u1bea-\u1bec\u1bee\u1bf2-\u1bf3\u1c24-\u1c2b\u1c34-\u1c35\u1ce1\u1cf2-\u1cf3\u302e-\u302f\ua823-\ua824\ua827\ua880-\ua881\ua8b4-\ua8c3\ua952-\ua953\ua983\ua9b4-\ua9b5\ua9ba-\ua9bb\ua9bd-\ua9c0\uaa2f-\uaa30\uaa33-\uaa34\uaa4d\uaa7b\uaaeb\uaaee-\uaaef\uaaf5\uabe3-\uabe4\uabe6-\uabe7\uabe9-\uabea\uabec]*|`@?[_A-Z\u00c0-\u00d6\u00d8-\u00de\u0100\u0102\u0104\u0106\u0108\u010a\u010c\u010e\u0110\u0112\u0114\u0116\u0118\u011a\u011c\u011e\u0120\u0122\u0124\u0126\u0128\u012a\u012c\u012e\u0130\u0132\u0134\u0136\u0139\u013b\u013d\u013f\u0141\u0143\u0145\u0147\u014a\u014c\u014e\u0150\u0152\u0154\u0156\u0158\u015a\u015c\u015e\u0160\u0162\u0164\u0166\u0168\u016a\u016c\u016e\u0170\u0172\u0174\u0176\u0178-\u0179\u017b\u017d\u0181-\u0182\u0184\u0186-\u0187\u0189-\u018b\u018e-\u0191\u0193-\u0194\u0196-\u0198\u019c-\u019d\u019f-\u01a0\u01a2\u01a4\u01a6-\u01a7\u01a9\u01ac\u01ae-\u01af\u01b1-\u01b3\u01b5\u01b7-\u01b8\u01bc\u01c4\u01c7\u01ca\u01cd\u01cf\u01d1\u01d3\u01d5\u01d7\u01d9\u01db\u01de\u01e0\u01e2\u01e4\u01e6\u01e8\u01ea\u01ec\u01ee\u01f1\u01f4\u01f6-\u01f8\u01fa\u01fc\u01fe\u0200\u0202\u0204\u0206\u0208\u020a\u020c\u020e\u0210\u0212\u0214\u0216\u0218\u021a\u021c\u021e\u0220\u0222\u0224\u0226\u0228\u022a\u022c\u022e\u0230\u0232\u023a-\u023b\u023d-\u023e\u0241\u0243-\u0246\u0248\u024a\u024c\u024e\u0370\u0372\u0376\u0386\u0388-\u038a\u038c\u038e-\u038f\u0391-\u03a1\u03a3-\u03ab\u03cf\u03d2-\u03d4\u03d8\u03da\u03dc\u03de\u03e0\u03e2\u03e4\u03e6\u03e8\u03ea\u03ec\u03ee\u03f4\u03f7\u03f9-\u03fa\u03fd-\u042f\u0460\u0462\u0464\u0466\u0468\u046a\u046c\u046e\u0470\u0472\u0474\u0476\u0478\u047a\u047c\u047e\u0480\u048a\u048c\u048e\u0490\u0492\u0494\u0496\u0498\u049a\u049c\u049e\u04a0\u04a2\u04a4\u04a6\u04a8\u04aa\u04ac\u04ae\u04b0\u04b2\u04b4\u04b6\u04b8\u04ba\u04bc\u04be\u04c0-\u04c1\u04c3\u04c5\u04c7\u04c9\u04cb\u04cd\u04d0\u04d2\u04d4\u04d6\u04d8\u04da\u04dc\u04de\u04e0\u04e2\u04e4\u04e6\u04e8\u04ea\u04ec\u04ee\u04f0\u04f2\u04f4\u04f6\u04f8\u04fa\u04fc\u04fe\u0500\u0502\u0504\u0506\u0508\u050a\u050c\u050e\u0510\u0512\u0514\u0516\u0518\u051a\u051c\u051e\u0520\u0522\u0524\u0526\u0531-\u0556\u10a0-\u10c5\u10c7\u10cd\u1e00\u1e02\u1e04\u1e06\u1e08\u1e0a\u1e0c\u1e0e\u1e10\u1e12\u1e14\u1e16\u1e18\u1e1a\u1e1c\u1e1e\u1e20\u1e22\u1e24\u1e26\u1e28\u1e2a\u1e2c\u1e2e\u1e30\u1e32\u1e34\u1e36\u1e38\u1e3a\u1e3c\u1e3e\u1e40\u1e42\u1e44\u1e46\u1e48\u1e4a\u1e4c\u1e4e\u1e50\u1e52\u1e54\u1e56\u1e58\u1e5a\u1e5c\u1e5e\u1e60\u1e62\u1e64\u1e66\u1e68\u1e6a\u1e6c\u1e6e\u1e70\u1e72\u1e74\u1e76\u1e78\u1e7a\u1e7c\u1e7e\u1e80\u1e82\u1e84\u1e86\u1e88\u1e8a\u1e8c\u1e8e\u1e90\u1e92\u1e94\u1e9e\u1ea0\u1ea2\u1ea4\u1ea6\u1ea8\u1eaa\u1eac\u1eae\u1eb0\u1eb2\u1eb4\u1eb6\u1eb8\u1eba\u1ebc\u1ebe\u1ec0\u1ec2\u1ec4\u1ec6\u1ec8\u1eca\u1ecc\u1ece\u1ed0\u1ed2\u1ed4\u1ed6\u1ed8\u1eda\u1edc\u1ede\u1ee0\u1ee2\u1ee4\u1ee6\u1ee8\u1eea\u1eec\u1eee\u1ef0\u1ef2\u1ef4\u1ef6\u1ef8\u1efa\u1efc\u1efe\u1f08-\u1f0f\u1f18-\u1f1d\u1f28-\u1f2f\u1f38-\u1f3f\u1f48-\u1f4d\u1f59\u1f5b\u1f5d\u1f5f\u1f68-\u1f6f\u1fb8-\u1fbb\u1fc8-\u1fcb\u1fd8-\u1fdb\u1fe8-\u1fec\u1ff8-\u1ffb\u2102\u2107\u210b-\u210d\u2110-\u2112\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u2130-\u2133\u213e-\u213f\u2145\u2183\u2c00-\u2c2e\u2c60\u2c62-\u2c64\u2c67\u2c69\u2c6b\u2c6d-\u2c70\u2c72\u2c75\u2c7e-\u2c80\u2c82\u2c84\u2c86\u2c88\u2c8a\u2c8c\u2c8e\u2c90\u2c92\u2c94\u2c96\u2c98\u2c9a\u2c9c\u2c9e\u2ca0\u2ca2\u2ca4\u2ca6\u2ca8\u2caa\u2cac\u2cae\u2cb0\u2cb2\u2cb4\u2cb6\u2cb8\u2cba\u2cbc\u2cbe\u2cc0\u2cc2\u2cc4\u2cc6\u2cc8\u2cca\u2ccc\u2cce\u2cd0\u2cd2\u2cd4\u2cd6\u2cd8\u2cda\u2cdc\u2cde\u2ce0\u2ce2\u2ceb\u2ced\u2cf2\ua640\ua642\ua644\ua646\ua648\ua64a\ua64c\ua64e\ua650\ua652\ua654\ua656\ua658\ua65a\ua65c\ua65e\ua660\ua662\ua664\ua666\ua668\ua66a\ua66c\ua680\ua682\ua684\ua686\ua688\ua68a\ua68c\ua68e\ua690\ua692\ua694\ua696\ua722\ua724\ua726\ua728\ua72a\ua72c\ua72e\ua732\ua734\ua736\ua738\ua73a\ua73c\ua73e\ua740\ua742\ua744\ua746\ua748\ua74a\ua74c\ua74e\ua750\ua752\ua754\ua756\ua758\ua75a\ua75c\ua75e\ua760\ua762\ua764\ua766\ua768\ua76a\ua76c\ua76e\ua779\ua77b\ua77d-\ua77e\ua780\ua782\ua784\ua786\ua78b\ua78d\ua790\ua792\ua7a0\ua7a2\ua7a4\ua7a6\ua7a8\ua7aa\uff21-\uff3aa-z\u00b5\u00df-\u00f6\u00f8-\u00ff\u0101\u0103\u0105\u0107\u0109\u010b\u010d\u010f\u0111\u0113\u0115\u0117\u0119\u011b\u011d\u011f\u0121\u0123\u0125\u0127\u0129\u012b\u012d\u012f\u0131\u0133\u0135\u0137-\u0138\u013a\u013c\u013e\u0140\u0142\u0144\u0146\u0148-\u0149\u014b\u014d\u014f\u0151\u0153\u0155\u0157\u0159\u015b\u015d\u015f\u0161\u0163\u0165\u0167\u0169\u016b\u016d\u016f\u0171\u0173\u0175\u0177\u017a\u017c\u017e-\u0180\u0183\u0185\u0188\u018c-\u018d\u0192\u0195\u0199-\u019b\u019e\u01a1\u01a3\u01a5\u01a8\u01aa-\u01ab\u01ad\u01b0\u01b4\u01b6\u01b9-\u01ba\u01bd-\u01bf\u01c6\u01c9\u01cc\u01ce\u01d0\u01d2\u01d4\u01d6\u01d8\u01da\u01dc-\u01dd\u01df\u01e1\u01e3\u01e5\u01e7\u01e9\u01eb\u01ed\u01ef-\u01f0\u01f3\u01f5\u01f9\u01fb\u01fd\u01ff\u0201\u0203\u0205\u0207\u0209\u020b\u020d\u020f\u0211\u0213\u0215\u0217\u0219\u021b\u021d\u021f\u0221\u0223\u0225\u0227\u0229\u022b\u022d\u022f\u0231\u0233-\u0239\u023c\u023f-\u0240\u0242\u0247\u0249\u024b\u024d\u024f-\u0293\u0295-\u02af\u0371\u0373\u0377\u037b-\u037d\u0390\u03ac-\u03ce\u03d0-\u03d1\u03d5-\u03d7\u03d9\u03db\u03dd\u03df\u03e1\u03e3\u03e5\u03e7\u03e9\u03eb\u03ed\u03ef-\u03f3\u03f5\u03f8\u03fb-\u03fc\u0430-\u045f\u0461\u0463\u0465\u0467\u0469\u046b\u046d\u046f\u0471\u0473\u0475\u0477\u0479\u047b\u047d\u047f\u0481\u048b\u048d\u048f\u0491\u0493\u0495\u0497\u0499\u049b\u049d\u049f\u04a1\u04a3\u04a5\u04a7\u04a9\u04ab\u04ad\u04af\u04b1\u04b3\u04b5\u04b7\u04b9\u04bb\u04bd\u04bf\u04c2\u04c4\u04c6\u04c8\u04ca\u04cc\u04ce-\u04cf\u04d1\u04d3\u04d5\u04d7\u04d9\u04db\u04dd\u04df\u04e1\u04e3\u04e5\u04e7\u04e9\u04eb\u04ed\u04ef\u04f1\u04f3\u04f5\u04f7\u04f9\u04fb\u04fd\u04ff\u0501\u0503\u0505\u0507\u0509\u050b\u050d\u050f\u0511\u0513\u0515\u0517\u0519\u051b\u051d\u051f\u0521\u0523\u0525\u0527\u0561-\u0587\u1d00-\u1d2b\u1d6b-\u1d77\u1d79-\u1d9a\u1e01\u1e03\u1e05\u1e07\u1e09\u1e0b\u1e0d\u1e0f\u1e11\u1e13\u1e15\u1e17\u1e19\u1e1b\u1e1d\u1e1f\u1e21\u1e23\u1e25\u1e27\u1e29\u1e2b\u1e2d\u1e2f\u1e31\u1e33\u1e35\u1e37\u1e39\u1e3b\u1e3d\u1e3f\u1e41\u1e43\u1e45\u1e47\u1e49\u1e4b\u1e4d\u1e4f\u1e51\u1e53\u1e55\u1e57\u1e59\u1e5b\u1e5d\u1e5f\u1e61\u1e63\u1e65\u1e67\u1e69\u1e6b\u1e6d\u1e6f\u1e71\u1e73\u1e75\u1e77\u1e79\u1e7b\u1e7d\u1e7f\u1e81\u1e83\u1e85\u1e87\u1e89\u1e8b\u1e8d\u1e8f\u1e91\u1e93\u1e95-\u1e9d\u1e9f\u1ea1\u1ea3\u1ea5\u1ea7\u1ea9\u1eab\u1ead\u1eaf\u1eb1\u1eb3\u1eb5\u1eb7\u1eb9\u1ebb\u1ebd\u1ebf\u1ec1\u1ec3\u1ec5\u1ec7\u1ec9\u1ecb\u1ecd\u1ecf\u1ed1\u1ed3\u1ed5\u1ed7\u1ed9\u1edb\u1edd\u1edf\u1ee1\u1ee3\u1ee5\u1ee7\u1ee9\u1eeb\u1eed\u1eef\u1ef1\u1ef3\u1ef5\u1ef7\u1ef9\u1efb\u1efd\u1eff-\u1f07\u1f10-\u1f15\u1f20-\u1f27\u1f30-\u1f37\u1f40-\u1f45\u1f50-\u1f57\u1f60-\u1f67\u1f70-\u1f7d\u1f80-\u1f87\u1f90-\u1f97\u1fa0-\u1fa7\u1fb0-\u1fb4\u1fb6-\u1fb7\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fc7\u1fd0-\u1fd3\u1fd6-\u1fd7\u1fe0-\u1fe7\u1ff2-\u1ff4\u1ff6-\u1ff7\u210a\u210e-\u210f\u2113\u212f\u2134\u2139\u213c-\u213d\u2146-\u2149\u214e\u2184\u2c30-\u2c5e\u2c61\u2c65-\u2c66\u2c68\u2c6a\u2c6c\u2c71\u2c73-\u2c74\u2c76-\u2c7b\u2c81\u2c83\u2c85\u2c87\u2c89\u2c8b\u2c8d\u2c8f\u2c91\u2c93\u2c95\u2c97\u2c99\u2c9b\u2c9d\u2c9f\u2ca1\u2ca3\u2ca5\u2ca7\u2ca9\u2cab\u2cad\u2caf\u2cb1\u2cb3\u2cb5\u2cb7\u2cb9\u2cbb\u2cbd\u2cbf\u2cc1\u2cc3\u2cc5\u2cc7\u2cc9\u2ccb\u2ccd\u2ccf\u2cd1\u2cd3\u2cd5\u2cd7\u2cd9\u2cdb\u2cdd\u2cdf\u2ce1\u2ce3-\u2ce4\u2cec\u2cee\u2cf3\u2d00-\u2d25\u2d27\u2d2d\ua641\ua643\ua645\ua647\ua649\ua64b\ua64d\ua64f\ua651\ua653\ua655\ua657\ua659\ua65b\ua65d\ua65f\ua661\ua663\ua665\ua667\ua669\ua66b\ua66d\ua681\ua683\ua685\ua687\ua689\ua68b\ua68d\ua68f\ua691\ua693\ua695\ua697\ua723\ua725\ua727\ua729\ua72b\ua72d\ua72f-\ua731\ua733\ua735\ua737\ua739\ua73b\ua73d\ua73f\ua741\ua743\ua745\ua747\ua749\ua74b\ua74d\ua74f\ua751\ua753\ua755\ua757\ua759\ua75b\ua75d\ua75f\ua761\ua763\ua765\ua767\ua769\ua76b\ua76d\ua76f\ua771-\ua778\ua77a\ua77c\ua77f\ua781\ua783\ua785\ua787\ua78c\ua78e\ua791\ua793\ua7a1\ua7a3\ua7a5\ua7a7\ua7a9\ua7fa\ufb00-\ufb06\ufb13-\ufb17\uff41-\uff5a\u01c5\u01c8\u01cb\u01f2\u1f88-\u1f8f\u1f98-\u1f9f\u1fa8-\u1faf\u1fbc\u1fcc\u1ffc\u02b0-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0374\u037a\u0559\u0640\u06e5-\u06e6\u07f4-\u07f5\u07fa\u081a\u0824\u0828\u0971\u0e46\u0ec6\u10fc\u17d7\u1843\u1aa7\u1c78-\u1c7d\u1d2c-\u1d6a\u1d78\u1d9b-\u1dbf\u2071\u207f\u2090-\u209c\u2c7c-\u2c7d\u2d6f\u2e2f\u3005\u3031-\u3035\u303b\u309d-\u309e\u30fc-\u30fe\ua015\ua4f8-\ua4fd\ua60c\ua67f\ua717-\ua71f\ua770\ua788\ua7f8-\ua7f9\ua9cf\uaa70\uaadd\uaaf3-\uaaf4\uff70\uff9e-\uff9f\u16ee-\u16f0\u2160-\u2182\u2185-\u2188\u3007\u3021-\u3029\u3038-\u303a\ua6e6-\ua6ef][A-Z\u00c0-\u00d6\u00d8-\u00de\u0100\u0102\u0104\u0106\u0108\u010a\u010c\u010e\u0110\u0112\u0114\u0116\u0118\u011a\u011c\u011e\u0120\u0122\u0124\u0126\u0128\u012a\u012c\u012e\u0130\u0132\u0134\u0136\u0139\u013b\u013d\u013f\u0141\u0143\u0145\u0147\u014a\u014c\u014e\u0150\u0152\u0154\u0156\u0158\u015a\u015c\u015e\u0160\u0162\u0164\u0166\u0168\u016a\u016c\u016e\u0170\u0172\u0174\u0176\u0178-\u0179\u017b\u017d\u0181-\u0182\u0184\u0186-\u0187\u0189-\u018b\u018e-\u0191\u0193-\u0194\u0196-\u0198\u019c-\u019d\u019f-\u01a0\u01a2\u01a4\u01a6-\u01a7\u01a9\u01ac\u01ae-\u01af\u01b1-\u01b3\u01b5\u01b7-\u01b8\u01bc\u01c4\u01c7\u01ca\u01cd\u01cf\u01d1\u01d3\u01d5\u01d7\u01d9\u01db\u01de\u01e0\u01e2\u01e4\u01e6\u01e8\u01ea\u01ec\u01ee\u01f1\u01f4\u01f6-\u01f8\u01fa\u01fc\u01fe\u0200\u0202\u0204\u0206\u0208\u020a\u020c\u020e\u0210\u0212\u0214\u0216\u0218\u021a\u021c\u021e\u0220\u0222\u0224\u0226\u0228\u022a\u022c\u022e\u0230\u0232\u023a-\u023b\u023d-\u023e\u0241\u0243-\u0246\u0248\u024a\u024c\u024e\u0370\u0372\u0376\u0386\u0388-\u038a\u038c\u038e-\u038f\u0391-\u03a1\u03a3-\u03ab\u03cf\u03d2-\u03d4\u03d8\u03da\u03dc\u03de\u03e0\u03e2\u03e4\u03e6\u03e8\u03ea\u03ec\u03ee\u03f4\u03f7\u03f9-\u03fa\u03fd-\u042f\u0460\u0462\u0464\u0466\u0468\u046a\u046c\u046e\u0470\u0472\u0474\u0476\u0478\u047a\u047c\u047e\u0480\u048a\u048c\u048e\u0490\u0492\u0494\u0496\u0498\u049a\u049c\u049e\u04a0\u04a2\u04a4\u04a6\u04a8\u04aa\u04ac\u04ae\u04b0\u04b2\u04b4\u04b6\u04b8\u04ba\u04bc\u04be\u04c0-\u04c1\u04c3\u04c5\u04c7\u04c9\u04cb\u04cd\u04d0\u04d2\u04d4\u04d6\u04d8\u04da\u04dc\u04de\u04e0\u04e2\u04e4\u04e6\u04e8\u04ea\u04ec\u04ee\u04f0\u04f2\u04f4\u04f6\u04f8\u04fa\u04fc\u04fe\u0500\u0502\u0504\u0506\u0508\u050a\u050c\u050e\u0510\u0512\u0514\u0516\u0518\u051a\u051c\u051e\u0520\u0522\u0524\u0526\u0531-\u0556\u10a0-\u10c5\u10c7\u10cd\u1e00\u1e02\u1e04\u1e06\u1e08\u1e0a\u1e0c\u1e0e\u1e10\u1e12\u1e14\u1e16\u1e18\u1e1a\u1e1c\u1e1e\u1e20\u1e22\u1e24\u1e26\u1e28\u1e2a\u1e2c\u1e2e\u1e30\u1e32\u1e34\u1e36\u1e38\u1e3a\u1e3c\u1e3e\u1e40\u1e42\u1e44\u1e46\u1e48\u1e4a\u1e4c\u1e4e\u1e50\u1e52\u1e54\u1e56\u1e58\u1e5a\u1e5c\u1e5e\u1e60\u1e62\u1e64\u1e66\u1e68\u1e6a\u1e6c\u1e6e\u1e70\u1e72\u1e74\u1e76\u1e78\u1e7a\u1e7c\u1e7e\u1e80\u1e82\u1e84\u1e86\u1e88\u1e8a\u1e8c\u1e8e\u1e90\u1e92\u1e94\u1e9e\u1ea0\u1ea2\u1ea4\u1ea6\u1ea8\u1eaa\u1eac\u1eae\u1eb0\u1eb2\u1eb4\u1eb6\u1eb8\u1eba\u1ebc\u1ebe\u1ec0\u1ec2\u1ec4\u1ec6\u1ec8\u1eca\u1ecc\u1ece\u1ed0\u1ed2\u1ed4\u1ed6\u1ed8\u1eda\u1edc\u1ede\u1ee0\u1ee2\u1ee4\u1ee6\u1ee8\u1eea\u1eec\u1eee\u1ef0\u1ef2\u1ef4\u1ef6\u1ef8\u1efa\u1efc\u1efe\u1f08-\u1f0f\u1f18-\u1f1d\u1f28-\u1f2f\u1f38-\u1f3f\u1f48-\u1f4d\u1f59\u1f5b\u1f5d\u1f5f\u1f68-\u1f6f\u1fb8-\u1fbb\u1fc8-\u1fcb\u1fd8-\u1fdb\u1fe8-\u1fec\u1ff8-\u1ffb\u2102\u2107\u210b-\u210d\u2110-\u2112\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u2130-\u2133\u213e-\u213f\u2145\u2183\u2c00-\u2c2e\u2c60\u2c62-\u2c64\u2c67\u2c69\u2c6b\u2c6d-\u2c70\u2c72\u2c75\u2c7e-\u2c80\u2c82\u2c84\u2c86\u2c88\u2c8a\u2c8c\u2c8e\u2c90\u2c92\u2c94\u2c96\u2c98\u2c9a\u2c9c\u2c9e\u2ca0\u2ca2\u2ca4\u2ca6\u2ca8\u2caa\u2cac\u2cae\u2cb0\u2cb2\u2cb4\u2cb6\u2cb8\u2cba\u2cbc\u2cbe\u2cc0\u2cc2\u2cc4\u2cc6\u2cc8\u2cca\u2ccc\u2cce\u2cd0\u2cd2\u2cd4\u2cd6\u2cd8\u2cda\u2cdc\u2cde\u2ce0\u2ce2\u2ceb\u2ced\u2cf2\ua640\ua642\ua644\ua646\ua648\ua64a\ua64c\ua64e\ua650\ua652\ua654\ua656\ua658\ua65a\ua65c\ua65e\ua660\ua662\ua664\ua666\ua668\ua66a\ua66c\ua680\ua682\ua684\ua686\ua688\ua68a\ua68c\ua68e\ua690\ua692\ua694\ua696\ua722\ua724\ua726\ua728\ua72a\ua72c\ua72e\ua732\ua734\ua736\ua738\ua73a\ua73c\ua73e\ua740\ua742\ua744\ua746\ua748\ua74a\ua74c\ua74e\ua750\ua752\ua754\ua756\ua758\ua75a\ua75c\ua75e\ua760\ua762\ua764\ua766\ua768\ua76a\ua76c\ua76e\ua779\ua77b\ua77d-\ua77e\ua780\ua782\ua784\ua786\ua78b\ua78d\ua790\ua792\ua7a0\ua7a2\ua7a4\ua7a6\ua7a8\ua7aa\uff21-\uff3aa-z\u00b5\u00df-\u00f6\u00f8-\u00ff\u0101\u0103\u0105\u0107\u0109\u010b\u010d\u010f\u0111\u0113\u0115\u0117\u0119\u011b\u011d\u011f\u0121\u0123\u0125\u0127\u0129\u012b\u012d\u012f\u0131\u0133\u0135\u0137-\u0138\u013a\u013c\u013e\u0140\u0142\u0144\u0146\u0148-\u0149\u014b\u014d\u014f\u0151\u0153\u0155\u0157\u0159\u015b\u015d\u015f\u0161\u0163\u0165\u0167\u0169\u016b\u016d\u016f\u0171\u0173\u0175\u0177\u017a\u017c\u017e-\u0180\u0183\u0185\u0188\u018c-\u018d\u0192\u0195\u0199-\u019b\u019e\u01a1\u01a3\u01a5\u01a8\u01aa-\u01ab\u01ad\u01b0\u01b4\u01b6\u01b9-\u01ba\u01bd-\u01bf\u01c6\u01c9\u01cc\u01ce\u01d0\u01d2\u01d4\u01d6\u01d8\u01da\u01dc-\u01dd\u01df\u01e1\u01e3\u01e5\u01e7\u01e9\u01eb\u01ed\u01ef-\u01f0\u01f3\u01f5\u01f9\u01fb\u01fd\u01ff\u0201\u0203\u0205\u0207\u0209\u020b\u020d\u020f\u0211\u0213\u0215\u0217\u0219\u021b\u021d\u021f\u0221\u0223\u0225\u0227\u0229\u022b\u022d\u022f\u0231\u0233-\u0239\u023c\u023f-\u0240\u0242\u0247\u0249\u024b\u024d\u024f-\u0293\u0295-\u02af\u0371\u0373\u0377\u037b-\u037d\u0390\u03ac-\u03ce\u03d0-\u03d1\u03d5-\u03d7\u03d9\u03db\u03dd\u03df\u03e1\u03e3\u03e5\u03e7\u03e9\u03eb\u03ed\u03ef-\u03f3\u03f5\u03f8\u03fb-\u03fc\u0430-\u045f\u0461\u0463\u0465\u0467\u0469\u046b\u046d\u046f\u0471\u0473\u0475\u0477\u0479\u047b\u047d\u047f\u0481\u048b\u048d\u048f\u0491\u0493\u0495\u0497\u0499\u049b\u049d\u049f\u04a1\u04a3\u04a5\u04a7\u04a9\u04ab\u04ad\u04af\u04b1\u04b3\u04b5\u04b7\u04b9\u04bb\u04bd\u04bf\u04c2\u04c4\u04c6\u04c8\u04ca\u04cc\u04ce-\u04cf\u04d1\u04d3\u04d5\u04d7\u04d9\u04db\u04dd\u04df\u04e1\u04e3\u04e5\u04e7\u04e9\u04eb\u04ed\u04ef\u04f1\u04f3\u04f5\u04f7\u04f9\u04fb\u04fd\u04ff\u0501\u0503\u0505\u0507\u0509\u050b\u050d\u050f\u0511\u0513\u0515\u0517\u0519\u051b\u051d\u051f\u0521\u0523\u0525\u0527\u0561-\u0587\u1d00-\u1d2b\u1d6b-\u1d77\u1d79-\u1d9a\u1e01\u1e03\u1e05\u1e07\u1e09\u1e0b\u1e0d\u1e0f\u1e11\u1e13\u1e15\u1e17\u1e19\u1e1b\u1e1d\u1e1f\u1e21\u1e23\u1e25\u1e27\u1e29\u1e2b\u1e2d\u1e2f\u1e31\u1e33\u1e35\u1e37\u1e39\u1e3b\u1e3d\u1e3f\u1e41\u1e43\u1e45\u1e47\u1e49\u1e4b\u1e4d\u1e4f\u1e51\u1e53\u1e55\u1e57\u1e59\u1e5b\u1e5d\u1e5f\u1e61\u1e63\u1e65\u1e67\u1e69\u1e6b\u1e6d\u1e6f\u1e71\u1e73\u1e75\u1e77\u1e79\u1e7b\u1e7d\u1e7f\u1e81\u1e83\u1e85\u1e87\u1e89\u1e8b\u1e8d\u1e8f\u1e91\u1e93\u1e95-\u1e9d\u1e9f\u1ea1\u1ea3\u1ea5\u1ea7\u1ea9\u1eab\u1ead\u1eaf\u1eb1\u1eb3\u1eb5\u1eb7\u1eb9\u1ebb\u1ebd\u1ebf\u1ec1\u1ec3\u1ec5\u1ec7\u1ec9\u1ecb\u1ecd\u1ecf\u1ed1\u1ed3\u1ed5\u1ed7\u1ed9\u1edb\u1edd\u1edf\u1ee1\u1ee3\u1ee5\u1ee7\u1ee9\u1eeb\u1eed\u1eef\u1ef1\u1ef3\u1ef5\u1ef7\u1ef9\u1efb\u1efd\u1eff-\u1f07\u1f10-\u1f15\u1f20-\u1f27\u1f30-\u1f37\u1f40-\u1f45\u1f50-\u1f57\u1f60-\u1f67\u1f70-\u1f7d\u1f80-\u1f87\u1f90-\u1f97\u1fa0-\u1fa7\u1fb0-\u1fb4\u1fb6-\u1fb7\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fc7\u1fd0-\u1fd3\u1fd6-\u1fd7\u1fe0-\u1fe7\u1ff2-\u1ff4\u1ff6-\u1ff7\u210a\u210e-\u210f\u2113\u212f\u2134\u2139\u213c-\u213d\u2146-\u2149\u214e\u2184\u2c30-\u2c5e\u2c61\u2c65-\u2c66\u2c68\u2c6a\u2c6c\u2c71\u2c73-\u2c74\u2c76-\u2c7b\u2c81\u2c83\u2c85\u2c87\u2c89\u2c8b\u2c8d\u2c8f\u2c91\u2c93\u2c95\u2c97\u2c99\u2c9b\u2c9d\u2c9f\u2ca1\u2ca3\u2ca5\u2ca7\u2ca9\u2cab\u2cad\u2caf\u2cb1\u2cb3\u2cb5\u2cb7\u2cb9\u2cbb\u2cbd\u2cbf\u2cc1\u2cc3\u2cc5\u2cc7\u2cc9\u2ccb\u2ccd\u2ccf\u2cd1\u2cd3\u2cd5\u2cd7\u2cd9\u2cdb\u2cdd\u2cdf\u2ce1\u2ce3-\u2ce4\u2cec\u2cee\u2cf3\u2d00-\u2d25\u2d27\u2d2d\ua641\ua643\ua645\ua647\ua649\ua64b\ua64d\ua64f\ua651\ua653\ua655\ua657\ua659\ua65b\ua65d\ua65f\ua661\ua663\ua665\ua667\ua669\ua66b\ua66d\ua681\ua683\ua685\ua687\ua689\ua68b\ua68d\ua68f\ua691\ua693\ua695\ua697\ua723\ua725\ua727\ua729\ua72b\ua72d\ua72f-\ua731\ua733\ua735\ua737\ua739\ua73b\ua73d\ua73f\ua741\ua743\ua745\ua747\ua749\ua74b\ua74d\ua74f\ua751\ua753\ua755\ua757\ua759\ua75b\ua75d\ua75f\ua761\ua763\ua765\ua767\ua769\ua76b\ua76d\ua76f\ua771-\ua778\ua77a\ua77c\ua77f\ua781\ua783\ua785\ua787\ua78c\ua78e\ua791\ua793\ua7a1\ua7a3\ua7a5\ua7a7\ua7a9\ua7fa\ufb00-\ufb06\ufb13-\ufb17\uff41-\uff5a\u01c5\u01c8\u01cb\u01f2\u1f88-\u1f8f\u1f98-\u1f9f\u1fa8-\u1faf\u1fbc\u1fcc\u1ffc\u02b0-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0374\u037a\u0559\u0640\u06e5-\u06e6\u07f4-\u07f5\u07fa\u081a\u0824\u0828\u0971\u0e46\u0ec6\u10fc\u17d7\u1843\u1aa7\u1c78-\u1c7d\u1d2c-\u1d6a\u1d78\u1d9b-\u1dbf\u2071\u207f\u2090-\u209c\u2c7c-\u2c7d\u2d6f\u2e2f\u3005\u3031-\u3035\u303b\u309d-\u309e\u30fc-\u30fe\ua015\ua4f8-\ua4fd\ua60c\ua67f\ua717-\ua71f\ua770\ua788\ua7f8-\ua7f9\ua9cf\uaa70\uaadd\uaaf3-\uaaf4\uff70\uff9e-\uff9f\u16ee-\u16f0\u2160-\u2182\u2185-\u2188\u3007\u3021-\u3029\u3038-\u303a\ua6e6-\ua6ef0-9\u0660-\u0669\u06f0-\u06f9\u07c0-\u07c9\u0966-\u096f\u09e6-\u09ef\u0a66-\u0a6f\u0ae6-\u0aef\u0b66-\u0b6f\u0be6-\u0bef\u0c66-\u0c6f\u0ce6-\u0cef\u0d66-\u0d6f\u0e50-\u0e59\u0ed0-\u0ed9\u0f20-\u0f29\u1040-\u1049\u1090-\u1099\u17e0-\u17e9\u1810-\u1819\u1946-\u194f\u19d0-\u19d9\u1a80-\u1a89\u1a90-\u1a99\u1b50-\u1b59\u1bb0-\u1bb9\u1c40-\u1c49\u1c50-\u1c59\ua620-\ua629\ua8d0-\ua8d9\ua900-\ua909\ua9d0-\ua9d9\uaa50-\uaa59\uabf0-\uabf9\uff10-\uff19_\u203f-\u2040\u2054\ufe33-\ufe34\ufe4d-\ufe4f\uff3f\u00ad\u0600-\u0604\u061c\u06dd\u070f\u180e\u200b-\u200f\u202a-\u202e\u2060-\u2064\u2066-\u206f\ufeff\ufff9-\ufffb\u0300-\u036f\u0483-\u0487\u0591-\u05bd\u05bf\u05c1-\u05c2\u05c4-\u05c5\u05c7\u0610-\u061a\u064b-\u065f\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7-\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08e4-\u08fe\u0900-\u0902\u093a\u093c\u0941-\u0948\u094d\u0951-\u0957\u0962-\u0963\u0981\u09bc\u09c1-\u09c4\u09cd\u09e2-\u09e3\u0a01-\u0a02\u0a3c\u0a41-\u0a42\u0a47-\u0a48\u0a4b-\u0a4d\u0a51\u0a70-\u0a71\u0a75\u0a81-\u0a82\u0abc\u0ac1-\u0ac5\u0ac7-\u0ac8\u0acd\u0ae2-\u0ae3\u0b01\u0b3c\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b62-\u0b63\u0b82\u0bc0\u0bcd\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55-\u0c56\u0c62-\u0c63\u0cbc\u0cbf\u0cc6\u0ccc-\u0ccd\u0ce2-\u0ce3\u0d41-\u0d44\u0d4d\u0d62-\u0d63\u0dca\u0dd2-\u0dd4\u0dd6\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb-\u0ebc\u0ec8-\u0ecd\u0f18-\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039-\u103a\u103d-\u103e\u1058-\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085-\u1086\u108d\u109d\u135d-\u135f\u1712-\u1714\u1732-\u1734\u1752-\u1753\u1772-\u1773\u17b4-\u17b5\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927-\u1928\u1932\u1939-\u193b\u1a17-\u1a18\u1a1b\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80-\u1b81\u1ba2-\u1ba5\u1ba8-\u1ba9\u1bab\u1be6\u1be8-\u1be9\u1bed\u1bef-\u1bf1\u1c2c-\u1c33\u1c36-\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1cf4\u1dc0-\u1de6\u1dfc-\u1dff\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302d\u3099-\u309a\ua66f\ua674-\ua67d\ua69f\ua6f0-\ua6f1\ua802\ua806\ua80b\ua825-\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31-\uaa32\uaa35-\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7-\uaab8\uaabe-\uaabf\uaac1\uaaec-\uaaed\uaaf6\uabe5\uabe8\uabed\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\u0903\u093b\u093e-\u0940\u0949-\u094c\u094e-\u094f\u0982-\u0983\u09be-\u09c0\u09c7-\u09c8\u09cb-\u09cc\u09d7\u0a03\u0a3e-\u0a40\u0a83\u0abe-\u0ac0\u0ac9\u0acb-\u0acc\u0b02-\u0b03\u0b3e\u0b40\u0b47-\u0b48\u0b4b-\u0b4c\u0b57\u0bbe-\u0bbf\u0bc1-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcc\u0bd7\u0c01-\u0c03\u0c41-\u0c44\u0c82-\u0c83\u0cbe\u0cc0-\u0cc4\u0cc7-\u0cc8\u0cca-\u0ccb\u0cd5-\u0cd6\u0d02-\u0d03\u0d3e-\u0d40\u0d46-\u0d48\u0d4a-\u0d4c\u0d57\u0d82-\u0d83\u0dcf-\u0dd1\u0dd8-\u0ddf\u0df2-\u0df3\u0f3e-\u0f3f\u0f7f\u102b-\u102c\u1031\u1038\u103b-\u103c\u1056-\u1057\u1062-\u1064\u1067-\u106d\u1083-\u1084\u1087-\u108c\u108f\u109a-\u109c\u17b6\u17be-\u17c5\u17c7-\u17c8\u1923-\u1926\u1929-\u192b\u1930-\u1931\u1933-\u1938\u19b0-\u19c0\u19c8-\u19c9\u1a19-\u1a1a\u1a55\u1a57\u1a61\u1a63-\u1a64\u1a6d-\u1a72\u1b04\u1b35\u1b3b\u1b3d-\u1b41\u1b43-\u1b44\u1b82\u1ba1\u1ba6-\u1ba7\u1baa\u1bac-\u1bad\u1be7\u1bea-\u1bec\u1bee\u1bf2-\u1bf3\u1c24-\u1c2b\u1c34-\u1c35\u1ce1\u1cf2-\u1cf3\u302e-\u302f\ua823-\ua824\ua827\ua880-\ua881\ua8b4-\ua8c3\ua952-\ua953\ua983\ua9b4-\ua9b5\ua9ba-\ua9bb\ua9bd-\ua9c0\uaa2f-\uaa30\uaa33-\uaa34\uaa4d\uaa7b\uaaeb\uaaee-\uaaef\uaaf5\uabe3-\uabe4\uabe6-\uabe7\uabe9-\uabea\uabec" - // Kotlin lexer. -var Kotlin = internal.Register(MustNewLexer( +var Kotlin = internal.Register(MustNewLazyLexer( &Config{ Name: "Kotlin", Aliases: []string{"kotlin"}, @@ -16,7 +14,13 @@ var Kotlin = internal.Register(MustNewLexer( MimeTypes: []string{"text/x-kotlin"}, DotAll: true, }, - Rules{ + kotlinRules, +)) + +func kotlinRules() Rules { + const kotlinIdentifier = "_A-Z\u00c0-\u00d6\u00d8-\u00de\u0100\u0102\u0104\u0106\u0108\u010a\u010c\u010e\u0110\u0112\u0114\u0116\u0118\u011a\u011c\u011e\u0120\u0122\u0124\u0126\u0128\u012a\u012c\u012e\u0130\u0132\u0134\u0136\u0139\u013b\u013d\u013f\u0141\u0143\u0145\u0147\u014a\u014c\u014e\u0150\u0152\u0154\u0156\u0158\u015a\u015c\u015e\u0160\u0162\u0164\u0166\u0168\u016a\u016c\u016e\u0170\u0172\u0174\u0176\u0178-\u0179\u017b\u017d\u0181-\u0182\u0184\u0186-\u0187\u0189-\u018b\u018e-\u0191\u0193-\u0194\u0196-\u0198\u019c-\u019d\u019f-\u01a0\u01a2\u01a4\u01a6-\u01a7\u01a9\u01ac\u01ae-\u01af\u01b1-\u01b3\u01b5\u01b7-\u01b8\u01bc\u01c4\u01c7\u01ca\u01cd\u01cf\u01d1\u01d3\u01d5\u01d7\u01d9\u01db\u01de\u01e0\u01e2\u01e4\u01e6\u01e8\u01ea\u01ec\u01ee\u01f1\u01f4\u01f6-\u01f8\u01fa\u01fc\u01fe\u0200\u0202\u0204\u0206\u0208\u020a\u020c\u020e\u0210\u0212\u0214\u0216\u0218\u021a\u021c\u021e\u0220\u0222\u0224\u0226\u0228\u022a\u022c\u022e\u0230\u0232\u023a-\u023b\u023d-\u023e\u0241\u0243-\u0246\u0248\u024a\u024c\u024e\u0370\u0372\u0376\u0386\u0388-\u038a\u038c\u038e-\u038f\u0391-\u03a1\u03a3-\u03ab\u03cf\u03d2-\u03d4\u03d8\u03da\u03dc\u03de\u03e0\u03e2\u03e4\u03e6\u03e8\u03ea\u03ec\u03ee\u03f4\u03f7\u03f9-\u03fa\u03fd-\u042f\u0460\u0462\u0464\u0466\u0468\u046a\u046c\u046e\u0470\u0472\u0474\u0476\u0478\u047a\u047c\u047e\u0480\u048a\u048c\u048e\u0490\u0492\u0494\u0496\u0498\u049a\u049c\u049e\u04a0\u04a2\u04a4\u04a6\u04a8\u04aa\u04ac\u04ae\u04b0\u04b2\u04b4\u04b6\u04b8\u04ba\u04bc\u04be\u04c0-\u04c1\u04c3\u04c5\u04c7\u04c9\u04cb\u04cd\u04d0\u04d2\u04d4\u04d6\u04d8\u04da\u04dc\u04de\u04e0\u04e2\u04e4\u04e6\u04e8\u04ea\u04ec\u04ee\u04f0\u04f2\u04f4\u04f6\u04f8\u04fa\u04fc\u04fe\u0500\u0502\u0504\u0506\u0508\u050a\u050c\u050e\u0510\u0512\u0514\u0516\u0518\u051a\u051c\u051e\u0520\u0522\u0524\u0526\u0531-\u0556\u10a0-\u10c5\u10c7\u10cd\u1e00\u1e02\u1e04\u1e06\u1e08\u1e0a\u1e0c\u1e0e\u1e10\u1e12\u1e14\u1e16\u1e18\u1e1a\u1e1c\u1e1e\u1e20\u1e22\u1e24\u1e26\u1e28\u1e2a\u1e2c\u1e2e\u1e30\u1e32\u1e34\u1e36\u1e38\u1e3a\u1e3c\u1e3e\u1e40\u1e42\u1e44\u1e46\u1e48\u1e4a\u1e4c\u1e4e\u1e50\u1e52\u1e54\u1e56\u1e58\u1e5a\u1e5c\u1e5e\u1e60\u1e62\u1e64\u1e66\u1e68\u1e6a\u1e6c\u1e6e\u1e70\u1e72\u1e74\u1e76\u1e78\u1e7a\u1e7c\u1e7e\u1e80\u1e82\u1e84\u1e86\u1e88\u1e8a\u1e8c\u1e8e\u1e90\u1e92\u1e94\u1e9e\u1ea0\u1ea2\u1ea4\u1ea6\u1ea8\u1eaa\u1eac\u1eae\u1eb0\u1eb2\u1eb4\u1eb6\u1eb8\u1eba\u1ebc\u1ebe\u1ec0\u1ec2\u1ec4\u1ec6\u1ec8\u1eca\u1ecc\u1ece\u1ed0\u1ed2\u1ed4\u1ed6\u1ed8\u1eda\u1edc\u1ede\u1ee0\u1ee2\u1ee4\u1ee6\u1ee8\u1eea\u1eec\u1eee\u1ef0\u1ef2\u1ef4\u1ef6\u1ef8\u1efa\u1efc\u1efe\u1f08-\u1f0f\u1f18-\u1f1d\u1f28-\u1f2f\u1f38-\u1f3f\u1f48-\u1f4d\u1f59\u1f5b\u1f5d\u1f5f\u1f68-\u1f6f\u1fb8-\u1fbb\u1fc8-\u1fcb\u1fd8-\u1fdb\u1fe8-\u1fec\u1ff8-\u1ffb\u2102\u2107\u210b-\u210d\u2110-\u2112\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u2130-\u2133\u213e-\u213f\u2145\u2183\u2c00-\u2c2e\u2c60\u2c62-\u2c64\u2c67\u2c69\u2c6b\u2c6d-\u2c70\u2c72\u2c75\u2c7e-\u2c80\u2c82\u2c84\u2c86\u2c88\u2c8a\u2c8c\u2c8e\u2c90\u2c92\u2c94\u2c96\u2c98\u2c9a\u2c9c\u2c9e\u2ca0\u2ca2\u2ca4\u2ca6\u2ca8\u2caa\u2cac\u2cae\u2cb0\u2cb2\u2cb4\u2cb6\u2cb8\u2cba\u2cbc\u2cbe\u2cc0\u2cc2\u2cc4\u2cc6\u2cc8\u2cca\u2ccc\u2cce\u2cd0\u2cd2\u2cd4\u2cd6\u2cd8\u2cda\u2cdc\u2cde\u2ce0\u2ce2\u2ceb\u2ced\u2cf2\ua640\ua642\ua644\ua646\ua648\ua64a\ua64c\ua64e\ua650\ua652\ua654\ua656\ua658\ua65a\ua65c\ua65e\ua660\ua662\ua664\ua666\ua668\ua66a\ua66c\ua680\ua682\ua684\ua686\ua688\ua68a\ua68c\ua68e\ua690\ua692\ua694\ua696\ua722\ua724\ua726\ua728\ua72a\ua72c\ua72e\ua732\ua734\ua736\ua738\ua73a\ua73c\ua73e\ua740\ua742\ua744\ua746\ua748\ua74a\ua74c\ua74e\ua750\ua752\ua754\ua756\ua758\ua75a\ua75c\ua75e\ua760\ua762\ua764\ua766\ua768\ua76a\ua76c\ua76e\ua779\ua77b\ua77d-\ua77e\ua780\ua782\ua784\ua786\ua78b\ua78d\ua790\ua792\ua7a0\ua7a2\ua7a4\ua7a6\ua7a8\ua7aa\uff21-\uff3aa-z\u00b5\u00df-\u00f6\u00f8-\u00ff\u0101\u0103\u0105\u0107\u0109\u010b\u010d\u010f\u0111\u0113\u0115\u0117\u0119\u011b\u011d\u011f\u0121\u0123\u0125\u0127\u0129\u012b\u012d\u012f\u0131\u0133\u0135\u0137-\u0138\u013a\u013c\u013e\u0140\u0142\u0144\u0146\u0148-\u0149\u014b\u014d\u014f\u0151\u0153\u0155\u0157\u0159\u015b\u015d\u015f\u0161\u0163\u0165\u0167\u0169\u016b\u016d\u016f\u0171\u0173\u0175\u0177\u017a\u017c\u017e-\u0180\u0183\u0185\u0188\u018c-\u018d\u0192\u0195\u0199-\u019b\u019e\u01a1\u01a3\u01a5\u01a8\u01aa-\u01ab\u01ad\u01b0\u01b4\u01b6\u01b9-\u01ba\u01bd-\u01bf\u01c6\u01c9\u01cc\u01ce\u01d0\u01d2\u01d4\u01d6\u01d8\u01da\u01dc-\u01dd\u01df\u01e1\u01e3\u01e5\u01e7\u01e9\u01eb\u01ed\u01ef-\u01f0\u01f3\u01f5\u01f9\u01fb\u01fd\u01ff\u0201\u0203\u0205\u0207\u0209\u020b\u020d\u020f\u0211\u0213\u0215\u0217\u0219\u021b\u021d\u021f\u0221\u0223\u0225\u0227\u0229\u022b\u022d\u022f\u0231\u0233-\u0239\u023c\u023f-\u0240\u0242\u0247\u0249\u024b\u024d\u024f-\u0293\u0295-\u02af\u0371\u0373\u0377\u037b-\u037d\u0390\u03ac-\u03ce\u03d0-\u03d1\u03d5-\u03d7\u03d9\u03db\u03dd\u03df\u03e1\u03e3\u03e5\u03e7\u03e9\u03eb\u03ed\u03ef-\u03f3\u03f5\u03f8\u03fb-\u03fc\u0430-\u045f\u0461\u0463\u0465\u0467\u0469\u046b\u046d\u046f\u0471\u0473\u0475\u0477\u0479\u047b\u047d\u047f\u0481\u048b\u048d\u048f\u0491\u0493\u0495\u0497\u0499\u049b\u049d\u049f\u04a1\u04a3\u04a5\u04a7\u04a9\u04ab\u04ad\u04af\u04b1\u04b3\u04b5\u04b7\u04b9\u04bb\u04bd\u04bf\u04c2\u04c4\u04c6\u04c8\u04ca\u04cc\u04ce-\u04cf\u04d1\u04d3\u04d5\u04d7\u04d9\u04db\u04dd\u04df\u04e1\u04e3\u04e5\u04e7\u04e9\u04eb\u04ed\u04ef\u04f1\u04f3\u04f5\u04f7\u04f9\u04fb\u04fd\u04ff\u0501\u0503\u0505\u0507\u0509\u050b\u050d\u050f\u0511\u0513\u0515\u0517\u0519\u051b\u051d\u051f\u0521\u0523\u0525\u0527\u0561-\u0587\u1d00-\u1d2b\u1d6b-\u1d77\u1d79-\u1d9a\u1e01\u1e03\u1e05\u1e07\u1e09\u1e0b\u1e0d\u1e0f\u1e11\u1e13\u1e15\u1e17\u1e19\u1e1b\u1e1d\u1e1f\u1e21\u1e23\u1e25\u1e27\u1e29\u1e2b\u1e2d\u1e2f\u1e31\u1e33\u1e35\u1e37\u1e39\u1e3b\u1e3d\u1e3f\u1e41\u1e43\u1e45\u1e47\u1e49\u1e4b\u1e4d\u1e4f\u1e51\u1e53\u1e55\u1e57\u1e59\u1e5b\u1e5d\u1e5f\u1e61\u1e63\u1e65\u1e67\u1e69\u1e6b\u1e6d\u1e6f\u1e71\u1e73\u1e75\u1e77\u1e79\u1e7b\u1e7d\u1e7f\u1e81\u1e83\u1e85\u1e87\u1e89\u1e8b\u1e8d\u1e8f\u1e91\u1e93\u1e95-\u1e9d\u1e9f\u1ea1\u1ea3\u1ea5\u1ea7\u1ea9\u1eab\u1ead\u1eaf\u1eb1\u1eb3\u1eb5\u1eb7\u1eb9\u1ebb\u1ebd\u1ebf\u1ec1\u1ec3\u1ec5\u1ec7\u1ec9\u1ecb\u1ecd\u1ecf\u1ed1\u1ed3\u1ed5\u1ed7\u1ed9\u1edb\u1edd\u1edf\u1ee1\u1ee3\u1ee5\u1ee7\u1ee9\u1eeb\u1eed\u1eef\u1ef1\u1ef3\u1ef5\u1ef7\u1ef9\u1efb\u1efd\u1eff-\u1f07\u1f10-\u1f15\u1f20-\u1f27\u1f30-\u1f37\u1f40-\u1f45\u1f50-\u1f57\u1f60-\u1f67\u1f70-\u1f7d\u1f80-\u1f87\u1f90-\u1f97\u1fa0-\u1fa7\u1fb0-\u1fb4\u1fb6-\u1fb7\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fc7\u1fd0-\u1fd3\u1fd6-\u1fd7\u1fe0-\u1fe7\u1ff2-\u1ff4\u1ff6-\u1ff7\u210a\u210e-\u210f\u2113\u212f\u2134\u2139\u213c-\u213d\u2146-\u2149\u214e\u2184\u2c30-\u2c5e\u2c61\u2c65-\u2c66\u2c68\u2c6a\u2c6c\u2c71\u2c73-\u2c74\u2c76-\u2c7b\u2c81\u2c83\u2c85\u2c87\u2c89\u2c8b\u2c8d\u2c8f\u2c91\u2c93\u2c95\u2c97\u2c99\u2c9b\u2c9d\u2c9f\u2ca1\u2ca3\u2ca5\u2ca7\u2ca9\u2cab\u2cad\u2caf\u2cb1\u2cb3\u2cb5\u2cb7\u2cb9\u2cbb\u2cbd\u2cbf\u2cc1\u2cc3\u2cc5\u2cc7\u2cc9\u2ccb\u2ccd\u2ccf\u2cd1\u2cd3\u2cd5\u2cd7\u2cd9\u2cdb\u2cdd\u2cdf\u2ce1\u2ce3-\u2ce4\u2cec\u2cee\u2cf3\u2d00-\u2d25\u2d27\u2d2d\ua641\ua643\ua645\ua647\ua649\ua64b\ua64d\ua64f\ua651\ua653\ua655\ua657\ua659\ua65b\ua65d\ua65f\ua661\ua663\ua665\ua667\ua669\ua66b\ua66d\ua681\ua683\ua685\ua687\ua689\ua68b\ua68d\ua68f\ua691\ua693\ua695\ua697\ua723\ua725\ua727\ua729\ua72b\ua72d\ua72f-\ua731\ua733\ua735\ua737\ua739\ua73b\ua73d\ua73f\ua741\ua743\ua745\ua747\ua749\ua74b\ua74d\ua74f\ua751\ua753\ua755\ua757\ua759\ua75b\ua75d\ua75f\ua761\ua763\ua765\ua767\ua769\ua76b\ua76d\ua76f\ua771-\ua778\ua77a\ua77c\ua77f\ua781\ua783\ua785\ua787\ua78c\ua78e\ua791\ua793\ua7a1\ua7a3\ua7a5\ua7a7\ua7a9\ua7fa\ufb00-\ufb06\ufb13-\ufb17\uff41-\uff5a\u01c5\u01c8\u01cb\u01f2\u1f88-\u1f8f\u1f98-\u1f9f\u1fa8-\u1faf\u1fbc\u1fcc\u1ffc\u02b0-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0374\u037a\u0559\u0640\u06e5-\u06e6\u07f4-\u07f5\u07fa\u081a\u0824\u0828\u0971\u0e46\u0ec6\u10fc\u17d7\u1843\u1aa7\u1c78-\u1c7d\u1d2c-\u1d6a\u1d78\u1d9b-\u1dbf\u2071\u207f\u2090-\u209c\u2c7c-\u2c7d\u2d6f\u2e2f\u3005\u3031-\u3035\u303b\u309d-\u309e\u30fc-\u30fe\ua015\ua4f8-\ua4fd\ua60c\ua67f\ua717-\ua71f\ua770\ua788\ua7f8-\ua7f9\ua9cf\uaa70\uaadd\uaaf3-\uaaf4\uff70\uff9e-\uff9f\u16ee-\u16f0\u2160-\u2182\u2185-\u2188\u3007\u3021-\u3029\u3038-\u303a\ua6e6-\ua6ef][A-Z\u00c0-\u00d6\u00d8-\u00de\u0100\u0102\u0104\u0106\u0108\u010a\u010c\u010e\u0110\u0112\u0114\u0116\u0118\u011a\u011c\u011e\u0120\u0122\u0124\u0126\u0128\u012a\u012c\u012e\u0130\u0132\u0134\u0136\u0139\u013b\u013d\u013f\u0141\u0143\u0145\u0147\u014a\u014c\u014e\u0150\u0152\u0154\u0156\u0158\u015a\u015c\u015e\u0160\u0162\u0164\u0166\u0168\u016a\u016c\u016e\u0170\u0172\u0174\u0176\u0178-\u0179\u017b\u017d\u0181-\u0182\u0184\u0186-\u0187\u0189-\u018b\u018e-\u0191\u0193-\u0194\u0196-\u0198\u019c-\u019d\u019f-\u01a0\u01a2\u01a4\u01a6-\u01a7\u01a9\u01ac\u01ae-\u01af\u01b1-\u01b3\u01b5\u01b7-\u01b8\u01bc\u01c4\u01c7\u01ca\u01cd\u01cf\u01d1\u01d3\u01d5\u01d7\u01d9\u01db\u01de\u01e0\u01e2\u01e4\u01e6\u01e8\u01ea\u01ec\u01ee\u01f1\u01f4\u01f6-\u01f8\u01fa\u01fc\u01fe\u0200\u0202\u0204\u0206\u0208\u020a\u020c\u020e\u0210\u0212\u0214\u0216\u0218\u021a\u021c\u021e\u0220\u0222\u0224\u0226\u0228\u022a\u022c\u022e\u0230\u0232\u023a-\u023b\u023d-\u023e\u0241\u0243-\u0246\u0248\u024a\u024c\u024e\u0370\u0372\u0376\u0386\u0388-\u038a\u038c\u038e-\u038f\u0391-\u03a1\u03a3-\u03ab\u03cf\u03d2-\u03d4\u03d8\u03da\u03dc\u03de\u03e0\u03e2\u03e4\u03e6\u03e8\u03ea\u03ec\u03ee\u03f4\u03f7\u03f9-\u03fa\u03fd-\u042f\u0460\u0462\u0464\u0466\u0468\u046a\u046c\u046e\u0470\u0472\u0474\u0476\u0478\u047a\u047c\u047e\u0480\u048a\u048c\u048e\u0490\u0492\u0494\u0496\u0498\u049a\u049c\u049e\u04a0\u04a2\u04a4\u04a6\u04a8\u04aa\u04ac\u04ae\u04b0\u04b2\u04b4\u04b6\u04b8\u04ba\u04bc\u04be\u04c0-\u04c1\u04c3\u04c5\u04c7\u04c9\u04cb\u04cd\u04d0\u04d2\u04d4\u04d6\u04d8\u04da\u04dc\u04de\u04e0\u04e2\u04e4\u04e6\u04e8\u04ea\u04ec\u04ee\u04f0\u04f2\u04f4\u04f6\u04f8\u04fa\u04fc\u04fe\u0500\u0502\u0504\u0506\u0508\u050a\u050c\u050e\u0510\u0512\u0514\u0516\u0518\u051a\u051c\u051e\u0520\u0522\u0524\u0526\u0531-\u0556\u10a0-\u10c5\u10c7\u10cd\u1e00\u1e02\u1e04\u1e06\u1e08\u1e0a\u1e0c\u1e0e\u1e10\u1e12\u1e14\u1e16\u1e18\u1e1a\u1e1c\u1e1e\u1e20\u1e22\u1e24\u1e26\u1e28\u1e2a\u1e2c\u1e2e\u1e30\u1e32\u1e34\u1e36\u1e38\u1e3a\u1e3c\u1e3e\u1e40\u1e42\u1e44\u1e46\u1e48\u1e4a\u1e4c\u1e4e\u1e50\u1e52\u1e54\u1e56\u1e58\u1e5a\u1e5c\u1e5e\u1e60\u1e62\u1e64\u1e66\u1e68\u1e6a\u1e6c\u1e6e\u1e70\u1e72\u1e74\u1e76\u1e78\u1e7a\u1e7c\u1e7e\u1e80\u1e82\u1e84\u1e86\u1e88\u1e8a\u1e8c\u1e8e\u1e90\u1e92\u1e94\u1e9e\u1ea0\u1ea2\u1ea4\u1ea6\u1ea8\u1eaa\u1eac\u1eae\u1eb0\u1eb2\u1eb4\u1eb6\u1eb8\u1eba\u1ebc\u1ebe\u1ec0\u1ec2\u1ec4\u1ec6\u1ec8\u1eca\u1ecc\u1ece\u1ed0\u1ed2\u1ed4\u1ed6\u1ed8\u1eda\u1edc\u1ede\u1ee0\u1ee2\u1ee4\u1ee6\u1ee8\u1eea\u1eec\u1eee\u1ef0\u1ef2\u1ef4\u1ef6\u1ef8\u1efa\u1efc\u1efe\u1f08-\u1f0f\u1f18-\u1f1d\u1f28-\u1f2f\u1f38-\u1f3f\u1f48-\u1f4d\u1f59\u1f5b\u1f5d\u1f5f\u1f68-\u1f6f\u1fb8-\u1fbb\u1fc8-\u1fcb\u1fd8-\u1fdb\u1fe8-\u1fec\u1ff8-\u1ffb\u2102\u2107\u210b-\u210d\u2110-\u2112\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u2130-\u2133\u213e-\u213f\u2145\u2183\u2c00-\u2c2e\u2c60\u2c62-\u2c64\u2c67\u2c69\u2c6b\u2c6d-\u2c70\u2c72\u2c75\u2c7e-\u2c80\u2c82\u2c84\u2c86\u2c88\u2c8a\u2c8c\u2c8e\u2c90\u2c92\u2c94\u2c96\u2c98\u2c9a\u2c9c\u2c9e\u2ca0\u2ca2\u2ca4\u2ca6\u2ca8\u2caa\u2cac\u2cae\u2cb0\u2cb2\u2cb4\u2cb6\u2cb8\u2cba\u2cbc\u2cbe\u2cc0\u2cc2\u2cc4\u2cc6\u2cc8\u2cca\u2ccc\u2cce\u2cd0\u2cd2\u2cd4\u2cd6\u2cd8\u2cda\u2cdc\u2cde\u2ce0\u2ce2\u2ceb\u2ced\u2cf2\ua640\ua642\ua644\ua646\ua648\ua64a\ua64c\ua64e\ua650\ua652\ua654\ua656\ua658\ua65a\ua65c\ua65e\ua660\ua662\ua664\ua666\ua668\ua66a\ua66c\ua680\ua682\ua684\ua686\ua688\ua68a\ua68c\ua68e\ua690\ua692\ua694\ua696\ua722\ua724\ua726\ua728\ua72a\ua72c\ua72e\ua732\ua734\ua736\ua738\ua73a\ua73c\ua73e\ua740\ua742\ua744\ua746\ua748\ua74a\ua74c\ua74e\ua750\ua752\ua754\ua756\ua758\ua75a\ua75c\ua75e\ua760\ua762\ua764\ua766\ua768\ua76a\ua76c\ua76e\ua779\ua77b\ua77d-\ua77e\ua780\ua782\ua784\ua786\ua78b\ua78d\ua790\ua792\ua7a0\ua7a2\ua7a4\ua7a6\ua7a8\ua7aa\uff21-\uff3aa-z\u00b5\u00df-\u00f6\u00f8-\u00ff\u0101\u0103\u0105\u0107\u0109\u010b\u010d\u010f\u0111\u0113\u0115\u0117\u0119\u011b\u011d\u011f\u0121\u0123\u0125\u0127\u0129\u012b\u012d\u012f\u0131\u0133\u0135\u0137-\u0138\u013a\u013c\u013e\u0140\u0142\u0144\u0146\u0148-\u0149\u014b\u014d\u014f\u0151\u0153\u0155\u0157\u0159\u015b\u015d\u015f\u0161\u0163\u0165\u0167\u0169\u016b\u016d\u016f\u0171\u0173\u0175\u0177\u017a\u017c\u017e-\u0180\u0183\u0185\u0188\u018c-\u018d\u0192\u0195\u0199-\u019b\u019e\u01a1\u01a3\u01a5\u01a8\u01aa-\u01ab\u01ad\u01b0\u01b4\u01b6\u01b9-\u01ba\u01bd-\u01bf\u01c6\u01c9\u01cc\u01ce\u01d0\u01d2\u01d4\u01d6\u01d8\u01da\u01dc-\u01dd\u01df\u01e1\u01e3\u01e5\u01e7\u01e9\u01eb\u01ed\u01ef-\u01f0\u01f3\u01f5\u01f9\u01fb\u01fd\u01ff\u0201\u0203\u0205\u0207\u0209\u020b\u020d\u020f\u0211\u0213\u0215\u0217\u0219\u021b\u021d\u021f\u0221\u0223\u0225\u0227\u0229\u022b\u022d\u022f\u0231\u0233-\u0239\u023c\u023f-\u0240\u0242\u0247\u0249\u024b\u024d\u024f-\u0293\u0295-\u02af\u0371\u0373\u0377\u037b-\u037d\u0390\u03ac-\u03ce\u03d0-\u03d1\u03d5-\u03d7\u03d9\u03db\u03dd\u03df\u03e1\u03e3\u03e5\u03e7\u03e9\u03eb\u03ed\u03ef-\u03f3\u03f5\u03f8\u03fb-\u03fc\u0430-\u045f\u0461\u0463\u0465\u0467\u0469\u046b\u046d\u046f\u0471\u0473\u0475\u0477\u0479\u047b\u047d\u047f\u0481\u048b\u048d\u048f\u0491\u0493\u0495\u0497\u0499\u049b\u049d\u049f\u04a1\u04a3\u04a5\u04a7\u04a9\u04ab\u04ad\u04af\u04b1\u04b3\u04b5\u04b7\u04b9\u04bb\u04bd\u04bf\u04c2\u04c4\u04c6\u04c8\u04ca\u04cc\u04ce-\u04cf\u04d1\u04d3\u04d5\u04d7\u04d9\u04db\u04dd\u04df\u04e1\u04e3\u04e5\u04e7\u04e9\u04eb\u04ed\u04ef\u04f1\u04f3\u04f5\u04f7\u04f9\u04fb\u04fd\u04ff\u0501\u0503\u0505\u0507\u0509\u050b\u050d\u050f\u0511\u0513\u0515\u0517\u0519\u051b\u051d\u051f\u0521\u0523\u0525\u0527\u0561-\u0587\u1d00-\u1d2b\u1d6b-\u1d77\u1d79-\u1d9a\u1e01\u1e03\u1e05\u1e07\u1e09\u1e0b\u1e0d\u1e0f\u1e11\u1e13\u1e15\u1e17\u1e19\u1e1b\u1e1d\u1e1f\u1e21\u1e23\u1e25\u1e27\u1e29\u1e2b\u1e2d\u1e2f\u1e31\u1e33\u1e35\u1e37\u1e39\u1e3b\u1e3d\u1e3f\u1e41\u1e43\u1e45\u1e47\u1e49\u1e4b\u1e4d\u1e4f\u1e51\u1e53\u1e55\u1e57\u1e59\u1e5b\u1e5d\u1e5f\u1e61\u1e63\u1e65\u1e67\u1e69\u1e6b\u1e6d\u1e6f\u1e71\u1e73\u1e75\u1e77\u1e79\u1e7b\u1e7d\u1e7f\u1e81\u1e83\u1e85\u1e87\u1e89\u1e8b\u1e8d\u1e8f\u1e91\u1e93\u1e95-\u1e9d\u1e9f\u1ea1\u1ea3\u1ea5\u1ea7\u1ea9\u1eab\u1ead\u1eaf\u1eb1\u1eb3\u1eb5\u1eb7\u1eb9\u1ebb\u1ebd\u1ebf\u1ec1\u1ec3\u1ec5\u1ec7\u1ec9\u1ecb\u1ecd\u1ecf\u1ed1\u1ed3\u1ed5\u1ed7\u1ed9\u1edb\u1edd\u1edf\u1ee1\u1ee3\u1ee5\u1ee7\u1ee9\u1eeb\u1eed\u1eef\u1ef1\u1ef3\u1ef5\u1ef7\u1ef9\u1efb\u1efd\u1eff-\u1f07\u1f10-\u1f15\u1f20-\u1f27\u1f30-\u1f37\u1f40-\u1f45\u1f50-\u1f57\u1f60-\u1f67\u1f70-\u1f7d\u1f80-\u1f87\u1f90-\u1f97\u1fa0-\u1fa7\u1fb0-\u1fb4\u1fb6-\u1fb7\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fc7\u1fd0-\u1fd3\u1fd6-\u1fd7\u1fe0-\u1fe7\u1ff2-\u1ff4\u1ff6-\u1ff7\u210a\u210e-\u210f\u2113\u212f\u2134\u2139\u213c-\u213d\u2146-\u2149\u214e\u2184\u2c30-\u2c5e\u2c61\u2c65-\u2c66\u2c68\u2c6a\u2c6c\u2c71\u2c73-\u2c74\u2c76-\u2c7b\u2c81\u2c83\u2c85\u2c87\u2c89\u2c8b\u2c8d\u2c8f\u2c91\u2c93\u2c95\u2c97\u2c99\u2c9b\u2c9d\u2c9f\u2ca1\u2ca3\u2ca5\u2ca7\u2ca9\u2cab\u2cad\u2caf\u2cb1\u2cb3\u2cb5\u2cb7\u2cb9\u2cbb\u2cbd\u2cbf\u2cc1\u2cc3\u2cc5\u2cc7\u2cc9\u2ccb\u2ccd\u2ccf\u2cd1\u2cd3\u2cd5\u2cd7\u2cd9\u2cdb\u2cdd\u2cdf\u2ce1\u2ce3-\u2ce4\u2cec\u2cee\u2cf3\u2d00-\u2d25\u2d27\u2d2d\ua641\ua643\ua645\ua647\ua649\ua64b\ua64d\ua64f\ua651\ua653\ua655\ua657\ua659\ua65b\ua65d\ua65f\ua661\ua663\ua665\ua667\ua669\ua66b\ua66d\ua681\ua683\ua685\ua687\ua689\ua68b\ua68d\ua68f\ua691\ua693\ua695\ua697\ua723\ua725\ua727\ua729\ua72b\ua72d\ua72f-\ua731\ua733\ua735\ua737\ua739\ua73b\ua73d\ua73f\ua741\ua743\ua745\ua747\ua749\ua74b\ua74d\ua74f\ua751\ua753\ua755\ua757\ua759\ua75b\ua75d\ua75f\ua761\ua763\ua765\ua767\ua769\ua76b\ua76d\ua76f\ua771-\ua778\ua77a\ua77c\ua77f\ua781\ua783\ua785\ua787\ua78c\ua78e\ua791\ua793\ua7a1\ua7a3\ua7a5\ua7a7\ua7a9\ua7fa\ufb00-\ufb06\ufb13-\ufb17\uff41-\uff5a\u01c5\u01c8\u01cb\u01f2\u1f88-\u1f8f\u1f98-\u1f9f\u1fa8-\u1faf\u1fbc\u1fcc\u1ffc\u02b0-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0374\u037a\u0559\u0640\u06e5-\u06e6\u07f4-\u07f5\u07fa\u081a\u0824\u0828\u0971\u0e46\u0ec6\u10fc\u17d7\u1843\u1aa7\u1c78-\u1c7d\u1d2c-\u1d6a\u1d78\u1d9b-\u1dbf\u2071\u207f\u2090-\u209c\u2c7c-\u2c7d\u2d6f\u2e2f\u3005\u3031-\u3035\u303b\u309d-\u309e\u30fc-\u30fe\ua015\ua4f8-\ua4fd\ua60c\ua67f\ua717-\ua71f\ua770\ua788\ua7f8-\ua7f9\ua9cf\uaa70\uaadd\uaaf3-\uaaf4\uff70\uff9e-\uff9f\u16ee-\u16f0\u2160-\u2182\u2185-\u2188\u3007\u3021-\u3029\u3038-\u303a\ua6e6-\ua6ef0-9\u0660-\u0669\u06f0-\u06f9\u07c0-\u07c9\u0966-\u096f\u09e6-\u09ef\u0a66-\u0a6f\u0ae6-\u0aef\u0b66-\u0b6f\u0be6-\u0bef\u0c66-\u0c6f\u0ce6-\u0cef\u0d66-\u0d6f\u0e50-\u0e59\u0ed0-\u0ed9\u0f20-\u0f29\u1040-\u1049\u1090-\u1099\u17e0-\u17e9\u1810-\u1819\u1946-\u194f\u19d0-\u19d9\u1a80-\u1a89\u1a90-\u1a99\u1b50-\u1b59\u1bb0-\u1bb9\u1c40-\u1c49\u1c50-\u1c59\ua620-\ua629\ua8d0-\ua8d9\ua900-\ua909\ua9d0-\ua9d9\uaa50-\uaa59\uabf0-\uabf9\uff10-\uff19_\u203f-\u2040\u2054\ufe33-\ufe34\ufe4d-\ufe4f\uff3f\u00ad\u0600-\u0604\u061c\u06dd\u070f\u180e\u200b-\u200f\u202a-\u202e\u2060-\u2064\u2066-\u206f\ufeff\ufff9-\ufffb\u0300-\u036f\u0483-\u0487\u0591-\u05bd\u05bf\u05c1-\u05c2\u05c4-\u05c5\u05c7\u0610-\u061a\u064b-\u065f\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7-\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08e4-\u08fe\u0900-\u0902\u093a\u093c\u0941-\u0948\u094d\u0951-\u0957\u0962-\u0963\u0981\u09bc\u09c1-\u09c4\u09cd\u09e2-\u09e3\u0a01-\u0a02\u0a3c\u0a41-\u0a42\u0a47-\u0a48\u0a4b-\u0a4d\u0a51\u0a70-\u0a71\u0a75\u0a81-\u0a82\u0abc\u0ac1-\u0ac5\u0ac7-\u0ac8\u0acd\u0ae2-\u0ae3\u0b01\u0b3c\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b62-\u0b63\u0b82\u0bc0\u0bcd\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55-\u0c56\u0c62-\u0c63\u0cbc\u0cbf\u0cc6\u0ccc-\u0ccd\u0ce2-\u0ce3\u0d41-\u0d44\u0d4d\u0d62-\u0d63\u0dca\u0dd2-\u0dd4\u0dd6\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb-\u0ebc\u0ec8-\u0ecd\u0f18-\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039-\u103a\u103d-\u103e\u1058-\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085-\u1086\u108d\u109d\u135d-\u135f\u1712-\u1714\u1732-\u1734\u1752-\u1753\u1772-\u1773\u17b4-\u17b5\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927-\u1928\u1932\u1939-\u193b\u1a17-\u1a18\u1a1b\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80-\u1b81\u1ba2-\u1ba5\u1ba8-\u1ba9\u1bab\u1be6\u1be8-\u1be9\u1bed\u1bef-\u1bf1\u1c2c-\u1c33\u1c36-\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1cf4\u1dc0-\u1de6\u1dfc-\u1dff\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302d\u3099-\u309a\ua66f\ua674-\ua67d\ua69f\ua6f0-\ua6f1\ua802\ua806\ua80b\ua825-\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31-\uaa32\uaa35-\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7-\uaab8\uaabe-\uaabf\uaac1\uaaec-\uaaed\uaaf6\uabe5\uabe8\uabed\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\u0903\u093b\u093e-\u0940\u0949-\u094c\u094e-\u094f\u0982-\u0983\u09be-\u09c0\u09c7-\u09c8\u09cb-\u09cc\u09d7\u0a03\u0a3e-\u0a40\u0a83\u0abe-\u0ac0\u0ac9\u0acb-\u0acc\u0b02-\u0b03\u0b3e\u0b40\u0b47-\u0b48\u0b4b-\u0b4c\u0b57\u0bbe-\u0bbf\u0bc1-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcc\u0bd7\u0c01-\u0c03\u0c41-\u0c44\u0c82-\u0c83\u0cbe\u0cc0-\u0cc4\u0cc7-\u0cc8\u0cca-\u0ccb\u0cd5-\u0cd6\u0d02-\u0d03\u0d3e-\u0d40\u0d46-\u0d48\u0d4a-\u0d4c\u0d57\u0d82-\u0d83\u0dcf-\u0dd1\u0dd8-\u0ddf\u0df2-\u0df3\u0f3e-\u0f3f\u0f7f\u102b-\u102c\u1031\u1038\u103b-\u103c\u1056-\u1057\u1062-\u1064\u1067-\u106d\u1083-\u1084\u1087-\u108c\u108f\u109a-\u109c\u17b6\u17be-\u17c5\u17c7-\u17c8\u1923-\u1926\u1929-\u192b\u1930-\u1931\u1933-\u1938\u19b0-\u19c0\u19c8-\u19c9\u1a19-\u1a1a\u1a55\u1a57\u1a61\u1a63-\u1a64\u1a6d-\u1a72\u1b04\u1b35\u1b3b\u1b3d-\u1b41\u1b43-\u1b44\u1b82\u1ba1\u1ba6-\u1ba7\u1baa\u1bac-\u1bad\u1be7\u1bea-\u1bec\u1bee\u1bf2-\u1bf3\u1c24-\u1c2b\u1c34-\u1c35\u1ce1\u1cf2-\u1cf3\u302e-\u302f\ua823-\ua824\ua827\ua880-\ua881\ua8b4-\ua8c3\ua952-\ua953\ua983\ua9b4-\ua9b5\ua9ba-\ua9bb\ua9bd-\ua9c0\uaa2f-\uaa30\uaa33-\uaa34\uaa4d\uaa7b\uaaeb\uaaee-\uaaef\uaaf5\uabe3-\uabe4\uabe6-\uabe7\uabe9-\uabea\uabec]*|`@?[_A-Z\u00c0-\u00d6\u00d8-\u00de\u0100\u0102\u0104\u0106\u0108\u010a\u010c\u010e\u0110\u0112\u0114\u0116\u0118\u011a\u011c\u011e\u0120\u0122\u0124\u0126\u0128\u012a\u012c\u012e\u0130\u0132\u0134\u0136\u0139\u013b\u013d\u013f\u0141\u0143\u0145\u0147\u014a\u014c\u014e\u0150\u0152\u0154\u0156\u0158\u015a\u015c\u015e\u0160\u0162\u0164\u0166\u0168\u016a\u016c\u016e\u0170\u0172\u0174\u0176\u0178-\u0179\u017b\u017d\u0181-\u0182\u0184\u0186-\u0187\u0189-\u018b\u018e-\u0191\u0193-\u0194\u0196-\u0198\u019c-\u019d\u019f-\u01a0\u01a2\u01a4\u01a6-\u01a7\u01a9\u01ac\u01ae-\u01af\u01b1-\u01b3\u01b5\u01b7-\u01b8\u01bc\u01c4\u01c7\u01ca\u01cd\u01cf\u01d1\u01d3\u01d5\u01d7\u01d9\u01db\u01de\u01e0\u01e2\u01e4\u01e6\u01e8\u01ea\u01ec\u01ee\u01f1\u01f4\u01f6-\u01f8\u01fa\u01fc\u01fe\u0200\u0202\u0204\u0206\u0208\u020a\u020c\u020e\u0210\u0212\u0214\u0216\u0218\u021a\u021c\u021e\u0220\u0222\u0224\u0226\u0228\u022a\u022c\u022e\u0230\u0232\u023a-\u023b\u023d-\u023e\u0241\u0243-\u0246\u0248\u024a\u024c\u024e\u0370\u0372\u0376\u0386\u0388-\u038a\u038c\u038e-\u038f\u0391-\u03a1\u03a3-\u03ab\u03cf\u03d2-\u03d4\u03d8\u03da\u03dc\u03de\u03e0\u03e2\u03e4\u03e6\u03e8\u03ea\u03ec\u03ee\u03f4\u03f7\u03f9-\u03fa\u03fd-\u042f\u0460\u0462\u0464\u0466\u0468\u046a\u046c\u046e\u0470\u0472\u0474\u0476\u0478\u047a\u047c\u047e\u0480\u048a\u048c\u048e\u0490\u0492\u0494\u0496\u0498\u049a\u049c\u049e\u04a0\u04a2\u04a4\u04a6\u04a8\u04aa\u04ac\u04ae\u04b0\u04b2\u04b4\u04b6\u04b8\u04ba\u04bc\u04be\u04c0-\u04c1\u04c3\u04c5\u04c7\u04c9\u04cb\u04cd\u04d0\u04d2\u04d4\u04d6\u04d8\u04da\u04dc\u04de\u04e0\u04e2\u04e4\u04e6\u04e8\u04ea\u04ec\u04ee\u04f0\u04f2\u04f4\u04f6\u04f8\u04fa\u04fc\u04fe\u0500\u0502\u0504\u0506\u0508\u050a\u050c\u050e\u0510\u0512\u0514\u0516\u0518\u051a\u051c\u051e\u0520\u0522\u0524\u0526\u0531-\u0556\u10a0-\u10c5\u10c7\u10cd\u1e00\u1e02\u1e04\u1e06\u1e08\u1e0a\u1e0c\u1e0e\u1e10\u1e12\u1e14\u1e16\u1e18\u1e1a\u1e1c\u1e1e\u1e20\u1e22\u1e24\u1e26\u1e28\u1e2a\u1e2c\u1e2e\u1e30\u1e32\u1e34\u1e36\u1e38\u1e3a\u1e3c\u1e3e\u1e40\u1e42\u1e44\u1e46\u1e48\u1e4a\u1e4c\u1e4e\u1e50\u1e52\u1e54\u1e56\u1e58\u1e5a\u1e5c\u1e5e\u1e60\u1e62\u1e64\u1e66\u1e68\u1e6a\u1e6c\u1e6e\u1e70\u1e72\u1e74\u1e76\u1e78\u1e7a\u1e7c\u1e7e\u1e80\u1e82\u1e84\u1e86\u1e88\u1e8a\u1e8c\u1e8e\u1e90\u1e92\u1e94\u1e9e\u1ea0\u1ea2\u1ea4\u1ea6\u1ea8\u1eaa\u1eac\u1eae\u1eb0\u1eb2\u1eb4\u1eb6\u1eb8\u1eba\u1ebc\u1ebe\u1ec0\u1ec2\u1ec4\u1ec6\u1ec8\u1eca\u1ecc\u1ece\u1ed0\u1ed2\u1ed4\u1ed6\u1ed8\u1eda\u1edc\u1ede\u1ee0\u1ee2\u1ee4\u1ee6\u1ee8\u1eea\u1eec\u1eee\u1ef0\u1ef2\u1ef4\u1ef6\u1ef8\u1efa\u1efc\u1efe\u1f08-\u1f0f\u1f18-\u1f1d\u1f28-\u1f2f\u1f38-\u1f3f\u1f48-\u1f4d\u1f59\u1f5b\u1f5d\u1f5f\u1f68-\u1f6f\u1fb8-\u1fbb\u1fc8-\u1fcb\u1fd8-\u1fdb\u1fe8-\u1fec\u1ff8-\u1ffb\u2102\u2107\u210b-\u210d\u2110-\u2112\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u2130-\u2133\u213e-\u213f\u2145\u2183\u2c00-\u2c2e\u2c60\u2c62-\u2c64\u2c67\u2c69\u2c6b\u2c6d-\u2c70\u2c72\u2c75\u2c7e-\u2c80\u2c82\u2c84\u2c86\u2c88\u2c8a\u2c8c\u2c8e\u2c90\u2c92\u2c94\u2c96\u2c98\u2c9a\u2c9c\u2c9e\u2ca0\u2ca2\u2ca4\u2ca6\u2ca8\u2caa\u2cac\u2cae\u2cb0\u2cb2\u2cb4\u2cb6\u2cb8\u2cba\u2cbc\u2cbe\u2cc0\u2cc2\u2cc4\u2cc6\u2cc8\u2cca\u2ccc\u2cce\u2cd0\u2cd2\u2cd4\u2cd6\u2cd8\u2cda\u2cdc\u2cde\u2ce0\u2ce2\u2ceb\u2ced\u2cf2\ua640\ua642\ua644\ua646\ua648\ua64a\ua64c\ua64e\ua650\ua652\ua654\ua656\ua658\ua65a\ua65c\ua65e\ua660\ua662\ua664\ua666\ua668\ua66a\ua66c\ua680\ua682\ua684\ua686\ua688\ua68a\ua68c\ua68e\ua690\ua692\ua694\ua696\ua722\ua724\ua726\ua728\ua72a\ua72c\ua72e\ua732\ua734\ua736\ua738\ua73a\ua73c\ua73e\ua740\ua742\ua744\ua746\ua748\ua74a\ua74c\ua74e\ua750\ua752\ua754\ua756\ua758\ua75a\ua75c\ua75e\ua760\ua762\ua764\ua766\ua768\ua76a\ua76c\ua76e\ua779\ua77b\ua77d-\ua77e\ua780\ua782\ua784\ua786\ua78b\ua78d\ua790\ua792\ua7a0\ua7a2\ua7a4\ua7a6\ua7a8\ua7aa\uff21-\uff3aa-z\u00b5\u00df-\u00f6\u00f8-\u00ff\u0101\u0103\u0105\u0107\u0109\u010b\u010d\u010f\u0111\u0113\u0115\u0117\u0119\u011b\u011d\u011f\u0121\u0123\u0125\u0127\u0129\u012b\u012d\u012f\u0131\u0133\u0135\u0137-\u0138\u013a\u013c\u013e\u0140\u0142\u0144\u0146\u0148-\u0149\u014b\u014d\u014f\u0151\u0153\u0155\u0157\u0159\u015b\u015d\u015f\u0161\u0163\u0165\u0167\u0169\u016b\u016d\u016f\u0171\u0173\u0175\u0177\u017a\u017c\u017e-\u0180\u0183\u0185\u0188\u018c-\u018d\u0192\u0195\u0199-\u019b\u019e\u01a1\u01a3\u01a5\u01a8\u01aa-\u01ab\u01ad\u01b0\u01b4\u01b6\u01b9-\u01ba\u01bd-\u01bf\u01c6\u01c9\u01cc\u01ce\u01d0\u01d2\u01d4\u01d6\u01d8\u01da\u01dc-\u01dd\u01df\u01e1\u01e3\u01e5\u01e7\u01e9\u01eb\u01ed\u01ef-\u01f0\u01f3\u01f5\u01f9\u01fb\u01fd\u01ff\u0201\u0203\u0205\u0207\u0209\u020b\u020d\u020f\u0211\u0213\u0215\u0217\u0219\u021b\u021d\u021f\u0221\u0223\u0225\u0227\u0229\u022b\u022d\u022f\u0231\u0233-\u0239\u023c\u023f-\u0240\u0242\u0247\u0249\u024b\u024d\u024f-\u0293\u0295-\u02af\u0371\u0373\u0377\u037b-\u037d\u0390\u03ac-\u03ce\u03d0-\u03d1\u03d5-\u03d7\u03d9\u03db\u03dd\u03df\u03e1\u03e3\u03e5\u03e7\u03e9\u03eb\u03ed\u03ef-\u03f3\u03f5\u03f8\u03fb-\u03fc\u0430-\u045f\u0461\u0463\u0465\u0467\u0469\u046b\u046d\u046f\u0471\u0473\u0475\u0477\u0479\u047b\u047d\u047f\u0481\u048b\u048d\u048f\u0491\u0493\u0495\u0497\u0499\u049b\u049d\u049f\u04a1\u04a3\u04a5\u04a7\u04a9\u04ab\u04ad\u04af\u04b1\u04b3\u04b5\u04b7\u04b9\u04bb\u04bd\u04bf\u04c2\u04c4\u04c6\u04c8\u04ca\u04cc\u04ce-\u04cf\u04d1\u04d3\u04d5\u04d7\u04d9\u04db\u04dd\u04df\u04e1\u04e3\u04e5\u04e7\u04e9\u04eb\u04ed\u04ef\u04f1\u04f3\u04f5\u04f7\u04f9\u04fb\u04fd\u04ff\u0501\u0503\u0505\u0507\u0509\u050b\u050d\u050f\u0511\u0513\u0515\u0517\u0519\u051b\u051d\u051f\u0521\u0523\u0525\u0527\u0561-\u0587\u1d00-\u1d2b\u1d6b-\u1d77\u1d79-\u1d9a\u1e01\u1e03\u1e05\u1e07\u1e09\u1e0b\u1e0d\u1e0f\u1e11\u1e13\u1e15\u1e17\u1e19\u1e1b\u1e1d\u1e1f\u1e21\u1e23\u1e25\u1e27\u1e29\u1e2b\u1e2d\u1e2f\u1e31\u1e33\u1e35\u1e37\u1e39\u1e3b\u1e3d\u1e3f\u1e41\u1e43\u1e45\u1e47\u1e49\u1e4b\u1e4d\u1e4f\u1e51\u1e53\u1e55\u1e57\u1e59\u1e5b\u1e5d\u1e5f\u1e61\u1e63\u1e65\u1e67\u1e69\u1e6b\u1e6d\u1e6f\u1e71\u1e73\u1e75\u1e77\u1e79\u1e7b\u1e7d\u1e7f\u1e81\u1e83\u1e85\u1e87\u1e89\u1e8b\u1e8d\u1e8f\u1e91\u1e93\u1e95-\u1e9d\u1e9f\u1ea1\u1ea3\u1ea5\u1ea7\u1ea9\u1eab\u1ead\u1eaf\u1eb1\u1eb3\u1eb5\u1eb7\u1eb9\u1ebb\u1ebd\u1ebf\u1ec1\u1ec3\u1ec5\u1ec7\u1ec9\u1ecb\u1ecd\u1ecf\u1ed1\u1ed3\u1ed5\u1ed7\u1ed9\u1edb\u1edd\u1edf\u1ee1\u1ee3\u1ee5\u1ee7\u1ee9\u1eeb\u1eed\u1eef\u1ef1\u1ef3\u1ef5\u1ef7\u1ef9\u1efb\u1efd\u1eff-\u1f07\u1f10-\u1f15\u1f20-\u1f27\u1f30-\u1f37\u1f40-\u1f45\u1f50-\u1f57\u1f60-\u1f67\u1f70-\u1f7d\u1f80-\u1f87\u1f90-\u1f97\u1fa0-\u1fa7\u1fb0-\u1fb4\u1fb6-\u1fb7\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fc7\u1fd0-\u1fd3\u1fd6-\u1fd7\u1fe0-\u1fe7\u1ff2-\u1ff4\u1ff6-\u1ff7\u210a\u210e-\u210f\u2113\u212f\u2134\u2139\u213c-\u213d\u2146-\u2149\u214e\u2184\u2c30-\u2c5e\u2c61\u2c65-\u2c66\u2c68\u2c6a\u2c6c\u2c71\u2c73-\u2c74\u2c76-\u2c7b\u2c81\u2c83\u2c85\u2c87\u2c89\u2c8b\u2c8d\u2c8f\u2c91\u2c93\u2c95\u2c97\u2c99\u2c9b\u2c9d\u2c9f\u2ca1\u2ca3\u2ca5\u2ca7\u2ca9\u2cab\u2cad\u2caf\u2cb1\u2cb3\u2cb5\u2cb7\u2cb9\u2cbb\u2cbd\u2cbf\u2cc1\u2cc3\u2cc5\u2cc7\u2cc9\u2ccb\u2ccd\u2ccf\u2cd1\u2cd3\u2cd5\u2cd7\u2cd9\u2cdb\u2cdd\u2cdf\u2ce1\u2ce3-\u2ce4\u2cec\u2cee\u2cf3\u2d00-\u2d25\u2d27\u2d2d\ua641\ua643\ua645\ua647\ua649\ua64b\ua64d\ua64f\ua651\ua653\ua655\ua657\ua659\ua65b\ua65d\ua65f\ua661\ua663\ua665\ua667\ua669\ua66b\ua66d\ua681\ua683\ua685\ua687\ua689\ua68b\ua68d\ua68f\ua691\ua693\ua695\ua697\ua723\ua725\ua727\ua729\ua72b\ua72d\ua72f-\ua731\ua733\ua735\ua737\ua739\ua73b\ua73d\ua73f\ua741\ua743\ua745\ua747\ua749\ua74b\ua74d\ua74f\ua751\ua753\ua755\ua757\ua759\ua75b\ua75d\ua75f\ua761\ua763\ua765\ua767\ua769\ua76b\ua76d\ua76f\ua771-\ua778\ua77a\ua77c\ua77f\ua781\ua783\ua785\ua787\ua78c\ua78e\ua791\ua793\ua7a1\ua7a3\ua7a5\ua7a7\ua7a9\ua7fa\ufb00-\ufb06\ufb13-\ufb17\uff41-\uff5a\u01c5\u01c8\u01cb\u01f2\u1f88-\u1f8f\u1f98-\u1f9f\u1fa8-\u1faf\u1fbc\u1fcc\u1ffc\u02b0-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0374\u037a\u0559\u0640\u06e5-\u06e6\u07f4-\u07f5\u07fa\u081a\u0824\u0828\u0971\u0e46\u0ec6\u10fc\u17d7\u1843\u1aa7\u1c78-\u1c7d\u1d2c-\u1d6a\u1d78\u1d9b-\u1dbf\u2071\u207f\u2090-\u209c\u2c7c-\u2c7d\u2d6f\u2e2f\u3005\u3031-\u3035\u303b\u309d-\u309e\u30fc-\u30fe\ua015\ua4f8-\ua4fd\ua60c\ua67f\ua717-\ua71f\ua770\ua788\ua7f8-\ua7f9\ua9cf\uaa70\uaadd\uaaf3-\uaaf4\uff70\uff9e-\uff9f\u16ee-\u16f0\u2160-\u2182\u2185-\u2188\u3007\u3021-\u3029\u3038-\u303a\ua6e6-\ua6ef][A-Z\u00c0-\u00d6\u00d8-\u00de\u0100\u0102\u0104\u0106\u0108\u010a\u010c\u010e\u0110\u0112\u0114\u0116\u0118\u011a\u011c\u011e\u0120\u0122\u0124\u0126\u0128\u012a\u012c\u012e\u0130\u0132\u0134\u0136\u0139\u013b\u013d\u013f\u0141\u0143\u0145\u0147\u014a\u014c\u014e\u0150\u0152\u0154\u0156\u0158\u015a\u015c\u015e\u0160\u0162\u0164\u0166\u0168\u016a\u016c\u016e\u0170\u0172\u0174\u0176\u0178-\u0179\u017b\u017d\u0181-\u0182\u0184\u0186-\u0187\u0189-\u018b\u018e-\u0191\u0193-\u0194\u0196-\u0198\u019c-\u019d\u019f-\u01a0\u01a2\u01a4\u01a6-\u01a7\u01a9\u01ac\u01ae-\u01af\u01b1-\u01b3\u01b5\u01b7-\u01b8\u01bc\u01c4\u01c7\u01ca\u01cd\u01cf\u01d1\u01d3\u01d5\u01d7\u01d9\u01db\u01de\u01e0\u01e2\u01e4\u01e6\u01e8\u01ea\u01ec\u01ee\u01f1\u01f4\u01f6-\u01f8\u01fa\u01fc\u01fe\u0200\u0202\u0204\u0206\u0208\u020a\u020c\u020e\u0210\u0212\u0214\u0216\u0218\u021a\u021c\u021e\u0220\u0222\u0224\u0226\u0228\u022a\u022c\u022e\u0230\u0232\u023a-\u023b\u023d-\u023e\u0241\u0243-\u0246\u0248\u024a\u024c\u024e\u0370\u0372\u0376\u0386\u0388-\u038a\u038c\u038e-\u038f\u0391-\u03a1\u03a3-\u03ab\u03cf\u03d2-\u03d4\u03d8\u03da\u03dc\u03de\u03e0\u03e2\u03e4\u03e6\u03e8\u03ea\u03ec\u03ee\u03f4\u03f7\u03f9-\u03fa\u03fd-\u042f\u0460\u0462\u0464\u0466\u0468\u046a\u046c\u046e\u0470\u0472\u0474\u0476\u0478\u047a\u047c\u047e\u0480\u048a\u048c\u048e\u0490\u0492\u0494\u0496\u0498\u049a\u049c\u049e\u04a0\u04a2\u04a4\u04a6\u04a8\u04aa\u04ac\u04ae\u04b0\u04b2\u04b4\u04b6\u04b8\u04ba\u04bc\u04be\u04c0-\u04c1\u04c3\u04c5\u04c7\u04c9\u04cb\u04cd\u04d0\u04d2\u04d4\u04d6\u04d8\u04da\u04dc\u04de\u04e0\u04e2\u04e4\u04e6\u04e8\u04ea\u04ec\u04ee\u04f0\u04f2\u04f4\u04f6\u04f8\u04fa\u04fc\u04fe\u0500\u0502\u0504\u0506\u0508\u050a\u050c\u050e\u0510\u0512\u0514\u0516\u0518\u051a\u051c\u051e\u0520\u0522\u0524\u0526\u0531-\u0556\u10a0-\u10c5\u10c7\u10cd\u1e00\u1e02\u1e04\u1e06\u1e08\u1e0a\u1e0c\u1e0e\u1e10\u1e12\u1e14\u1e16\u1e18\u1e1a\u1e1c\u1e1e\u1e20\u1e22\u1e24\u1e26\u1e28\u1e2a\u1e2c\u1e2e\u1e30\u1e32\u1e34\u1e36\u1e38\u1e3a\u1e3c\u1e3e\u1e40\u1e42\u1e44\u1e46\u1e48\u1e4a\u1e4c\u1e4e\u1e50\u1e52\u1e54\u1e56\u1e58\u1e5a\u1e5c\u1e5e\u1e60\u1e62\u1e64\u1e66\u1e68\u1e6a\u1e6c\u1e6e\u1e70\u1e72\u1e74\u1e76\u1e78\u1e7a\u1e7c\u1e7e\u1e80\u1e82\u1e84\u1e86\u1e88\u1e8a\u1e8c\u1e8e\u1e90\u1e92\u1e94\u1e9e\u1ea0\u1ea2\u1ea4\u1ea6\u1ea8\u1eaa\u1eac\u1eae\u1eb0\u1eb2\u1eb4\u1eb6\u1eb8\u1eba\u1ebc\u1ebe\u1ec0\u1ec2\u1ec4\u1ec6\u1ec8\u1eca\u1ecc\u1ece\u1ed0\u1ed2\u1ed4\u1ed6\u1ed8\u1eda\u1edc\u1ede\u1ee0\u1ee2\u1ee4\u1ee6\u1ee8\u1eea\u1eec\u1eee\u1ef0\u1ef2\u1ef4\u1ef6\u1ef8\u1efa\u1efc\u1efe\u1f08-\u1f0f\u1f18-\u1f1d\u1f28-\u1f2f\u1f38-\u1f3f\u1f48-\u1f4d\u1f59\u1f5b\u1f5d\u1f5f\u1f68-\u1f6f\u1fb8-\u1fbb\u1fc8-\u1fcb\u1fd8-\u1fdb\u1fe8-\u1fec\u1ff8-\u1ffb\u2102\u2107\u210b-\u210d\u2110-\u2112\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u2130-\u2133\u213e-\u213f\u2145\u2183\u2c00-\u2c2e\u2c60\u2c62-\u2c64\u2c67\u2c69\u2c6b\u2c6d-\u2c70\u2c72\u2c75\u2c7e-\u2c80\u2c82\u2c84\u2c86\u2c88\u2c8a\u2c8c\u2c8e\u2c90\u2c92\u2c94\u2c96\u2c98\u2c9a\u2c9c\u2c9e\u2ca0\u2ca2\u2ca4\u2ca6\u2ca8\u2caa\u2cac\u2cae\u2cb0\u2cb2\u2cb4\u2cb6\u2cb8\u2cba\u2cbc\u2cbe\u2cc0\u2cc2\u2cc4\u2cc6\u2cc8\u2cca\u2ccc\u2cce\u2cd0\u2cd2\u2cd4\u2cd6\u2cd8\u2cda\u2cdc\u2cde\u2ce0\u2ce2\u2ceb\u2ced\u2cf2\ua640\ua642\ua644\ua646\ua648\ua64a\ua64c\ua64e\ua650\ua652\ua654\ua656\ua658\ua65a\ua65c\ua65e\ua660\ua662\ua664\ua666\ua668\ua66a\ua66c\ua680\ua682\ua684\ua686\ua688\ua68a\ua68c\ua68e\ua690\ua692\ua694\ua696\ua722\ua724\ua726\ua728\ua72a\ua72c\ua72e\ua732\ua734\ua736\ua738\ua73a\ua73c\ua73e\ua740\ua742\ua744\ua746\ua748\ua74a\ua74c\ua74e\ua750\ua752\ua754\ua756\ua758\ua75a\ua75c\ua75e\ua760\ua762\ua764\ua766\ua768\ua76a\ua76c\ua76e\ua779\ua77b\ua77d-\ua77e\ua780\ua782\ua784\ua786\ua78b\ua78d\ua790\ua792\ua7a0\ua7a2\ua7a4\ua7a6\ua7a8\ua7aa\uff21-\uff3aa-z\u00b5\u00df-\u00f6\u00f8-\u00ff\u0101\u0103\u0105\u0107\u0109\u010b\u010d\u010f\u0111\u0113\u0115\u0117\u0119\u011b\u011d\u011f\u0121\u0123\u0125\u0127\u0129\u012b\u012d\u012f\u0131\u0133\u0135\u0137-\u0138\u013a\u013c\u013e\u0140\u0142\u0144\u0146\u0148-\u0149\u014b\u014d\u014f\u0151\u0153\u0155\u0157\u0159\u015b\u015d\u015f\u0161\u0163\u0165\u0167\u0169\u016b\u016d\u016f\u0171\u0173\u0175\u0177\u017a\u017c\u017e-\u0180\u0183\u0185\u0188\u018c-\u018d\u0192\u0195\u0199-\u019b\u019e\u01a1\u01a3\u01a5\u01a8\u01aa-\u01ab\u01ad\u01b0\u01b4\u01b6\u01b9-\u01ba\u01bd-\u01bf\u01c6\u01c9\u01cc\u01ce\u01d0\u01d2\u01d4\u01d6\u01d8\u01da\u01dc-\u01dd\u01df\u01e1\u01e3\u01e5\u01e7\u01e9\u01eb\u01ed\u01ef-\u01f0\u01f3\u01f5\u01f9\u01fb\u01fd\u01ff\u0201\u0203\u0205\u0207\u0209\u020b\u020d\u020f\u0211\u0213\u0215\u0217\u0219\u021b\u021d\u021f\u0221\u0223\u0225\u0227\u0229\u022b\u022d\u022f\u0231\u0233-\u0239\u023c\u023f-\u0240\u0242\u0247\u0249\u024b\u024d\u024f-\u0293\u0295-\u02af\u0371\u0373\u0377\u037b-\u037d\u0390\u03ac-\u03ce\u03d0-\u03d1\u03d5-\u03d7\u03d9\u03db\u03dd\u03df\u03e1\u03e3\u03e5\u03e7\u03e9\u03eb\u03ed\u03ef-\u03f3\u03f5\u03f8\u03fb-\u03fc\u0430-\u045f\u0461\u0463\u0465\u0467\u0469\u046b\u046d\u046f\u0471\u0473\u0475\u0477\u0479\u047b\u047d\u047f\u0481\u048b\u048d\u048f\u0491\u0493\u0495\u0497\u0499\u049b\u049d\u049f\u04a1\u04a3\u04a5\u04a7\u04a9\u04ab\u04ad\u04af\u04b1\u04b3\u04b5\u04b7\u04b9\u04bb\u04bd\u04bf\u04c2\u04c4\u04c6\u04c8\u04ca\u04cc\u04ce-\u04cf\u04d1\u04d3\u04d5\u04d7\u04d9\u04db\u04dd\u04df\u04e1\u04e3\u04e5\u04e7\u04e9\u04eb\u04ed\u04ef\u04f1\u04f3\u04f5\u04f7\u04f9\u04fb\u04fd\u04ff\u0501\u0503\u0505\u0507\u0509\u050b\u050d\u050f\u0511\u0513\u0515\u0517\u0519\u051b\u051d\u051f\u0521\u0523\u0525\u0527\u0561-\u0587\u1d00-\u1d2b\u1d6b-\u1d77\u1d79-\u1d9a\u1e01\u1e03\u1e05\u1e07\u1e09\u1e0b\u1e0d\u1e0f\u1e11\u1e13\u1e15\u1e17\u1e19\u1e1b\u1e1d\u1e1f\u1e21\u1e23\u1e25\u1e27\u1e29\u1e2b\u1e2d\u1e2f\u1e31\u1e33\u1e35\u1e37\u1e39\u1e3b\u1e3d\u1e3f\u1e41\u1e43\u1e45\u1e47\u1e49\u1e4b\u1e4d\u1e4f\u1e51\u1e53\u1e55\u1e57\u1e59\u1e5b\u1e5d\u1e5f\u1e61\u1e63\u1e65\u1e67\u1e69\u1e6b\u1e6d\u1e6f\u1e71\u1e73\u1e75\u1e77\u1e79\u1e7b\u1e7d\u1e7f\u1e81\u1e83\u1e85\u1e87\u1e89\u1e8b\u1e8d\u1e8f\u1e91\u1e93\u1e95-\u1e9d\u1e9f\u1ea1\u1ea3\u1ea5\u1ea7\u1ea9\u1eab\u1ead\u1eaf\u1eb1\u1eb3\u1eb5\u1eb7\u1eb9\u1ebb\u1ebd\u1ebf\u1ec1\u1ec3\u1ec5\u1ec7\u1ec9\u1ecb\u1ecd\u1ecf\u1ed1\u1ed3\u1ed5\u1ed7\u1ed9\u1edb\u1edd\u1edf\u1ee1\u1ee3\u1ee5\u1ee7\u1ee9\u1eeb\u1eed\u1eef\u1ef1\u1ef3\u1ef5\u1ef7\u1ef9\u1efb\u1efd\u1eff-\u1f07\u1f10-\u1f15\u1f20-\u1f27\u1f30-\u1f37\u1f40-\u1f45\u1f50-\u1f57\u1f60-\u1f67\u1f70-\u1f7d\u1f80-\u1f87\u1f90-\u1f97\u1fa0-\u1fa7\u1fb0-\u1fb4\u1fb6-\u1fb7\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fc7\u1fd0-\u1fd3\u1fd6-\u1fd7\u1fe0-\u1fe7\u1ff2-\u1ff4\u1ff6-\u1ff7\u210a\u210e-\u210f\u2113\u212f\u2134\u2139\u213c-\u213d\u2146-\u2149\u214e\u2184\u2c30-\u2c5e\u2c61\u2c65-\u2c66\u2c68\u2c6a\u2c6c\u2c71\u2c73-\u2c74\u2c76-\u2c7b\u2c81\u2c83\u2c85\u2c87\u2c89\u2c8b\u2c8d\u2c8f\u2c91\u2c93\u2c95\u2c97\u2c99\u2c9b\u2c9d\u2c9f\u2ca1\u2ca3\u2ca5\u2ca7\u2ca9\u2cab\u2cad\u2caf\u2cb1\u2cb3\u2cb5\u2cb7\u2cb9\u2cbb\u2cbd\u2cbf\u2cc1\u2cc3\u2cc5\u2cc7\u2cc9\u2ccb\u2ccd\u2ccf\u2cd1\u2cd3\u2cd5\u2cd7\u2cd9\u2cdb\u2cdd\u2cdf\u2ce1\u2ce3-\u2ce4\u2cec\u2cee\u2cf3\u2d00-\u2d25\u2d27\u2d2d\ua641\ua643\ua645\ua647\ua649\ua64b\ua64d\ua64f\ua651\ua653\ua655\ua657\ua659\ua65b\ua65d\ua65f\ua661\ua663\ua665\ua667\ua669\ua66b\ua66d\ua681\ua683\ua685\ua687\ua689\ua68b\ua68d\ua68f\ua691\ua693\ua695\ua697\ua723\ua725\ua727\ua729\ua72b\ua72d\ua72f-\ua731\ua733\ua735\ua737\ua739\ua73b\ua73d\ua73f\ua741\ua743\ua745\ua747\ua749\ua74b\ua74d\ua74f\ua751\ua753\ua755\ua757\ua759\ua75b\ua75d\ua75f\ua761\ua763\ua765\ua767\ua769\ua76b\ua76d\ua76f\ua771-\ua778\ua77a\ua77c\ua77f\ua781\ua783\ua785\ua787\ua78c\ua78e\ua791\ua793\ua7a1\ua7a3\ua7a5\ua7a7\ua7a9\ua7fa\ufb00-\ufb06\ufb13-\ufb17\uff41-\uff5a\u01c5\u01c8\u01cb\u01f2\u1f88-\u1f8f\u1f98-\u1f9f\u1fa8-\u1faf\u1fbc\u1fcc\u1ffc\u02b0-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0374\u037a\u0559\u0640\u06e5-\u06e6\u07f4-\u07f5\u07fa\u081a\u0824\u0828\u0971\u0e46\u0ec6\u10fc\u17d7\u1843\u1aa7\u1c78-\u1c7d\u1d2c-\u1d6a\u1d78\u1d9b-\u1dbf\u2071\u207f\u2090-\u209c\u2c7c-\u2c7d\u2d6f\u2e2f\u3005\u3031-\u3035\u303b\u309d-\u309e\u30fc-\u30fe\ua015\ua4f8-\ua4fd\ua60c\ua67f\ua717-\ua71f\ua770\ua788\ua7f8-\ua7f9\ua9cf\uaa70\uaadd\uaaf3-\uaaf4\uff70\uff9e-\uff9f\u16ee-\u16f0\u2160-\u2182\u2185-\u2188\u3007\u3021-\u3029\u3038-\u303a\ua6e6-\ua6ef0-9\u0660-\u0669\u06f0-\u06f9\u07c0-\u07c9\u0966-\u096f\u09e6-\u09ef\u0a66-\u0a6f\u0ae6-\u0aef\u0b66-\u0b6f\u0be6-\u0bef\u0c66-\u0c6f\u0ce6-\u0cef\u0d66-\u0d6f\u0e50-\u0e59\u0ed0-\u0ed9\u0f20-\u0f29\u1040-\u1049\u1090-\u1099\u17e0-\u17e9\u1810-\u1819\u1946-\u194f\u19d0-\u19d9\u1a80-\u1a89\u1a90-\u1a99\u1b50-\u1b59\u1bb0-\u1bb9\u1c40-\u1c49\u1c50-\u1c59\ua620-\ua629\ua8d0-\ua8d9\ua900-\ua909\ua9d0-\ua9d9\uaa50-\uaa59\uabf0-\uabf9\uff10-\uff19_\u203f-\u2040\u2054\ufe33-\ufe34\ufe4d-\ufe4f\uff3f\u00ad\u0600-\u0604\u061c\u06dd\u070f\u180e\u200b-\u200f\u202a-\u202e\u2060-\u2064\u2066-\u206f\ufeff\ufff9-\ufffb\u0300-\u036f\u0483-\u0487\u0591-\u05bd\u05bf\u05c1-\u05c2\u05c4-\u05c5\u05c7\u0610-\u061a\u064b-\u065f\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7-\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08e4-\u08fe\u0900-\u0902\u093a\u093c\u0941-\u0948\u094d\u0951-\u0957\u0962-\u0963\u0981\u09bc\u09c1-\u09c4\u09cd\u09e2-\u09e3\u0a01-\u0a02\u0a3c\u0a41-\u0a42\u0a47-\u0a48\u0a4b-\u0a4d\u0a51\u0a70-\u0a71\u0a75\u0a81-\u0a82\u0abc\u0ac1-\u0ac5\u0ac7-\u0ac8\u0acd\u0ae2-\u0ae3\u0b01\u0b3c\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b62-\u0b63\u0b82\u0bc0\u0bcd\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55-\u0c56\u0c62-\u0c63\u0cbc\u0cbf\u0cc6\u0ccc-\u0ccd\u0ce2-\u0ce3\u0d41-\u0d44\u0d4d\u0d62-\u0d63\u0dca\u0dd2-\u0dd4\u0dd6\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb-\u0ebc\u0ec8-\u0ecd\u0f18-\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039-\u103a\u103d-\u103e\u1058-\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085-\u1086\u108d\u109d\u135d-\u135f\u1712-\u1714\u1732-\u1734\u1752-\u1753\u1772-\u1773\u17b4-\u17b5\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927-\u1928\u1932\u1939-\u193b\u1a17-\u1a18\u1a1b\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80-\u1b81\u1ba2-\u1ba5\u1ba8-\u1ba9\u1bab\u1be6\u1be8-\u1be9\u1bed\u1bef-\u1bf1\u1c2c-\u1c33\u1c36-\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1cf4\u1dc0-\u1de6\u1dfc-\u1dff\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302d\u3099-\u309a\ua66f\ua674-\ua67d\ua69f\ua6f0-\ua6f1\ua802\ua806\ua80b\ua825-\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31-\uaa32\uaa35-\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7-\uaab8\uaabe-\uaabf\uaac1\uaaec-\uaaed\uaaf6\uabe5\uabe8\uabed\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\u0903\u093b\u093e-\u0940\u0949-\u094c\u094e-\u094f\u0982-\u0983\u09be-\u09c0\u09c7-\u09c8\u09cb-\u09cc\u09d7\u0a03\u0a3e-\u0a40\u0a83\u0abe-\u0ac0\u0ac9\u0acb-\u0acc\u0b02-\u0b03\u0b3e\u0b40\u0b47-\u0b48\u0b4b-\u0b4c\u0b57\u0bbe-\u0bbf\u0bc1-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcc\u0bd7\u0c01-\u0c03\u0c41-\u0c44\u0c82-\u0c83\u0cbe\u0cc0-\u0cc4\u0cc7-\u0cc8\u0cca-\u0ccb\u0cd5-\u0cd6\u0d02-\u0d03\u0d3e-\u0d40\u0d46-\u0d48\u0d4a-\u0d4c\u0d57\u0d82-\u0d83\u0dcf-\u0dd1\u0dd8-\u0ddf\u0df2-\u0df3\u0f3e-\u0f3f\u0f7f\u102b-\u102c\u1031\u1038\u103b-\u103c\u1056-\u1057\u1062-\u1064\u1067-\u106d\u1083-\u1084\u1087-\u108c\u108f\u109a-\u109c\u17b6\u17be-\u17c5\u17c7-\u17c8\u1923-\u1926\u1929-\u192b\u1930-\u1931\u1933-\u1938\u19b0-\u19c0\u19c8-\u19c9\u1a19-\u1a1a\u1a55\u1a57\u1a61\u1a63-\u1a64\u1a6d-\u1a72\u1b04\u1b35\u1b3b\u1b3d-\u1b41\u1b43-\u1b44\u1b82\u1ba1\u1ba6-\u1ba7\u1baa\u1bac-\u1bad\u1be7\u1bea-\u1bec\u1bee\u1bf2-\u1bf3\u1c24-\u1c2b\u1c34-\u1c35\u1ce1\u1cf2-\u1cf3\u302e-\u302f\ua823-\ua824\ua827\ua880-\ua881\ua8b4-\ua8c3\ua952-\ua953\ua983\ua9b4-\ua9b5\ua9ba-\ua9bb\ua9bd-\ua9c0\uaa2f-\uaa30\uaa33-\uaa34\uaa4d\uaa7b\uaaeb\uaaee-\uaaef\uaaf5\uabe3-\uabe4\uabe6-\uabe7\uabe9-\uabea\uabec" + + return Rules{ "root": { {`^\s*\[.*?\]`, NameAttribute, nil}, {`[^\S\n]+`, Text, nil}, @@ -90,5 +94,5 @@ var Kotlin = internal.Register(MustNewLexer( {`\$[` + kotlinIdentifier + `]+`, LiteralStringInterpol, nil}, {`\${[^}\n]*}`, LiteralStringInterpol, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/l/lighttpd.go b/vendor/github.com/alecthomas/chroma/lexers/l/lighttpd.go index 799b77c30a10b..6d83298b663b3 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/l/lighttpd.go +++ b/vendor/github.com/alecthomas/chroma/lexers/l/lighttpd.go @@ -6,14 +6,18 @@ import ( ) // Lighttpd Configuration File lexer. -var Lighttpd = internal.Register(MustNewLexer( +var Lighttpd = internal.Register(MustNewLazyLexer( &Config{ Name: "Lighttpd configuration file", Aliases: []string{"lighty", "lighttpd"}, Filenames: []string{}, MimeTypes: []string{"text/x-lighttpd-conf"}, }, - Rules{ + lighttpdRules, +)) + +func lighttpdRules() Rules { + return Rules{ "root": { {`#.*\n`, CommentSingle, nil}, {`/\S*`, Name, nil}, @@ -26,5 +30,5 @@ var Lighttpd = internal.Register(MustNewLexer( {`"([^"\\]*(?:\\.[^"\\]*)*)"`, LiteralStringDouble, nil}, {`\s+`, Text, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/l/llvm.go b/vendor/github.com/alecthomas/chroma/lexers/l/llvm.go index 8f5b0b1235eee..e9ea319e3ca5d 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/l/llvm.go +++ b/vendor/github.com/alecthomas/chroma/lexers/l/llvm.go @@ -6,14 +6,18 @@ import ( ) // Llvm lexer. -var Llvm = internal.Register(MustNewLexer( +var Llvm = internal.Register(MustNewLazyLexer( &Config{ Name: "LLVM", Aliases: []string{"llvm"}, Filenames: []string{"*.ll"}, MimeTypes: []string{"text/x-llvm"}, }, - Rules{ + llvmRules, +)) + +func llvmRules() Rules { + return Rules{ "root": { Include("whitespace"), {`([-a-zA-Z$._][\w\-$.]*|"[^"]*?")\s*:`, NameLabel, nil}, @@ -39,5 +43,5 @@ var Llvm = internal.Register(MustNewLexer( {Words(``, ``, `void`, `half`, `float`, `double`, `x86_fp80`, `fp128`, `ppc_fp128`, `label`, `metadata`, `token`), KeywordType, nil}, {`i[1-9]\d*`, Keyword, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/l/lua.go b/vendor/github.com/alecthomas/chroma/lexers/l/lua.go index c397de065d627..db574a1b9a882 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/l/lua.go +++ b/vendor/github.com/alecthomas/chroma/lexers/l/lua.go @@ -6,14 +6,18 @@ import ( ) // Lua lexer. -var Lua = internal.Register(MustNewLexer( +var Lua = internal.Register(MustNewLazyLexer( &Config{ Name: "Lua", Aliases: []string{"lua"}, Filenames: []string{"*.lua", "*.wlua"}, MimeTypes: []string{"text/x-lua", "application/x-lua"}, }, - Rules{ + luaRules, +)) + +func luaRules() Rules { + return Rules{ "root": { {`#!.*`, CommentPreproc, nil}, Default(Push("base")), @@ -71,5 +75,5 @@ var Lua = internal.Register(MustNewLexer( {`"`, LiteralStringDouble, Pop(1)}, {`[^\\"]+`, LiteralStringDouble, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/m/make.go b/vendor/github.com/alecthomas/chroma/lexers/m/make.go index eb9d9e68b55b1..905491a642b22 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/m/make.go +++ b/vendor/github.com/alecthomas/chroma/lexers/m/make.go @@ -7,7 +7,7 @@ import ( ) // Makefile lexer. -var Makefile = internal.Register(MustNewLexer( +var Makefile = internal.Register(MustNewLazyLexer( &Config{ Name: "Base Makefile", Aliases: []string{"make", "makefile", "mf", "bsdmake"}, @@ -15,7 +15,11 @@ var Makefile = internal.Register(MustNewLexer( MimeTypes: []string{"text/x-makefile"}, EnsureNL: true, }, - Rules{ + makefileRules, +)) + +func makefileRules() Rules { + return Rules{ "root": { {`^(?:[\t ]+.*\n|\n)+`, Using(Bash), nil}, {`\$[<@$+%?|*]`, Keyword, nil}, @@ -50,5 +54,5 @@ var Makefile = internal.Register(MustNewLexer( {`\n`, Text, Pop(1)}, {`.`, Text, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/m/mako.go b/vendor/github.com/alecthomas/chroma/lexers/m/mako.go index f7c140d534be2..6f777dd58d2e5 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/m/mako.go +++ b/vendor/github.com/alecthomas/chroma/lexers/m/mako.go @@ -7,14 +7,18 @@ import ( ) // Mako lexer. -var Mako = internal.Register(MustNewLexer( +var Mako = internal.Register(MustNewLazyLexer( &Config{ Name: "Mako", Aliases: []string{"mako"}, Filenames: []string{"*.mao"}, MimeTypes: []string{"application/x-mako"}, }, - Rules{ + makoRules, +)) + +func makoRules() Rules { + return Rules{ "root": { {`(\s*)(%)(\s*end(?:\w+))(\n|\Z)`, ByGroups(Text, CommentPreproc, Keyword, Other), nil}, {`(\s*)(%)([^\n]*)(\n|\Z)`, ByGroups(Text, CommentPreproc, Using(Python), Other), nil}, @@ -56,5 +60,5 @@ var Mako = internal.Register(MustNewLexer( {`'.*?'`, LiteralString, Pop(1)}, {`[^\s>]+`, LiteralString, Pop(1)}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/m/markdown.go b/vendor/github.com/alecthomas/chroma/lexers/m/markdown.go index dd78ec08d77f5..9ee157be07d4f 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/m/markdown.go +++ b/vendor/github.com/alecthomas/chroma/lexers/m/markdown.go @@ -7,14 +7,18 @@ import ( ) // Markdown lexer. -var Markdown = internal.Register(DelegatingLexer(h.HTML, MustNewLexer( +var Markdown = internal.Register(DelegatingLexer(h.HTML, MustNewLazyLexer( &Config{ Name: "markdown", Aliases: []string{"md", "mkd"}, Filenames: []string{"*.md", "*.mkd", "*.markdown"}, MimeTypes: []string{"text/x-markdown"}, }, - Rules{ + markdownRules, +))) + +func markdownRules() Rules { + return Rules{ "root": { {`^(#[^#].+\n)`, ByGroups(GenericHeading), nil}, {`^(#{2,6}.+\n)`, ByGroups(GenericSubheading), nil}, @@ -23,7 +27,8 @@ var Markdown = internal.Register(DelegatingLexer(h.HTML, MustNewLexer( {`^(\s*)([0-9]+\.)( .+\n)`, ByGroups(Text, Keyword, UsingSelf("inline")), nil}, {`^(\s*>\s)(.+\n)`, ByGroups(Keyword, GenericEmph), nil}, {"^(```\\n)([\\w\\W]*?)(^```$)", ByGroups(String, Text, String), nil}, - {"^(```)(\\w+)(\\n)([\\w\\W]*?)(^```$)", + { + "^(```)(\\w+)(\\n)([\\w\\W]*?)(^```$)", UsingByGroup( internal.Get, 2, 4, @@ -44,5 +49,5 @@ var Markdown = internal.Register(DelegatingLexer(h.HTML, MustNewLexer( {`[^\\\s]+`, Other, nil}, {`.|\n`, Other, nil}, }, - }, -))) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/m/mason.go b/vendor/github.com/alecthomas/chroma/lexers/m/mason.go index 5c70ab0a90039..bc48f5caaa258 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/m/mason.go +++ b/vendor/github.com/alecthomas/chroma/lexers/m/mason.go @@ -8,7 +8,7 @@ import ( ) // Mason lexer. -var Mason = internal.Register(MustNewLexer( +var Mason = internal.Register(MustNewLazyLexer( &Config{ Name: "Mason", Aliases: []string{"mason"}, @@ -16,7 +16,11 @@ var Mason = internal.Register(MustNewLexer( MimeTypes: []string{"application/x-mason"}, Priority: 0.1, }, - Rules{ + masonRules, +)) + +func masonRules() Rules { + return Rules{ "root": { {`\s+`, Text, nil}, {`(<%doc>)(.*?)()(?s)`, ByGroups(NameTag, CommentMultiline, NameTag), nil}, @@ -39,5 +43,5 @@ var Mason = internal.Register(MustNewLexer( \Z # end of string )`, ByGroups(Using(HTML), Operator), nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/m/mathematica.go b/vendor/github.com/alecthomas/chroma/lexers/m/mathematica.go index 3d1f641f96337..ed1ea24be628c 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/m/mathematica.go +++ b/vendor/github.com/alecthomas/chroma/lexers/m/mathematica.go @@ -6,14 +6,18 @@ import ( ) // Mathematica lexer. -var Mathematica = internal.Register(MustNewLexer( +var Mathematica = internal.Register(MustNewLazyLexer( &Config{ Name: "Mathematica", Aliases: []string{"mathematica", "mma", "nb"}, Filenames: []string{"*.nb", "*.cdf", "*.nbp", "*.ma"}, MimeTypes: []string{"application/mathematica", "application/vnd.wolfram.mathematica", "application/vnd.wolfram.mathematica.package", "application/vnd.wolfram.cdf"}, }, - Rules{ + mathematicaRules, +)) + +func mathematicaRules() Rules { + return Rules{ "root": { {`(?s)\(\*.*?\*\)`, Comment, nil}, {"([a-zA-Z]+[A-Za-z0-9]*`)", NameNamespace, nil}, @@ -28,5 +32,5 @@ var Mathematica = internal.Register(MustNewLexer( {`".*?"`, LiteralString, nil}, {`\s+`, TextWhitespace, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/m/matlab.go b/vendor/github.com/alecthomas/chroma/lexers/m/matlab.go index 5b0baa51548f2..4e98d69776f8d 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/m/matlab.go +++ b/vendor/github.com/alecthomas/chroma/lexers/m/matlab.go @@ -6,14 +6,18 @@ import ( ) // Matlab lexer. -var Matlab = internal.Register(MustNewLexer( +var Matlab = internal.Register(MustNewLazyLexer( &Config{ Name: "Matlab", Aliases: []string{"matlab"}, Filenames: []string{"*.m"}, MimeTypes: []string{"text/matlab"}, }, - Rules{ + matlabRules, +)) + +func matlabRules() Rules { + return Rules{ "root": { {`\n`, Text, nil}, {`^!.*`, LiteralStringOther, nil}, @@ -47,5 +51,5 @@ var Matlab = internal.Register(MustNewLexer( {`(\s*)(?:(.+)(\s*)(=)(\s*))?(.+)(\()(.*)(\))(\s*)`, ByGroups(TextWhitespace, Text, TextWhitespace, Punctuation, TextWhitespace, NameFunction, Punctuation, Text, Punctuation, TextWhitespace), Pop(1)}, {`(\s*)([a-zA-Z_]\w*)`, ByGroups(Text, NameFunction), Pop(1)}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/m/mcfunction.go b/vendor/github.com/alecthomas/chroma/lexers/m/mcfunction.go new file mode 100644 index 0000000000000..1e8eb459279b1 --- /dev/null +++ b/vendor/github.com/alecthomas/chroma/lexers/m/mcfunction.go @@ -0,0 +1,107 @@ +package m + +import ( + . "github.com/alecthomas/chroma" // nolint + "github.com/alecthomas/chroma/lexers/internal" +) + +// mcfunction lexer. +var MCFunction = internal.Register(MustNewLexer( + &Config{ + Name: "mcfunction", + Aliases: []string{"mcfunction"}, + Filenames: []string{"*.mcfunction"}, + MimeTypes: []string{}, + NotMultiline: true, + DotAll: true, + }, + Rules{ + "simplevalue": { + {`(true|false)`, KeywordConstant, nil}, + {`[01]b`, LiteralNumber, nil}, + {`-?(0|[1-9]\d*)(\.\d+[eE](\+|-)?\d+|[eE](\+|-)?\d+|\.\d+)`, LiteralNumberFloat, nil}, + {`(-?\d+)(\.\.)(-?\d+)`, ByGroups(LiteralNumberInteger, Punctuation, LiteralNumberInteger), nil}, + {`-?(0|[1-9]\d*)`, LiteralNumberInteger, nil}, + {`"(\\\\|\\"|[^"])*"`, LiteralStringDouble, nil}, + {`'[^']+'`, LiteralStringSingle, nil}, + {`([!#]?)(\w+)`, ByGroups(Punctuation, Text), nil}, + }, + "nbtobjectattribute": { + Include("nbtvalue"), + {`:`, Punctuation, nil}, + {`,`, Punctuation, Pop(1)}, + {`\}`, Punctuation, Pop(2)}, + }, + "nbtobjectvalue": { + {`("(\\\\|\\"|[^"])*"|[a-zA-Z0-9_]+)`, NameTag, Push("nbtobjectattribute")}, + {`\}`, Punctuation, Pop(1)}, + }, + "nbtarrayvalue": { + Include("nbtvalue"), + {`,`, Punctuation, nil}, + {`\]`, Punctuation, Pop(1)}, + }, + "nbtvalue": { + Include("simplevalue"), + {`\{`, Punctuation, Push("nbtobjectvalue")}, + {`\[`, Punctuation, Push("nbtarrayvalue")}, + }, + "argumentvalue": { + Include("simplevalue"), + {`,`, Punctuation, Pop(1)}, + {`[}\]]`, Punctuation, Pop(2)}, + }, + "argumentlist": { + {`(nbt)(={)`, ByGroups(NameAttribute, Punctuation), Push("nbtobjectvalue")}, + {`([A-Za-z0-9/_!]+)(={)`, ByGroups(NameAttribute, Punctuation), Push("argumentlist")}, + {`([A-Za-z0-9/_!]+)(=)`, ByGroups(NameAttribute, Punctuation), Push("argumentvalue")}, + Include("simplevalue"), + {`,`, Punctuation, nil}, + {`[}\]]`, Punctuation, Pop(1)}, + }, + "root": { + {`#.*?\n`, CommentSingle, nil}, + {Words(`/?`, `\b`, `ability`, `attributes`, `advancement`, + `ban`, `ban-ip`, `banlist`, `bossbar`, + `camerashake`, `classroommode`, `clear`, + `clearspawnpoint`, `clone`, `code`, `collect`, + `createagent`, `data`, `datapack`, `debug`, + `defaultgamemode`, `deop`, `destroy`, `detect`, + `detectredstone`, `difficulty`, `dropall`, + `effect`, `enchant`, `event`, `execute`, + `experience`, `fill`, `flog`, `forceload`, + `function`, `gamemode`, `gamerule`, + `geteduclientinfo`, `give`, `help`, `item`, + `immutableworld`, `kick`, `kill`, `list`, + `locate`, `locatebiome`, `loot`, `me`, `mixer`, + `mobevent`, `move`, `msg`, `music`, `op`, + `pardon`, `particle`, `playanimation`, + `playsound`, `position`, `publish`, + `raytracefog`, `recipe`, `reload`, `remove`, + `replaceitem`, `ride`, `save`, `save-all`, + `save-off`, `save-on`, `say`, `schedule`, + `scoreboard`, `seed`, `setblock`, + `setidletimeout`, `setmaxplayers`, + `setworldspawn`, `spawnpoint`, `spectate`, + `spreadplayers`, `stop`, `stopsound`, + `structure`, `summon`, `tag`, `team`, `teammsg`, + `teleport`, `tell`, `tellraw`, `testfor`, + `testforblock`, `testforblocks`, `tickingarea`, + `time`, `title`, `toggledownfall`, `tp`, + `tpagent`, `transfer`, `transferserver`, + `trigger`, `turn`, `w`, `weather`, `whitelist`, + `worldborder`, `worldbuilder`, `wsserver`, `xp`, + ), KeywordReserved, nil}, + {Words(``, ``, `@p`, `@r`, `@a`, `@e`, `@s`, `@c`, `@v`), + KeywordConstant, nil}, + {`\[`, Punctuation, Push("argumentlist")}, + {`{`, Punctuation, Push("nbtobjectvalue")}, + {`~`, NameBuiltin, nil}, + {`([a-zA-Z_]+:)?[a-zA-Z_]+\b`, Text, nil}, + {`([a-z]+)(\.)([0-9]+)\b`, ByGroups(Text, Punctuation, LiteralNumber), nil}, + {`([<>=]|<=|>=)`, Punctuation, nil}, + Include("simplevalue"), + {`\s+`, TextWhitespace, nil}, + }, + }, +)) diff --git a/vendor/github.com/alecthomas/chroma/lexers/m/minizinc.go b/vendor/github.com/alecthomas/chroma/lexers/m/minizinc.go index ccf4eed99fe1b..63d59ed033e89 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/m/minizinc.go +++ b/vendor/github.com/alecthomas/chroma/lexers/m/minizinc.go @@ -6,14 +6,18 @@ import ( ) // MiniZinc lexer. -var MZN = internal.Register(MustNewLexer( +var MZN = internal.Register(MustNewLazyLexer( &Config{ Name: "MiniZinc", Aliases: []string{"minizinc", "MZN", "mzn"}, Filenames: []string{"*.mzn", "*.dzn", "*.fzn"}, MimeTypes: []string{"text/minizinc"}, }, - Rules{ + mznRules, +)) + +func mznRules() Rules { + return Rules{ "root": { {`\n`, Text, nil}, {`\s+`, Text, nil}, @@ -37,5 +41,5 @@ var MZN = internal.Register(MustNewLexer( {`\b([^\W\d]\w*)\b(\()`, ByGroups(NameFunction, Punctuation), nil}, {`[^\W\d]\w*`, NameOther, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/m/mlir.go b/vendor/github.com/alecthomas/chroma/lexers/m/mlir.go index 2ae4b00b06a76..112a1c3a61bdf 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/m/mlir.go +++ b/vendor/github.com/alecthomas/chroma/lexers/m/mlir.go @@ -6,14 +6,18 @@ import ( ) // MLIR lexer. -var Mlir = internal.Register(MustNewLexer( +var Mlir = internal.Register(MustNewLazyLexer( &Config{ Name: "MLIR", Aliases: []string{"mlir"}, Filenames: []string{"*.mlir"}, MimeTypes: []string{"text/x-mlir"}, }, - Rules{ + mlirRules, +)) + +func mlirRules() Rules { + return Rules{ "root": { Include("whitespace"), {`c?"[^"]*?"`, LiteralString, nil}, @@ -39,5 +43,5 @@ var Mlir = internal.Register(MustNewLexer( {`bf16|f16|f32|f64|index`, Keyword, nil}, {`i[1-9]\d*`, Keyword, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/m/modula2.go b/vendor/github.com/alecthomas/chroma/lexers/m/modula2.go index 6fadb79f374f2..6ab05c9ed3d20 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/m/modula2.go +++ b/vendor/github.com/alecthomas/chroma/lexers/m/modula2.go @@ -6,7 +6,7 @@ import ( ) // Modula-2 lexer. -var Modula2 = internal.Register(MustNewLexer( +var Modula2 = internal.Register(MustNewLazyLexer( &Config{ Name: "Modula-2", Aliases: []string{"modula2", "m2"}, @@ -14,7 +14,11 @@ var Modula2 = internal.Register(MustNewLexer( MimeTypes: []string{"text/x-modula2"}, DotAll: true, }, - Rules{ + modula2Rules, +)) + +func modula2Rules() Rules { + return Rules{ "whitespace": { {`\n+`, Text, nil}, {`\s+`, Text, nil}, @@ -111,5 +115,5 @@ var Modula2 = internal.Register(MustNewLexer( Include("unigraph_punctuation"), Include("unigraph_operators"), }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/m/monkeyc.go b/vendor/github.com/alecthomas/chroma/lexers/m/monkeyc.go index 828ff0bff0e5c..8ad81cc086616 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/m/monkeyc.go +++ b/vendor/github.com/alecthomas/chroma/lexers/m/monkeyc.go @@ -5,14 +5,18 @@ import ( "github.com/alecthomas/chroma/lexers/internal" ) -var MonkeyC = internal.Register(MustNewLexer( +var MonkeyC = internal.Register(MustNewLazyLexer( &Config{ Name: "MonkeyC", Aliases: []string{"monkeyc"}, Filenames: []string{"*.mc"}, MimeTypes: []string{"text/x-monkeyc"}, }, - Rules{ + monkeyCRules, +)) + +func monkeyCRules() Rules { + return Rules{ "root": { {`[^\S\n]+`, Text, nil}, {`\n`, Text, nil}, @@ -58,5 +62,5 @@ var MonkeyC = internal.Register(MustNewLexer( {`[a-zA-Z_][\w_\.]*`, NameNamespace, nil}, Default(Pop(1)), }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/m/mwscript.go b/vendor/github.com/alecthomas/chroma/lexers/m/mwscript.go index b86bbdfeefce5..0ba0ba97c315b 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/m/mwscript.go +++ b/vendor/github.com/alecthomas/chroma/lexers/m/mwscript.go @@ -6,14 +6,18 @@ import ( ) // MorrowindScript lexer. -var MorrowindScript = internal.Register(MustNewLexer( +var MorrowindScript = internal.Register(MustNewLazyLexer( &Config{ Name: "MorrowindScript", Aliases: []string{"morrowind", "mwscript"}, Filenames: []string{}, MimeTypes: []string{}, }, - Rules{ + morrowindScriptRules, +)) + +func morrowindScriptRules() Rules { + return Rules{ "root": { {`\s+`, Text, nil}, {`;.*$`, Comment, nil}, @@ -49,5 +53,5 @@ var MorrowindScript = internal.Register(MustNewLexer( {`[#=,./%+\-?]`, Operator, nil}, {`(==|<=|<|>=|>|!=)`, Operator, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/m/myghty.go b/vendor/github.com/alecthomas/chroma/lexers/m/myghty.go index 02a20eae5e4b7..22227c5044141 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/m/myghty.go +++ b/vendor/github.com/alecthomas/chroma/lexers/m/myghty.go @@ -7,14 +7,18 @@ import ( ) // Myghty lexer. -var Myghty = internal.Register(MustNewLexer( +var Myghty = internal.Register(MustNewLazyLexer( &Config{ Name: "Myghty", Aliases: []string{"myghty"}, Filenames: []string{"*.myt", "autodelegate"}, MimeTypes: []string{"application/x-myghty"}, }, - Rules{ + myghtyRules, +)) + +func myghtyRules() Rules { + return Rules{ "root": { {`\s+`, Text, nil}, {`(<%(?:def|method))(\s*)(.*?)(>)(.*?)()(?s)`, ByGroups(NameTag, Text, NameFunction, NameTag, UsingSelf("root"), NameTag), nil}, @@ -36,5 +40,5 @@ var Myghty = internal.Register(MustNewLexer( \Z # end of string )`, ByGroups(Other, Operator), nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/m/mysql.go b/vendor/github.com/alecthomas/chroma/lexers/m/mysql.go index 9f47e32a20d4f..7e08e242c7ef2 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/m/mysql.go +++ b/vendor/github.com/alecthomas/chroma/lexers/m/mysql.go @@ -6,7 +6,7 @@ import ( ) // MySQL lexer. -var MySQL = internal.Register(MustNewLexer( +var MySQL = internal.Register(MustNewLazyLexer( &Config{ Name: "MySQL", Aliases: []string{"mysql"}, @@ -15,9 +15,13 @@ var MySQL = internal.Register(MustNewLexer( NotMultiline: true, CaseInsensitive: true, }, - Rules{ + mySQLRules, +)) + +func mySQLRules() Rules { + return Rules{ "root": { - {`\s+`, Text, nil}, + {`\s+`, TextWhitespace, nil}, {`(#|--\s+).*\n?`, CommentSingle, nil}, {`/\*`, CommentMultiline, Push("multiline-comments")}, {`[0-9]+`, LiteralNumberInteger, nil}, @@ -25,11 +29,11 @@ var MySQL = internal.Register(MustNewLexer( {`((?:_[a-z0-9]+)?)(')`, ByGroups(LiteralStringAffix, LiteralStringSingle), Push("string")}, {`((?:_[a-z0-9]+)?)(")`, ByGroups(LiteralStringAffix, LiteralStringDouble), Push("double-string")}, {"[+*/<>=~!@#%^&|`?-]", Operator, nil}, - {`\b(tinyint|smallint|mediumint|int|integer|bigint|date|datetime|time|bit|bool|tinytext|mediumtext|longtext|text|tinyblob|mediumblob|longblob|blob|float|double|double\s+precision|real|numeric|dec|decimal|timestamp|year|char|varchar|varbinary|varcharacter|enum|set)(\b\s*)(\()?`, ByGroups(KeywordType, Text, Punctuation), nil}, + {`\b(tinyint|smallint|mediumint|int|integer|bigint|date|datetime|time|bit|bool|tinytext|mediumtext|longtext|text|tinyblob|mediumblob|longblob|blob|float|double|double\s+precision|real|numeric|dec|decimal|timestamp|year|char|varchar|varbinary|varcharacter|enum|set)(\b\s*)(\()?`, ByGroups(KeywordType, TextWhitespace, Punctuation), nil}, {`\b(add|all|alter|analyze|and|as|asc|asensitive|before|between|bigint|binary|blob|both|by|call|cascade|case|change|char|character|check|collate|column|condition|constraint|continue|convert|create|cross|current_date|current_time|current_timestamp|current_user|cursor|database|databases|day_hour|day_microsecond|day_minute|day_second|dec|decimal|declare|default|delayed|delete|desc|describe|deterministic|distinct|distinctrow|div|double|drop|dual|each|else|elseif|enclosed|escaped|exists|exit|explain|fetch|flush|float|float4|float8|for|force|foreign|from|fulltext|grant|group|having|high_priority|hour_microsecond|hour_minute|hour_second|identified|if|ignore|in|index|infile|inner|inout|insensitive|insert|int|int1|int2|int3|int4|int8|integer|interval|into|is|iterate|join|key|keys|kill|leading|leave|left|like|limit|lines|load|localtime|localtimestamp|lock|long|loop|low_priority|match|minute_microsecond|minute_second|mod|modifies|natural|no_write_to_binlog|not|numeric|on|optimize|option|optionally|or|order|out|outer|outfile|precision|primary|privileges|procedure|purge|raid0|read|reads|real|references|regexp|release|rename|repeat|replace|require|restrict|return|revoke|right|rlike|schema|schemas|second_microsecond|select|sensitive|separator|set|show|smallint|soname|spatial|specific|sql|sql_big_result|sql_calc_found_rows|sql_small_result|sqlexception|sqlstate|sqlwarning|ssl|starting|straight_join|table|terminated|then|to|trailing|trigger|undo|union|unique|unlock|unsigned|update|usage|use|user|using|utc_date|utc_time|utc_timestamp|values|varying|when|where|while|with|write|x509|xor|year_month|zerofill)\b`, Keyword, nil}, {`\b(auto_increment|engine|charset|tables)\b`, KeywordPseudo, nil}, {`(true|false|null)`, NameConstant, nil}, - {`([a-z_]\w*)(\s*)(\()`, ByGroups(NameFunction, Text, Punctuation), nil}, + {`([a-z_]\w*)(\s*)(\()`, ByGroups(NameFunction, TextWhitespace, Punctuation), nil}, {`[a-z_]\w*`, Name, nil}, {`@[a-z0-9]*[._]*[a-z0-9]*`, NameVariable, nil}, {`[;:()\[\],.]`, Punctuation, nil}, @@ -50,5 +54,5 @@ var MySQL = internal.Register(MustNewLexer( {`""`, LiteralStringDouble, nil}, {`"`, LiteralStringDouble, Pop(1)}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/n/nasm.go b/vendor/github.com/alecthomas/chroma/lexers/n/nasm.go index d769d15752464..de6734e8659ec 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/n/nasm.go +++ b/vendor/github.com/alecthomas/chroma/lexers/n/nasm.go @@ -6,7 +6,7 @@ import ( ) // Nasm lexer. -var Nasm = internal.Register(MustNewLexer( +var Nasm = internal.Register(MustNewLazyLexer( &Config{ Name: "NASM", Aliases: []string{"nasm"}, @@ -14,7 +14,11 @@ var Nasm = internal.Register(MustNewLexer( MimeTypes: []string{"text/x-nasm"}, CaseInsensitive: true, }, - Rules{ + nasmRules, +)) + +func nasmRules() Rules { + return Rules{ "root": { {`^\s*%`, CommentPreproc, Push("preproc")}, Include("whitespace"), @@ -55,5 +59,5 @@ var Nasm = internal.Register(MustNewLexer( {`seg|wrt|strict`, OperatorWord, nil}, {`byte|[dq]?word`, KeywordType, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/n/newspeak.go b/vendor/github.com/alecthomas/chroma/lexers/n/newspeak.go index 1a6a37bd21cc8..c9d3ae032b3d9 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/n/newspeak.go +++ b/vendor/github.com/alecthomas/chroma/lexers/n/newspeak.go @@ -6,14 +6,18 @@ import ( ) // Newspeak lexer. -var Newspeak = internal.Register(MustNewLexer( +var Newspeak = internal.Register(MustNewLazyLexer( &Config{ Name: "Newspeak", Aliases: []string{"newspeak"}, Filenames: []string{"*.ns2"}, MimeTypes: []string{"text/x-newspeak"}, }, - Rules{ + newspeakRules, +)) + +func newspeakRules() Rules { + return Rules{ "root": { {`\b(Newsqueak2)\b`, KeywordDeclaration, nil}, {`'[^']*'`, LiteralString, nil}, @@ -51,5 +55,5 @@ var Newspeak = internal.Register(MustNewLexer( {`\s+`, Text, nil}, {`"[^"]*"`, Comment, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/n/nginx.go b/vendor/github.com/alecthomas/chroma/lexers/n/nginx.go index 840d100b7e507..6d80523ec781e 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/n/nginx.go +++ b/vendor/github.com/alecthomas/chroma/lexers/n/nginx.go @@ -6,14 +6,18 @@ import ( ) // Nginx Configuration File lexer. -var Nginx = internal.Register(MustNewLexer( +var Nginx = internal.Register(MustNewLazyLexer( &Config{ Name: "Nginx configuration file", Aliases: []string{"nginx"}, Filenames: []string{"nginx.conf"}, MimeTypes: []string{"text/x-nginx-conf"}, }, - Rules{ + nginxRules, +)) + +func nginxRules() Rules { + return Rules{ "root": { {`(include)(\s+)([^\s;]+)`, ByGroups(Keyword, Text, Name), nil}, {`[^\s;#]+`, Keyword, Push("stmt")}, @@ -43,5 +47,5 @@ var Nginx = internal.Register(MustNewLexer( {`\s+`, Text, nil}, {`[$;]`, Text, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/n/nim.go b/vendor/github.com/alecthomas/chroma/lexers/n/nim.go index b08c2f94d4585..3f98086b45f02 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/n/nim.go +++ b/vendor/github.com/alecthomas/chroma/lexers/n/nim.go @@ -6,7 +6,7 @@ import ( ) // Nim lexer. -var Nim = internal.Register(MustNewLexer( +var Nim = internal.Register(MustNewLazyLexer( &Config{ Name: "Nim", Aliases: []string{"nim", "nimrod"}, @@ -14,7 +14,11 @@ var Nim = internal.Register(MustNewLexer( MimeTypes: []string{"text/x-nim"}, CaseInsensitive: true, }, - Rules{ + nimRules, +)) + +func nimRules() Rules { + return Rules{ "root": { {`#\[[\s\S]*?\]#`, CommentMultiline, nil}, {`##.*$`, LiteralStringDoc, nil}, @@ -89,5 +93,5 @@ var Nim = internal.Register(MustNewLexer( {`\'i(8|16)`, LiteralNumberInteger, nil}, Default(Pop(1)), }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/n/nix.go b/vendor/github.com/alecthomas/chroma/lexers/n/nix.go index 7e21dd7c9dbaf..874560a565ffb 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/n/nix.go +++ b/vendor/github.com/alecthomas/chroma/lexers/n/nix.go @@ -7,18 +7,22 @@ import ( "github.com/alecthomas/chroma/lexers/internal" ) -// nixb matches right boundary of a nix word. Use it instead of \b. -const nixb = `(?![a-zA-Z0-9_'-])` - // Nix lexer. -var Nix = internal.Register(MustNewLexer( +var Nix = internal.Register(MustNewLazyLexer( &Config{ Name: "Nix", Aliases: []string{"nixos", "nix"}, Filenames: []string{"*.nix"}, MimeTypes: []string{"text/x-nix"}, }, - Rules{ + nixRules, +)) + +func nixRules() Rules { + // nixb matches right boundary of a nix word. Use it instead of \b. + const nixb = `(?![a-zA-Z0-9_'-])` + + return Rules{ "root": { Include("keywords"), Include("builtins"), @@ -118,5 +122,5 @@ var Nix = internal.Register(MustNewLexer( "space": { {`[ \t\r\n]+`, Text, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/o/objectivec.go b/vendor/github.com/alecthomas/chroma/lexers/o/objectivec.go index e3d0b1c4fc5fa..0ae3029609438 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/o/objectivec.go +++ b/vendor/github.com/alecthomas/chroma/lexers/o/objectivec.go @@ -6,14 +6,18 @@ import ( ) // Objective-C lexer. -var ObjectiveC = internal.Register(MustNewLexer( +var ObjectiveC = internal.Register(MustNewLazyLexer( &Config{ Name: "Objective-C", Aliases: []string{"objective-c", "objectivec", "obj-c", "objc"}, Filenames: []string{"*.m", "*.h"}, MimeTypes: []string{"text/x-objective-c"}, }, - Rules{ + objectiveCRules, +)) + +func objectiveCRules() Rules { + return Rules{ "statements": { {`@"`, LiteralString, Push("string")}, {`@(YES|NO)`, LiteralNumber, nil}, @@ -161,5 +165,5 @@ var ObjectiveC = internal.Register(MustNewLexer( {`^\s*#endif.*?(?]*)(>)", ByGroups(Punctuation, CommentPreprocFile, Punctuation), nil}, Default(Pop(1)), }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/o/org.go b/vendor/github.com/alecthomas/chroma/lexers/o/org.go index 1064eaf0f0afa..00f6df44502a2 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/o/org.go +++ b/vendor/github.com/alecthomas/chroma/lexers/o/org.go @@ -6,14 +6,18 @@ import ( ) // Org mode lexer. -var Org = internal.Register(MustNewLexer( +var Org = internal.Register(MustNewLazyLexer( &Config{ Name: "Org Mode", Aliases: []string{"org", "orgmode"}, Filenames: []string{"*.org"}, MimeTypes: []string{"text/org"}, // https://lists.gnu.org/r/emacs-orgmode/2017-09/msg00087.html }, - Rules{ + orgRules, +)) + +func orgRules() Rules { + return Rules{ "root": { {`^# .*$`, Comment, nil}, // Headings @@ -45,7 +49,8 @@ var Org = internal.Register(MustNewLexer( // - Comment Blocks {`(?i)^( *#\+begin_comment *\n)([\w\W]*?)(^ *#\+end_comment *$)`, ByGroups(Comment, Comment, Comment), nil}, // - Src Blocks - {`(?i)^( *#\+begin_src )([^ \n]+)(.*?\n)([\w\W]*?)(^ *#\+end_src *$)`, + { + `(?i)^( *#\+begin_src )([^ \n]+)(.*?\n)([\w\W]*?)(^ *#\+end_src *$)`, UsingByGroup( internal.Get, 2, 4, @@ -54,7 +59,8 @@ var Org = internal.Register(MustNewLexer( nil, }, // - Export Blocks - {`(?i)^( *#\+begin_export )(\w+)( *\n)([\w\W]*?)(^ *#\+end_export *$)`, + { + `(?i)^( *#\+begin_export )(\w+)( *\n)([\w\W]*?)(^ *#\+end_export *$)`, UsingByGroup( internal.Get, 2, 4, @@ -98,5 +104,5 @@ var Org = internal.Register(MustNewLexer( // Any other text {`.`, Text, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/p/pacman.go b/vendor/github.com/alecthomas/chroma/lexers/p/pacman.go index 807b67c56438b..00c6255af6f50 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/p/pacman.go +++ b/vendor/github.com/alecthomas/chroma/lexers/p/pacman.go @@ -6,14 +6,18 @@ import ( ) // Pacmanconf lexer. -var Pacmanconf = internal.Register(MustNewLexer( +var Pacmanconf = internal.Register(MustNewLazyLexer( &Config{ Name: "PacmanConf", Aliases: []string{"pacmanconf"}, Filenames: []string{"pacman.conf"}, MimeTypes: []string{}, }, - Rules{ + pacmanconfRules, +)) + +func pacmanconfRules() Rules { + return Rules{ "root": { {`#.*$`, CommentSingle, nil}, {`^\s*\[.*?\]\s*$`, Keyword, nil}, @@ -22,5 +26,5 @@ var Pacmanconf = internal.Register(MustNewLexer( {Words(``, `\b`, `$repo`, `$arch`, `%o`, `%u`), NameVariable, nil}, {`.`, Text, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/p/perl.go b/vendor/github.com/alecthomas/chroma/lexers/p/perl.go index 0a2b35b9a3b6a..6aa338ad116b7 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/p/perl.go +++ b/vendor/github.com/alecthomas/chroma/lexers/p/perl.go @@ -6,7 +6,7 @@ import ( ) // Perl lexer. -var Perl = internal.Register(MustNewLexer( +var Perl = internal.Register(MustNewLazyLexer( &Config{ Name: "Perl", Aliases: []string{"perl", "pl"}, @@ -14,7 +14,11 @@ var Perl = internal.Register(MustNewLexer( MimeTypes: []string{"text/x-perl", "application/x-perl"}, DotAll: true, }, - Rules{ + perlRules, +)) + +func perlRules() Rules { + return Rules{ "balanced-regex": { {`/(\\\\|\\[^\\]|[^\\/])*/[egimosx]*`, LiteralStringRegex, Pop(1)}, {`!(\\\\|\\[^\\]|[^\\!])*![egimosx]*`, LiteralStringRegex, Pop(1)}, @@ -134,5 +138,5 @@ var Perl = internal.Register(MustNewLexer( "end-part": { {`.+`, CommentPreproc, Pop(1)}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/p/pig.go b/vendor/github.com/alecthomas/chroma/lexers/p/pig.go index 0dbc591ad185c..f2852c18ae9ec 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/p/pig.go +++ b/vendor/github.com/alecthomas/chroma/lexers/p/pig.go @@ -6,7 +6,7 @@ import ( ) // Pig lexer. -var Pig = internal.Register(MustNewLexer( +var Pig = internal.Register(MustNewLazyLexer( &Config{ Name: "Pig", Aliases: []string{"pig"}, @@ -14,7 +14,11 @@ var Pig = internal.Register(MustNewLexer( MimeTypes: []string{"text/x-pig"}, CaseInsensitive: true, }, - Rules{ + pigRules, +)) + +func pigRules() Rules { + return Rules{ "root": { {`\s+`, Text, nil}, {`--.*`, Comment, nil}, @@ -53,5 +57,5 @@ var Pig = internal.Register(MustNewLexer( {`(eq|gt|lt|gte|lte|neq|matches)\b`, Operator, nil}, {`(==|<=|<|>=|>|!=)`, Operator, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/p/pkgconfig.go b/vendor/github.com/alecthomas/chroma/lexers/p/pkgconfig.go index 0a4872e76566b..9e024107f70fc 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/p/pkgconfig.go +++ b/vendor/github.com/alecthomas/chroma/lexers/p/pkgconfig.go @@ -6,14 +6,18 @@ import ( ) // Pkgconfig lexer. -var Pkgconfig = internal.Register(MustNewLexer( +var Pkgconfig = internal.Register(MustNewLazyLexer( &Config{ Name: "PkgConfig", Aliases: []string{"pkgconfig"}, Filenames: []string{"*.pc"}, MimeTypes: []string{}, }, - Rules{ + pkgconfigRules, +)) + +func pkgconfigRules() Rules { + return Rules{ "root": { {`#.*$`, CommentSingle, nil}, {`^(\w+)(=)`, ByGroups(NameAttribute, Operator), nil}, @@ -37,5 +41,5 @@ var Pkgconfig = internal.Register(MustNewLexer( {`[^${}#\n]+`, Text, nil}, {`.`, Text, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/p/plaintext.go b/vendor/github.com/alecthomas/chroma/lexers/p/plaintext.go index 7b252930fb57f..058c02a89fec6 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/p/plaintext.go +++ b/vendor/github.com/alecthomas/chroma/lexers/p/plaintext.go @@ -5,7 +5,7 @@ import ( "github.com/alecthomas/chroma/lexers/internal" ) -var Plaintext = internal.Register(MustNewLexer( +var Plaintext = internal.Register(MustNewLazyLexer( &Config{ Name: "plaintext", Aliases: []string{"text", "plain", "no-highlight"}, diff --git a/vendor/github.com/alecthomas/chroma/lexers/p/plsql.go b/vendor/github.com/alecthomas/chroma/lexers/p/plsql.go index a6068fd2215a3..324d3feb2aee5 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/p/plsql.go +++ b/vendor/github.com/alecthomas/chroma/lexers/p/plsql.go @@ -6,7 +6,7 @@ import ( ) // Pl/Pgsql lexer. -var PLpgSQL = internal.Register(MustNewLexer( +var PLpgSQL = internal.Register(MustNewLazyLexer( &Config{ Name: "PL/pgSQL", Aliases: []string{"plpgsql"}, @@ -15,13 +15,17 @@ var PLpgSQL = internal.Register(MustNewLexer( NotMultiline: true, CaseInsensitive: true, }, - Rules{ + plpgSQLRules, +)) + +func plpgSQLRules() Rules { + return Rules{ "root": { {`\%[a-z]\w*\b`, NameBuiltin, nil}, {`:=`, Operator, nil}, {`\<\<[a-z]\w*\>\>`, NameLabel, nil}, {`\#[a-z]\w*\b`, KeywordPseudo, nil}, - {`\s+`, Text, nil}, + {`\s+`, TextWhitespace, nil}, {`--.*\n?`, CommentSingle, nil}, {`/\*`, CommentMultiline, Push("multiline-comments")}, {`(bigint|bigserial|bit|bit\s+varying|bool|boolean|box|bytea|char|character|character\s+varying|cidr|circle|date|decimal|double\s+precision|float4|float8|inet|int|int2|int4|int8|integer|interval|json|jsonb|line|lseg|macaddr|money|numeric|path|pg_lsn|point|polygon|real|serial|serial2|serial4|serial8|smallint|smallserial|text|time|timestamp|timestamptz|timetz|tsquery|tsvector|txid_snapshot|uuid|varbit|varchar|with\s+time\s+zone|without\s+time\s+zone|xml|anyarray|anyelement|anyenum|anynonarray|anyrange|cstring|fdw_handler|internal|language_handler|opaque|record|void)\b`, NameBuiltin, nil}, @@ -54,5 +58,5 @@ var PLpgSQL = internal.Register(MustNewLexer( {`""`, LiteralStringName, nil}, {`"`, LiteralStringName, Pop(1)}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/p/pony.go b/vendor/github.com/alecthomas/chroma/lexers/p/pony.go index 9696b2079b786..41d568289a342 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/p/pony.go +++ b/vendor/github.com/alecthomas/chroma/lexers/p/pony.go @@ -6,14 +6,18 @@ import ( ) // Pony lexer. -var Pony = internal.Register(MustNewLexer( +var Pony = internal.Register(MustNewLazyLexer( &Config{ Name: "Pony", Aliases: []string{"pony"}, Filenames: []string{"*.pony"}, MimeTypes: []string{}, }, - Rules{ + ponyRules, +)) + +func ponyRules() Rules { + return Rules{ "root": { {`\n`, Text, nil}, {`[^\S\n]+`, Text, nil}, @@ -55,5 +59,5 @@ var Pony = internal.Register(MustNewLexer( {`\\"`, LiteralString, nil}, {`[^\\"]+`, LiteralString, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/p/postgres.go b/vendor/github.com/alecthomas/chroma/lexers/p/postgres.go index 3afa54debba40..8977e2cfd4017 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/p/postgres.go +++ b/vendor/github.com/alecthomas/chroma/lexers/p/postgres.go @@ -6,7 +6,7 @@ import ( ) // Postgresql Sql Dialect lexer. -var PostgreSQL = internal.Register(MustNewLexer( +var PostgreSQL = internal.Register(MustNewLazyLexer( &Config{ Name: "PostgreSQL SQL dialect", Aliases: []string{"postgresql", "postgres"}, @@ -15,13 +15,18 @@ var PostgreSQL = internal.Register(MustNewLexer( NotMultiline: true, CaseInsensitive: true, }, - Rules{ + postgreSQLRules, +)) + +func postgreSQLRules() Rules { + return Rules{ "root": { {`\s+`, Text, nil}, {`--.*\n?`, CommentSingle, nil}, {`/\*`, CommentMultiline, Push("multiline-comments")}, {`(bigint|bigserial|bit|bit\s+varying|bool|boolean|box|bytea|char|character|character\s+varying|cidr|circle|date|decimal|double\s+precision|float4|float8|inet|int|int2|int4|int8|integer|interval|json|jsonb|line|lseg|macaddr|money|numeric|path|pg_lsn|point|polygon|real|serial|serial2|serial4|serial8|smallint|smallserial|text|time|timestamp|timestamptz|timetz|tsquery|tsvector|txid_snapshot|uuid|varbit|varchar|with\s+time\s+zone|without\s+time\s+zone|xml|anyarray|anyelement|anyenum|anynonarray|anyrange|cstring|fdw_handler|internal|language_handler|opaque|record|void)\b`, NameBuiltin, nil}, - {`(?s)(DO)(\s+)(?:(LANGUAGE)?(\s+)('?)(\w+)?('?)(\s+))?(\$)([^$]*)(\$)(.*?)(\$)(\10)(\$)`, + { + `(?s)(DO)(\s+)(?:(LANGUAGE)?(\s+)('?)(\w+)?('?)(\s+))?(\$)([^$]*)(\$)(.*?)(\$)(\10)(\$)`, UsingByGroup( internal.Get, 6, 12, @@ -41,7 +46,8 @@ var PostgreSQL = internal.Register(MustNewLexer( {`[0-9]+`, LiteralNumberInteger, nil}, {`((?:E|U&)?)(')`, ByGroups(LiteralStringAffix, LiteralStringSingle), Push("string")}, {`((?:U&)?)(")`, ByGroups(LiteralStringAffix, LiteralStringName), Push("quoted-ident")}, - {`(?s)(\$)([^$]*)(\$)(.*?)(\$)(\2)(\$)(\s+)(LANGUAGE)?(\s+)('?)(\w+)?('?)`, + { + `(?s)(\$)([^$]*)(\$)(.*?)(\$)(\2)(\$)(\s+)(LANGUAGE)?(\s+)('?)(\w+)?('?)`, UsingByGroup(internal.Get, 12, 4, StringHeredoc, StringHeredoc, StringHeredoc, // $tag$ @@ -73,5 +79,5 @@ var PostgreSQL = internal.Register(MustNewLexer( {`""`, LiteralStringName, nil}, {`"`, LiteralStringName, Pop(1)}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/p/postscript.go b/vendor/github.com/alecthomas/chroma/lexers/p/postscript.go index 0cfa3a5f1295c..0b51ba5ef3944 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/p/postscript.go +++ b/vendor/github.com/alecthomas/chroma/lexers/p/postscript.go @@ -6,14 +6,18 @@ import ( ) // Postscript lexer. -var Postscript = internal.Register(MustNewLexer( +var Postscript = internal.Register(MustNewLazyLexer( &Config{ Name: "PostScript", Aliases: []string{"postscript", "postscr"}, Filenames: []string{"*.ps", "*.eps"}, MimeTypes: []string{"application/postscript"}, }, - Rules{ + postscriptRules, +)) + +func postscriptRules() Rules { + return Rules{ "root": { {`^%!.+\n`, CommentPreproc, nil}, {`%%.*\n`, CommentSpecial, nil}, @@ -42,5 +46,5 @@ var Postscript = internal.Register(MustNewLexer( {`[0-8]{3}|n|r|t|b|f|\\|\(|\)`, LiteralStringEscape, Pop(1)}, Default(Pop(1)), }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/p/povray.go b/vendor/github.com/alecthomas/chroma/lexers/p/povray.go index 6f1bb52493f7c..2d870f1063a3b 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/p/povray.go +++ b/vendor/github.com/alecthomas/chroma/lexers/p/povray.go @@ -6,14 +6,18 @@ import ( ) // Povray lexer. -var Povray = internal.Register(MustNewLexer( +var Povray = internal.Register(MustNewLazyLexer( &Config{ Name: "POVRay", Aliases: []string{"pov"}, Filenames: []string{"*.pov", "*.inc"}, MimeTypes: []string{"text/x-povray"}, }, - Rules{ + povrayRules, +)) + +func povrayRules() Rules { + return Rules{ "root": { {`/\*[\w\W]*?\*/`, CommentMultiline, nil}, {`//.*\n`, CommentSingle, nil}, @@ -31,5 +35,5 @@ var Povray = internal.Register(MustNewLexer( {`"(\\\\|\\"|[^"])*"`, LiteralString, nil}, {`\s+`, Text, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/p/powershell.go b/vendor/github.com/alecthomas/chroma/lexers/p/powershell.go index 10eba4f955f92..9b7b56ee02085 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/p/powershell.go +++ b/vendor/github.com/alecthomas/chroma/lexers/p/powershell.go @@ -6,16 +6,20 @@ import ( ) // Powershell lexer. -var Powershell = internal.Register(MustNewLexer( +var Powershell = internal.Register(MustNewLazyLexer( &Config{ Name: "PowerShell", - Aliases: []string{"powershell", "posh", "ps1", "psm1"}, - Filenames: []string{"*.ps1", "*.psm1"}, + Aliases: []string{"powershell", "posh", "ps1", "psm1", "psd1"}, + Filenames: []string{"*.ps1", "*.psm1", "*.psd1"}, MimeTypes: []string{"text/x-powershell"}, DotAll: true, CaseInsensitive: true, }, - Rules{ + powershellRules, +)) + +func powershellRules() Rules { + return Rules{ "root": { {`\(`, Punctuation, Push("child")}, {`\s+`, Text, nil}, @@ -29,9 +33,9 @@ var Powershell = internal.Register(MustNewLexer( {`"`, LiteralStringDouble, Push("string")}, {`'([^']|'')*'`, LiteralStringSingle, nil}, {`(\$|@@|@)((global|script|private|env):)?\w+`, NameVariable, nil}, + {`[a-z]\w*-[a-z]\w*\b`, NameBuiltin, nil}, {`(while|validateset|validaterange|validatepattern|validatelength|validatecount|until|trap|switch|return|ref|process|param|parameter|in|if|global:|function|foreach|for|finally|filter|end|elseif|else|dynamicparam|do|default|continue|cmdletbinding|break|begin|alias|\?|%|#script|#private|#local|#global|mandatory|parametersetname|position|valuefrompipeline|valuefrompipelinebypropertyname|valuefromremainingarguments|helpmessage|try|catch|throw)\b`, Keyword, nil}, {`-(and|as|band|bnot|bor|bxor|casesensitive|ccontains|ceq|cge|cgt|cle|clike|clt|cmatch|cne|cnotcontains|cnotlike|cnotmatch|contains|creplace|eq|exact|f|file|ge|gt|icontains|ieq|ige|igt|ile|ilike|ilt|imatch|ine|inotcontains|inotlike|inotmatch|ireplace|is|isnot|le|like|lt|match|ne|not|notcontains|notlike|notmatch|or|regex|replace|wildcard)\b`, Operator, nil}, - {`(write|where|watch|wait|use|update|unregister|unpublish|unprotect|unlock|uninstall|undo|unblock|trace|test|tee|take|sync|switch|suspend|submit|stop|step|start|split|sort|skip|show|set|send|select|search|scroll|save|revoke|resume|restore|restart|resolve|resize|reset|request|repair|rename|remove|register|redo|receive|read|push|publish|protect|pop|ping|out|optimize|open|new|move|mount|merge|measure|lock|limit|join|invoke|install|initialize|import|hide|group|grant|get|format|foreach|find|export|expand|exit|enter|enable|edit|dismount|disconnect|disable|deny|debug|cxnew|copy|convertto|convertfrom|convert|connect|confirm|compress|complete|compare|close|clear|checkpoint|block|backup|assert|approve|aggregate|add)-[a-z_]\w*\b`, NameBuiltin, nil}, {`(ac|asnp|cat|cd|cfs|chdir|clc|clear|clhy|cli|clp|cls|clv|cnsn|compare|copy|cp|cpi|cpp|curl|cvpa|dbp|del|diff|dir|dnsn|ebp|echo|epal|epcsv|epsn|erase|etsn|exsn|fc|fhx|fl|foreach|ft|fw|gal|gbp|gc|gci|gcm|gcs|gdr|ghy|gi|gjb|gl|gm|gmo|gp|gps|gpv|group|gsn|gsnp|gsv|gu|gv|gwmi|h|history|icm|iex|ihy|ii|ipal|ipcsv|ipmo|ipsn|irm|ise|iwmi|iwr|kill|lp|ls|man|md|measure|mi|mount|move|mp|mv|nal|ndr|ni|nmo|npssc|nsn|nv|ogv|oh|popd|ps|pushd|pwd|r|rbp|rcjb|rcsn|rd|rdr|ren|ri|rjb|rm|rmdir|rmo|rni|rnp|rp|rsn|rsnp|rujb|rv|rvpa|rwmi|sajb|sal|saps|sasv|sbp|sc|select|set|shcm|si|sl|sleep|sls|sort|sp|spjb|spps|spsv|start|sujb|sv|swmi|tee|trcm|type|wget|where|wjb|write)\s`, NameBuiltin, nil}, {"\\[[a-z_\\[][\\w. `,\\[\\]]*\\]", NameConstant, nil}, {`-[a-z_]\w*`, Name, nil}, @@ -62,5 +66,5 @@ var Powershell = internal.Register(MustNewLexer( {`[^@\n]+"]`, LiteralStringHeredoc, nil}, {`.`, LiteralStringHeredoc, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/p/prolog.go b/vendor/github.com/alecthomas/chroma/lexers/p/prolog.go index 08b48121b3428..a2f346b6263f4 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/p/prolog.go +++ b/vendor/github.com/alecthomas/chroma/lexers/p/prolog.go @@ -6,14 +6,18 @@ import ( ) // Prolog lexer. -var Prolog = internal.Register(MustNewLexer( +var Prolog = internal.Register(MustNewLazyLexer( &Config{ Name: "Prolog", Aliases: []string{"prolog"}, Filenames: []string{"*.ecl", "*.prolog", "*.pro", "*.pl"}, MimeTypes: []string{"text/x-prolog"}, }, - Rules{ + prologRules, +)) + +func prologRules() Rules { + return Rules{ "root": { {`/\*`, CommentMultiline, Push("nested-comment")}, {`%.*`, CommentSingle, nil}, @@ -46,5 +50,5 @@ var Prolog = internal.Register(MustNewLexer( {`[^*/]+`, CommentMultiline, nil}, {`[*/]`, CommentMultiline, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/p/promql.go b/vendor/github.com/alecthomas/chroma/lexers/p/promql.go index 38982e16e2398..e80b926c2edb0 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/p/promql.go +++ b/vendor/github.com/alecthomas/chroma/lexers/p/promql.go @@ -6,14 +6,18 @@ import ( ) // Promql lexer. -var Promql = internal.Register(MustNewLexer( +var Promql = internal.Register(MustNewLazyLexer( &Config{ Name: "PromQL", Aliases: []string{"promql"}, Filenames: []string{"*.promql"}, MimeTypes: []string{}, }, - Rules{ + promqlRules, +)) + +func promqlRules() Rules { + return Rules{ "root": { {`\n`, TextWhitespace, nil}, {`\s+`, TextWhitespace, nil}, @@ -40,7 +44,7 @@ var Promql = internal.Register(MustNewLexer( {`\n`, TextWhitespace, nil}, {`\s+`, TextWhitespace, nil}, {`,`, Punctuation, nil}, - {`([_a-zA-Z][a-zA-Z0-9_]*?)(\s*?)(=~|!=|=|~!)(\s*?)(")(.*?)(")`, ByGroups(NameLabel, TextWhitespace, Operator, TextWhitespace, Punctuation, LiteralString, Punctuation), nil}, + {`([_a-zA-Z][a-zA-Z0-9_]*?)(\s*?)(=~|!=|=|!~)(\s*?)("|')(.*?)("|')`, ByGroups(NameLabel, TextWhitespace, Operator, TextWhitespace, Punctuation, LiteralString, Punctuation), nil}, }, "range": { {`\]`, Punctuation, Pop(1)}, @@ -51,5 +55,5 @@ var Promql = internal.Register(MustNewLexer( {`\(`, Operator, Push()}, Default(Pop(1)), }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/p/protobuf.go b/vendor/github.com/alecthomas/chroma/lexers/p/protobuf.go index 30140131c8b40..76576cb3501df 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/p/protobuf.go +++ b/vendor/github.com/alecthomas/chroma/lexers/p/protobuf.go @@ -6,14 +6,18 @@ import ( ) // ProtocolBuffer lexer. -var ProtocolBuffer = internal.Register(MustNewLexer( +var ProtocolBuffer = internal.Register(MustNewLazyLexer( &Config{ Name: "Protocol Buffer", Aliases: []string{"protobuf", "proto"}, Filenames: []string{"*.proto"}, MimeTypes: []string{}, }, - Rules{ + protocolBufferRules, +)) + +func protocolBufferRules() Rules { + return Rules{ "root": { {`[ \t]+`, Text, nil}, {`[,;{}\[\]()<>]`, Punctuation, nil}, @@ -49,5 +53,5 @@ var ProtocolBuffer = internal.Register(MustNewLexer( {`[a-zA-Z_]\w*`, Name, Pop(1)}, Default(Pop(1)), }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/p/puppet.go b/vendor/github.com/alecthomas/chroma/lexers/p/puppet.go index a1db0d27bd248..b32caed09be9f 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/p/puppet.go +++ b/vendor/github.com/alecthomas/chroma/lexers/p/puppet.go @@ -6,14 +6,18 @@ import ( ) // Puppet lexer. -var Puppet = internal.Register(MustNewLexer( +var Puppet = internal.Register(MustNewLazyLexer( &Config{ Name: "Puppet", Aliases: []string{"puppet"}, Filenames: []string{"*.pp"}, MimeTypes: []string{}, }, - Rules{ + puppetRules, +)) + +func puppetRules() Rules { + return Rules{ "root": { Include("comments"), Include("keywords"), @@ -52,5 +56,5 @@ var Puppet = internal.Register(MustNewLexer( {`"([^"])*"`, LiteralString, nil}, {`'(\\'|[^'])*'`, LiteralString, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/p/python.go b/vendor/github.com/alecthomas/chroma/lexers/p/python.go index 77f2c8db6db1b..032e487d06d4a 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/p/python.go +++ b/vendor/github.com/alecthomas/chroma/lexers/p/python.go @@ -6,14 +6,18 @@ import ( ) // Python lexer. -var Python = internal.Register(MustNewLexer( +var Python = internal.Register(MustNewLazyLexer( &Config{ Name: "Python", Aliases: []string{"python", "py", "sage"}, Filenames: []string{"*.py", "*.pyw", "*.sc", "SConstruct", "SConscript", "*.tac", "*.sage"}, MimeTypes: []string{"text/x-python", "application/x-python"}, }, - Rules{ + pythonRules, +)) + +func pythonRules() Rules { + return Rules{ "root": { {`\n`, Text, nil}, {`^(\s*)([rRuUbB]{,2})("""(?:.|\n)*?""")`, ByGroups(Text, LiteralStringAffix, LiteralStringDoc), nil}, @@ -65,9 +69,9 @@ var Python = internal.Register(MustNewLexer( {`\d+[eE][+-]?[0-9]+j?`, LiteralNumberFloat, nil}, {`0[0-7]+j?`, LiteralNumberOct, nil}, {`0[bB][01]+`, LiteralNumberBin, nil}, - {`0[xX][a-fA-F0-9]+`, LiteralNumberHex, nil}, + {`0[xX][a-fA-F0-9_]+`, LiteralNumberHex, nil}, {`\d+L`, LiteralNumberIntegerLong, nil}, - {`\d+j?`, LiteralNumberInteger, nil}, + {`[\d_]+j?`, LiteralNumberInteger, nil}, }, "backtick": { {"`.*?`", LiteralStringBacktick, nil}, @@ -133,5 +137,5 @@ var Python = internal.Register(MustNewLexer( Include("strings-single"), {`\n`, LiteralStringSingle, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/p/python3.go b/vendor/github.com/alecthomas/chroma/lexers/p/python3.go index 980799824d4e4..a2402b8ff497e 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/p/python3.go +++ b/vendor/github.com/alecthomas/chroma/lexers/p/python3.go @@ -5,17 +5,21 @@ import ( "github.com/alecthomas/chroma/lexers/internal" ) -var python3Identifier = `[A-Z_a-zªµºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬˮͰ-ʹͶ-ͷͻ-ͽΆΈ-ΊΌΎ-ΡΣ-ϵϷ-ҁҊ-ԧԱ-Ֆՙա-ևא-תװ-ײؠ-يٮ-ٯٱ-ۓەۥ-ۦۮ-ۯۺ-ۼۿܐܒ-ܯݍ-ޥޱߊ-ߪߴ-ߵߺࠀ-ࠕࠚࠤࠨࡀ-ࡘࢠࢢ-ࢬऄ-हऽॐक़-ॡॱ-ॷॹ-ॿঅ-ঌএ-ঐও-নপ-রলশ-হঽৎড়-ঢ়য়-ৡৰ-ৱਅ-ਊਏ-ਐਓ-ਨਪ-ਰਲ-ਲ਼ਵ-ਸ਼ਸ-ਹਖ਼-ੜਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલ-ળવ-હઽૐૠ-ૡଅ-ଌଏ-ଐଓ-ନପ-ରଲ-ଳଵ-ହଽଡ଼-ଢ଼ୟ-ୡୱஃஅ-ஊஎ-ஐஒ-கங-சஜஞ-டண-தந-பம-ஹௐఅ-ఌఎ-ఐఒ-నప-ళవ-హఽౘ-ౙౠ-ౡಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽೞೠ-ೡೱ-ೲഅ-ഌഎ-ഐഒ-ഺഽൎൠ-ൡൺ-ൿඅ-ඖක-නඳ-රලව-ෆก-ะาเ-ๆກ-ຂຄງ-ຈຊຍດ-ທນ-ຟມ-ຣລວສ-ຫອ-ະາຽເ-ໄໆໜ-ໟༀཀ-ཇཉ-ཬྈ-ྌက-ဪဿၐ-ၕၚ-ၝၡၥ-ၦၮ-ၰၵ-ႁႎႠ-ჅჇჍა-ჺჼ-ቈቊ-ቍቐ-ቖቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏼᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᛮ-ᛰᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰក-ឳៗៜᠠ-ᡷᢀ-ᢨᢪᢰ-ᣵᤀ-ᤜᥐ-ᥭᥰ-ᥴᦀ-ᦫᧁ-ᧇᨀ-ᨖᨠ-ᩔᪧᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮ-ᮯᮺ-ᯥᰀ-ᰣᱍ-ᱏᱚ-ᱽᳩ-ᳬᳮ-ᳱᳵ-ᳶᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙὛὝὟ-ώᾀ-ᾴᾶ-ᾼιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱⁿₐ-ₜℂℇℊ-ℓℕ℘-ℝℤΩℨK-ℹℼ-ℿⅅ-ⅉⅎⅠ-ↈⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⳲ-ⳳⴀ-ⴥⴧⴭⴰ-ⵧⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞ々-〇〡-〩〱-〵〸-〼ぁ-ゖゝ-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿌ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪ-ꘫꙀ-ꙮꙿ-ꚗꚠ-ꛯꜗ-ꜟꜢ-ꞈꞋ-ꞎꞐ-ꞓꞠ-Ɦꟸ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꣲ-ꣷꣻꤊ-ꤥꤰ-ꥆꥠ-ꥼꦄ-ꦲꧏꨀ-ꨨꩀ-ꩂꩄ-ꩋꩠ-ꩶꩺꪀ-ꪯꪱꪵ-ꪶꪹ-ꪽꫀꫂꫛ-ꫝꫠ-ꫪꫲ-ꫴꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꯀ-ꯢ가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ff-stﬓ-ﬗיִײַ-ﬨשׁ-זּטּ-לּמּנּ-סּףּ-פּצּ-ﮱﯓ-ﱝﱤ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷹﹱﹳﹷﹹﹻﹽﹿ-ﻼA-Za-zヲ-ンᅠ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ𐀀-𐀋𐀍-𐀦𐀨-𐀺𐀼-𐀽𐀿-𐁍𐁐-𐁝𐂀-𐃺𐅀-𐅴𐊀-𐊜𐊠-𐋐𐌀-𐌞𐌰-𐍊𐎀-𐎝𐎠-𐏃𐏈-𐏏𐏑-𐏕𐐀-𐒝𐠀-𐠅𐠈𐠊-𐠵𐠷-𐠸𐠼𐠿-𐡕𐤀-𐤕𐤠-𐤹𐦀-𐦷𐦾-𐦿𐨀𐨐-𐨓𐨕-𐨗𐨙-𐨳𐩠-𐩼𐬀-𐬵𐭀-𐭕𐭠-𐭲𐰀-𐱈𑀃-𑀷𑂃-𑂯𑃐-𑃨𑄃-𑄦𑆃-𑆲𑇁-𑇄𑚀-𑚪𒀀-𒍮𒐀-𒑢𓀀-𓐮𖠀-𖨸𖼀-𖽄𖽐𖾓-𖾟𛀀-𛀁𝐀-𝑔𝑖-𝒜𝒞-𝒟𝒢𝒥-𝒦𝒩-𝒬𝒮-𝒹𝒻𝒽-𝓃𝓅-𝔅𝔇-𝔊𝔍-𝔔𝔖-𝔜𝔞-𝔹𝔻-𝔾𝕀-𝕄𝕆𝕊-𝕐𝕒-𝚥𝚨-𝛀𝛂-𝛚𝛜-𝛺𝛼-𝜔𝜖-𝜴𝜶-𝝎𝝐-𝝮𝝰-𝞈𝞊-𝞨𝞪-𝟂𝟄-𝟋𞸀-𞸃𞸅-𞸟𞸡-𞸢𞸤𞸧𞸩-𞸲𞸴-𞸷𞸹𞸻𞹂𞹇𞹉𞹋𞹍-𞹏𞹑-𞹒𞹔𞹗𞹙𞹛𞹝𞹟𞹡-𞹢𞹤𞹧-𞹪𞹬-𞹲𞹴-𞹷𞹹-𞹼𞹾𞺀-𞺉𞺋-𞺛𞺡-𞺣𞺥-𞺩𞺫-𞺻𠀀-𪛖𪜀-𫜴𫝀-𫠝丽-𪘀][0-9A-Z_a-zªµ·ºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬˮ̀-ʹͶ-ͷͻ-ͽΆ-ΊΌΎ-ΡΣ-ϵϷ-ҁ҃-҇Ҋ-ԧԱ-Ֆՙա-և֑-ֽֿׁ-ׂׄ-ׇׅא-תװ-ײؐ-ؚؠ-٩ٮ-ۓە-ۜ۟-۪ۨ-ۼۿܐ-݊ݍ-ޱ߀-ߵߺࠀ-࠭ࡀ-࡛ࢠࢢ-ࢬࣤ-ࣾऀ-ॣ०-९ॱ-ॷॹ-ॿঁ-ঃঅ-ঌএ-ঐও-নপ-রলশ-হ়-ৄে-ৈো-ৎৗড়-ঢ়য়-ৣ০-ৱਁ-ਃਅ-ਊਏ-ਐਓ-ਨਪ-ਰਲ-ਲ਼ਵ-ਸ਼ਸ-ਹ਼ਾ-ੂੇ-ੈੋ-੍ੑਖ਼-ੜਫ਼੦-ੵઁ-ઃઅ-ઍએ-ઑઓ-નપ-રલ-ળવ-હ઼-ૅે-ૉો-્ૐૠ-ૣ૦-૯ଁ-ଃଅ-ଌଏ-ଐଓ-ନପ-ରଲ-ଳଵ-ହ଼-ୄେ-ୈୋ-୍ୖ-ୗଡ଼-ଢ଼ୟ-ୣ୦-୯ୱஂ-ஃஅ-ஊஎ-ஐஒ-கங-சஜஞ-டண-தந-பம-ஹா-ூெ-ைொ-்ௐௗ௦-௯ఁ-ఃఅ-ఌఎ-ఐఒ-నప-ళవ-హఽ-ౄె-ైొ-్ౕ-ౖౘ-ౙౠ-ౣ౦-౯ಂ-ಃಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹ಼-ೄೆ-ೈೊ-್ೕ-ೖೞೠ-ೣ೦-೯ೱ-ೲം-ഃഅ-ഌഎ-ഐഒ-ഺഽ-ൄെ-ൈൊ-ൎൗൠ-ൣ൦-൯ൺ-ൿං-ඃඅ-ඖක-නඳ-රලව-ෆ්ා-ුූෘ-ෟෲ-ෳก-ฺเ-๎๐-๙ກ-ຂຄງ-ຈຊຍດ-ທນ-ຟມ-ຣລວສ-ຫອ-ູົ-ຽເ-ໄໆ່-ໍ໐-໙ໜ-ໟༀ༘-༙༠-༩༹༵༷༾-ཇཉ-ཬཱ-྄྆-ྗྙ-ྼ࿆က-၉ၐ-ႝႠ-ჅჇჍა-ჺჼ-ቈቊ-ቍቐ-ቖቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚ፝-፟፩-፱ᎀ-ᎏᎠ-Ᏼᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᛮ-ᛰᜀ-ᜌᜎ-᜔ᜠ-᜴ᝀ-ᝓᝠ-ᝬᝮ-ᝰᝲ-ᝳក-៓ៗៜ-៝០-៩᠋-᠍᠐-᠙ᠠ-ᡷᢀ-ᢪᢰ-ᣵᤀ-ᤜᤠ-ᤫᤰ-᤻᥆-ᥭᥰ-ᥴᦀ-ᦫᦰ-ᧉ᧐-᧚ᨀ-ᨛᨠ-ᩞ᩠-᩿᩼-᪉᪐-᪙ᪧᬀ-ᭋ᭐-᭙᭫-᭳ᮀ-᯳ᰀ-᰷᱀-᱉ᱍ-ᱽ᳐-᳔᳒-ᳶᴀ-ᷦ᷼-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙὛὝὟ-ώᾀ-ᾴᾶ-ᾼιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼ‿-⁀⁔ⁱⁿₐ-ₜ⃐-⃥⃜⃡-⃰ℂℇℊ-ℓℕ℘-ℝℤΩℨK-ℹℼ-ℿⅅ-ⅉⅎⅠ-ↈⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳳⴀ-ⴥⴧⴭⴰ-ⵧⵯ⵿-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞⷠ-ⷿ々-〇〡-〯〱-〵〸-〼ぁ-ゖ゙-゚ゝ-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿌ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘫꙀ-꙯ꙴ-꙽ꙿ-ꚗꚟ-꛱ꜗ-ꜟꜢ-ꞈꞋ-ꞎꞐ-ꞓꞠ-Ɦꟸ-ꠧꡀ-ꡳꢀ-꣄꣐-꣙꣠-ꣷꣻ꤀-꤭ꤰ-꥓ꥠ-ꥼꦀ-꧀ꧏ-꧙ꨀ-ꨶꩀ-ꩍ꩐-꩙ꩠ-ꩶꩺ-ꩻꪀ-ꫂꫛ-ꫝꫠ-ꫯꫲ-꫶ꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꯀ-ꯪ꯬-꯭꯰-꯹가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ff-stﬓ-ﬗיִ-ﬨשׁ-זּטּ-לּמּנּ-סּףּ-פּצּ-ﮱﯓ-ﱝﱤ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷹ︀-️︠-︦︳-︴﹍-﹏ﹱﹳﹷﹹﹻﹽﹿ-ﻼ0-9A-Z_a-zヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ𐀀-𐀋𐀍-𐀦𐀨-𐀺𐀼-𐀽𐀿-𐁍𐁐-𐁝𐂀-𐃺𐅀-𐅴𐇽𐊀-𐊜𐊠-𐋐𐌀-𐌞𐌰-𐍊𐎀-𐎝𐎠-𐏃𐏈-𐏏𐏑-𐏕𐐀-𐒝𐒠-𐒩𐠀-𐠅𐠈𐠊-𐠵𐠷-𐠸𐠼𐠿-𐡕𐤀-𐤕𐤠-𐤹𐦀-𐦷𐦾-𐦿𐨀-𐨃𐨅-𐨆𐨌-𐨓𐨕-𐨗𐨙-𐨳𐨸-𐨿𐨺𐩠-𐩼𐬀-𐬵𐭀-𐭕𐭠-𐭲𐰀-𐱈𑀀-𑁆𑁦-𑁯𑂀-𑂺𑃐-𑃨𑃰-𑃹𑄀-𑄴𑄶-𑄿𑆀-𑇄𑇐-𑇙𑚀-𑚷𑛀-𑛉𒀀-𒍮𒐀-𒑢𓀀-𓐮𖠀-𖨸𖼀-𖽄𖽐-𖽾𖾏-𖾟𛀀-𛀁𝅥-𝅩𝅭-𝅲𝅻-𝆂𝆅-𝆋𝆪-𝆭𝉂-𝉄𝐀-𝑔𝑖-𝒜𝒞-𝒟𝒢𝒥-𝒦𝒩-𝒬𝒮-𝒹𝒻𝒽-𝓃𝓅-𝔅𝔇-𝔊𝔍-𝔔𝔖-𝔜𝔞-𝔹𝔻-𝔾𝕀-𝕄𝕆𝕊-𝕐𝕒-𝚥𝚨-𝛀𝛂-𝛚𝛜-𝛺𝛼-𝜔𝜖-𝜴𝜶-𝝎𝝐-𝝮𝝰-𝞈𝞊-𝞨𝞪-𝟂𝟄-𝟋𝟎-𝟿𞸀-𞸃𞸅-𞸟𞸡-𞸢𞸤𞸧𞸩-𞸲𞸴-𞸷𞸹𞸻𞹂𞹇𞹉𞹋𞹍-𞹏𞹑-𞹒𞹔𞹗𞹙𞹛𞹝𞹟𞹡-𞹢𞹤𞹧-𞹪𞹬-𞹲𞹴-𞹷𞹹-𞹼𞹾𞺀-𞺉𞺋-𞺛𞺡-𞺣𞺥-𞺩𞺫-𞺻𠀀-𪛖𪜀-𫜴𫝀-𫠝丽-𪘀󠄀-󠇯]*` - // Python3 lexer. -var Python3 = internal.Register(MustNewLexer( +var Python3 = internal.Register(MustNewLazyLexer( &Config{ Name: "Python 3", Aliases: []string{"python3", "py3"}, Filenames: []string{}, MimeTypes: []string{"text/x-python3", "application/x-python3"}, }, - Rules{ + python3Rules, +)) + +func python3Rules() Rules { + const python3Identifier = `[A-Z_a-zªµºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬˮͰ-ʹͶ-ͷͻ-ͽΆΈ-ΊΌΎ-ΡΣ-ϵϷ-ҁҊ-ԧԱ-Ֆՙա-ևא-תװ-ײؠ-يٮ-ٯٱ-ۓەۥ-ۦۮ-ۯۺ-ۼۿܐܒ-ܯݍ-ޥޱߊ-ߪߴ-ߵߺࠀ-ࠕࠚࠤࠨࡀ-ࡘࢠࢢ-ࢬऄ-हऽॐक़-ॡॱ-ॷॹ-ॿঅ-ঌএ-ঐও-নপ-রলশ-হঽৎড়-ঢ়য়-ৡৰ-ৱਅ-ਊਏ-ਐਓ-ਨਪ-ਰਲ-ਲ਼ਵ-ਸ਼ਸ-ਹਖ਼-ੜਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલ-ળવ-હઽૐૠ-ૡଅ-ଌଏ-ଐଓ-ନପ-ରଲ-ଳଵ-ହଽଡ଼-ଢ଼ୟ-ୡୱஃஅ-ஊஎ-ஐஒ-கங-சஜஞ-டண-தந-பம-ஹௐఅ-ఌఎ-ఐఒ-నప-ళవ-హఽౘ-ౙౠ-ౡಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽೞೠ-ೡೱ-ೲഅ-ഌഎ-ഐഒ-ഺഽൎൠ-ൡൺ-ൿඅ-ඖක-නඳ-රලව-ෆก-ะาเ-ๆກ-ຂຄງ-ຈຊຍດ-ທນ-ຟມ-ຣລວສ-ຫອ-ະາຽເ-ໄໆໜ-ໟༀཀ-ཇཉ-ཬྈ-ྌက-ဪဿၐ-ၕၚ-ၝၡၥ-ၦၮ-ၰၵ-ႁႎႠ-ჅჇჍა-ჺჼ-ቈቊ-ቍቐ-ቖቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏼᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᛮ-ᛰᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰក-ឳៗៜᠠ-ᡷᢀ-ᢨᢪᢰ-ᣵᤀ-ᤜᥐ-ᥭᥰ-ᥴᦀ-ᦫᧁ-ᧇᨀ-ᨖᨠ-ᩔᪧᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮ-ᮯᮺ-ᯥᰀ-ᰣᱍ-ᱏᱚ-ᱽᳩ-ᳬᳮ-ᳱᳵ-ᳶᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙὛὝὟ-ώᾀ-ᾴᾶ-ᾼιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱⁿₐ-ₜℂℇℊ-ℓℕ℘-ℝℤΩℨK-ℹℼ-ℿⅅ-ⅉⅎⅠ-ↈⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⳲ-ⳳⴀ-ⴥⴧⴭⴰ-ⵧⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞ々-〇〡-〩〱-〵〸-〼ぁ-ゖゝ-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿌ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪ-ꘫꙀ-ꙮꙿ-ꚗꚠ-ꛯꜗ-ꜟꜢ-ꞈꞋ-ꞎꞐ-ꞓꞠ-Ɦꟸ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꣲ-ꣷꣻꤊ-ꤥꤰ-ꥆꥠ-ꥼꦄ-ꦲꧏꨀ-ꨨꩀ-ꩂꩄ-ꩋꩠ-ꩶꩺꪀ-ꪯꪱꪵ-ꪶꪹ-ꪽꫀꫂꫛ-ꫝꫠ-ꫪꫲ-ꫴꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꯀ-ꯢ가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ff-stﬓ-ﬗיִײַ-ﬨשׁ-זּטּ-לּמּנּ-סּףּ-פּצּ-ﮱﯓ-ﱝﱤ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷹﹱﹳﹷﹹﹻﹽﹿ-ﻼA-Za-zヲ-ンᅠ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ𐀀-𐀋𐀍-𐀦𐀨-𐀺𐀼-𐀽𐀿-𐁍𐁐-𐁝𐂀-𐃺𐅀-𐅴𐊀-𐊜𐊠-𐋐𐌀-𐌞𐌰-𐍊𐎀-𐎝𐎠-𐏃𐏈-𐏏𐏑-𐏕𐐀-𐒝𐠀-𐠅𐠈𐠊-𐠵𐠷-𐠸𐠼𐠿-𐡕𐤀-𐤕𐤠-𐤹𐦀-𐦷𐦾-𐦿𐨀𐨐-𐨓𐨕-𐨗𐨙-𐨳𐩠-𐩼𐬀-𐬵𐭀-𐭕𐭠-𐭲𐰀-𐱈𑀃-𑀷𑂃-𑂯𑃐-𑃨𑄃-𑄦𑆃-𑆲𑇁-𑇄𑚀-𑚪𒀀-𒍮𒐀-𒑢𓀀-𓐮𖠀-𖨸𖼀-𖽄𖽐𖾓-𖾟𛀀-𛀁𝐀-𝑔𝑖-𝒜𝒞-𝒟𝒢𝒥-𝒦𝒩-𝒬𝒮-𝒹𝒻𝒽-𝓃𝓅-𝔅𝔇-𝔊𝔍-𝔔𝔖-𝔜𝔞-𝔹𝔻-𝔾𝕀-𝕄𝕆𝕊-𝕐𝕒-𝚥𝚨-𝛀𝛂-𝛚𝛜-𝛺𝛼-𝜔𝜖-𝜴𝜶-𝝎𝝐-𝝮𝝰-𝞈𝞊-𝞨𝞪-𝟂𝟄-𝟋𞸀-𞸃𞸅-𞸟𞸡-𞸢𞸤𞸧𞸩-𞸲𞸴-𞸷𞸹𞸻𞹂𞹇𞹉𞹋𞹍-𞹏𞹑-𞹒𞹔𞹗𞹙𞹛𞹝𞹟𞹡-𞹢𞹤𞹧-𞹪𞹬-𞹲𞹴-𞹷𞹹-𞹼𞹾𞺀-𞺉𞺋-𞺛𞺡-𞺣𞺥-𞺩𞺫-𞺻𠀀-𪛖𪜀-𫜴𫝀-𫠝丽-𪘀][0-9A-Z_a-zªµ·ºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬˮ̀-ʹͶ-ͷͻ-ͽΆ-ΊΌΎ-ΡΣ-ϵϷ-ҁ҃-҇Ҋ-ԧԱ-Ֆՙա-և֑-ֽֿׁ-ׂׄ-ׇׅא-תװ-ײؐ-ؚؠ-٩ٮ-ۓە-ۜ۟-۪ۨ-ۼۿܐ-݊ݍ-ޱ߀-ߵߺࠀ-࠭ࡀ-࡛ࢠࢢ-ࢬࣤ-ࣾऀ-ॣ०-९ॱ-ॷॹ-ॿঁ-ঃঅ-ঌএ-ঐও-নপ-রলশ-হ়-ৄে-ৈো-ৎৗড়-ঢ়য়-ৣ০-ৱਁ-ਃਅ-ਊਏ-ਐਓ-ਨਪ-ਰਲ-ਲ਼ਵ-ਸ਼ਸ-ਹ਼ਾ-ੂੇ-ੈੋ-੍ੑਖ਼-ੜਫ਼੦-ੵઁ-ઃઅ-ઍએ-ઑઓ-નપ-રલ-ળવ-હ઼-ૅે-ૉો-્ૐૠ-ૣ૦-૯ଁ-ଃଅ-ଌଏ-ଐଓ-ନପ-ରଲ-ଳଵ-ହ଼-ୄେ-ୈୋ-୍ୖ-ୗଡ଼-ଢ଼ୟ-ୣ୦-୯ୱஂ-ஃஅ-ஊஎ-ஐஒ-கங-சஜஞ-டண-தந-பம-ஹா-ூெ-ைொ-்ௐௗ௦-௯ఁ-ఃఅ-ఌఎ-ఐఒ-నప-ళవ-హఽ-ౄె-ైొ-్ౕ-ౖౘ-ౙౠ-ౣ౦-౯ಂ-ಃಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹ಼-ೄೆ-ೈೊ-್ೕ-ೖೞೠ-ೣ೦-೯ೱ-ೲം-ഃഅ-ഌഎ-ഐഒ-ഺഽ-ൄെ-ൈൊ-ൎൗൠ-ൣ൦-൯ൺ-ൿං-ඃඅ-ඖක-නඳ-රලව-ෆ්ා-ුූෘ-ෟෲ-ෳก-ฺเ-๎๐-๙ກ-ຂຄງ-ຈຊຍດ-ທນ-ຟມ-ຣລວສ-ຫອ-ູົ-ຽເ-ໄໆ່-ໍ໐-໙ໜ-ໟༀ༘-༙༠-༩༹༵༷༾-ཇཉ-ཬཱ-྄྆-ྗྙ-ྼ࿆က-၉ၐ-ႝႠ-ჅჇჍა-ჺჼ-ቈቊ-ቍቐ-ቖቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚ፝-፟፩-፱ᎀ-ᎏᎠ-Ᏼᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᛮ-ᛰᜀ-ᜌᜎ-᜔ᜠ-᜴ᝀ-ᝓᝠ-ᝬᝮ-ᝰᝲ-ᝳក-៓ៗៜ-៝០-៩᠋-᠍᠐-᠙ᠠ-ᡷᢀ-ᢪᢰ-ᣵᤀ-ᤜᤠ-ᤫᤰ-᤻᥆-ᥭᥰ-ᥴᦀ-ᦫᦰ-ᧉ᧐-᧚ᨀ-ᨛᨠ-ᩞ᩠-᩿᩼-᪉᪐-᪙ᪧᬀ-ᭋ᭐-᭙᭫-᭳ᮀ-᯳ᰀ-᰷᱀-᱉ᱍ-ᱽ᳐-᳔᳒-ᳶᴀ-ᷦ᷼-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙὛὝὟ-ώᾀ-ᾴᾶ-ᾼιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼ‿-⁀⁔ⁱⁿₐ-ₜ⃐-⃥⃜⃡-⃰ℂℇℊ-ℓℕ℘-ℝℤΩℨK-ℹℼ-ℿⅅ-ⅉⅎⅠ-ↈⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳳⴀ-ⴥⴧⴭⴰ-ⵧⵯ⵿-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞⷠ-ⷿ々-〇〡-〯〱-〵〸-〼ぁ-ゖ゙-゚ゝ-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿌ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘫꙀ-꙯ꙴ-꙽ꙿ-ꚗꚟ-꛱ꜗ-ꜟꜢ-ꞈꞋ-ꞎꞐ-ꞓꞠ-Ɦꟸ-ꠧꡀ-ꡳꢀ-꣄꣐-꣙꣠-ꣷꣻ꤀-꤭ꤰ-꥓ꥠ-ꥼꦀ-꧀ꧏ-꧙ꨀ-ꨶꩀ-ꩍ꩐-꩙ꩠ-ꩶꩺ-ꩻꪀ-ꫂꫛ-ꫝꫠ-ꫯꫲ-꫶ꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꯀ-ꯪ꯬-꯭꯰-꯹가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ff-stﬓ-ﬗיִ-ﬨשׁ-זּטּ-לּמּנּ-סּףּ-פּצּ-ﮱﯓ-ﱝﱤ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷹ︀-️︠-︦︳-︴﹍-﹏ﹱﹳﹷﹹﹻﹽﹿ-ﻼ0-9A-Z_a-zヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ𐀀-𐀋𐀍-𐀦𐀨-𐀺𐀼-𐀽𐀿-𐁍𐁐-𐁝𐂀-𐃺𐅀-𐅴𐇽𐊀-𐊜𐊠-𐋐𐌀-𐌞𐌰-𐍊𐎀-𐎝𐎠-𐏃𐏈-𐏏𐏑-𐏕𐐀-𐒝𐒠-𐒩𐠀-𐠅𐠈𐠊-𐠵𐠷-𐠸𐠼𐠿-𐡕𐤀-𐤕𐤠-𐤹𐦀-𐦷𐦾-𐦿𐨀-𐨃𐨅-𐨆𐨌-𐨓𐨕-𐨗𐨙-𐨳𐨸-𐨿𐨺𐩠-𐩼𐬀-𐬵𐭀-𐭕𐭠-𐭲𐰀-𐱈𑀀-𑁆𑁦-𑁯𑂀-𑂺𑃐-𑃨𑃰-𑃹𑄀-𑄴𑄶-𑄿𑆀-𑇄𑇐-𑇙𑚀-𑚷𑛀-𑛉𒀀-𒍮𒐀-𒑢𓀀-𓐮𖠀-𖨸𖼀-𖽄𖽐-𖽾𖾏-𖾟𛀀-𛀁𝅥-𝅩𝅭-𝅲𝅻-𝆂𝆅-𝆋𝆪-𝆭𝉂-𝉄𝐀-𝑔𝑖-𝒜𝒞-𝒟𝒢𝒥-𝒦𝒩-𝒬𝒮-𝒹𝒻𝒽-𝓃𝓅-𝔅𝔇-𝔊𝔍-𝔔𝔖-𝔜𝔞-𝔹𝔻-𝔾𝕀-𝕄𝕆𝕊-𝕐𝕒-𝚥𝚨-𝛀𝛂-𝛚𝛜-𝛺𝛼-𝜔𝜖-𝜴𝜶-𝝎𝝐-𝝮𝝰-𝞈𝞊-𝞨𝞪-𝟂𝟄-𝟋𝟎-𝟿𞸀-𞸃𞸅-𞸟𞸡-𞸢𞸤𞸧𞸩-𞸲𞸴-𞸷𞸹𞸻𞹂𞹇𞹉𞹋𞹍-𞹏𞹑-𞹒𞹔𞹗𞹙𞹛𞹝𞹟𞹡-𞹢𞹤𞹧-𞹪𞹬-𞹲𞹴-𞹷𞹹-𞹼𞹾𞺀-𞺉𞺋-𞺛𞺡-𞺣𞺥-𞺩𞺫-𞺻𠀀-𪛖𪜀-𫜴𫝀-𫠝丽-𪘀󠄀-󠇯]*` + + return Rules{ "root": { {`\n`, Text, nil}, {`^(\s*)([rRuUbB]{,2})("""(?:.|\n)*?""")`, ByGroups(Text, LiteralStringAffix, LiteralStringDoc), nil}, @@ -133,5 +137,5 @@ var Python3 = internal.Register(MustNewLexer( Include("strings-single"), {`\n`, LiteralStringSingle, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/q/qbasic.go b/vendor/github.com/alecthomas/chroma/lexers/q/qbasic.go index fd47c08003982..1e36f86570b6b 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/q/qbasic.go +++ b/vendor/github.com/alecthomas/chroma/lexers/q/qbasic.go @@ -6,14 +6,18 @@ import ( ) // Qbasic lexer. -var Qbasic = internal.Register(MustNewLexer( +var Qbasic = internal.Register(MustNewLazyLexer( &Config{ Name: "QBasic", Aliases: []string{"qbasic", "basic"}, Filenames: []string{"*.BAS", "*.bas"}, MimeTypes: []string{"text/basic"}, }, - Rules{ + qbasicRules, +)) + +func qbasicRules() Rules { + return Rules{ "root": { {`\n+`, Text, nil}, {`\s+`, TextWhitespace, nil}, @@ -63,5 +67,5 @@ var Qbasic = internal.Register(MustNewLexer( "keywords": { {`\b(ACCESS|ALIAS|ANY|APPEND|AS|BASE|BINARY|BYVAL|CASE|CDECL|DOUBLE|ELSE|ELSEIF|ENDIF|INTEGER|IS|LIST|LOCAL|LONG|LOOP|MOD|NEXT|OFF|ON|OUTPUT|RANDOM|SIGNAL|SINGLE|STEP|STRING|THEN|TO|UNTIL|USING|WEND)\b`, Keyword, nil}, }, - }, -)) + } +} diff --git a/vendor/github.com/alecthomas/chroma/lexers/qml.go b/vendor/github.com/alecthomas/chroma/lexers/q/qml.go similarity index 95% rename from vendor/github.com/alecthomas/chroma/lexers/qml.go rename to vendor/github.com/alecthomas/chroma/lexers/q/qml.go index 9d2f2fb23add9..dfd3c75374b33 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/qml.go +++ b/vendor/github.com/alecthomas/chroma/lexers/q/qml.go @@ -1,4 +1,4 @@ -package lexers +package q import ( . "github.com/alecthomas/chroma" // nolint @@ -6,7 +6,7 @@ import ( ) // Qml lexer. -var Qml = internal.Register(MustNewLexer( +var Qml = internal.Register(MustNewLazyLexer( &Config{ Name: "QML", Aliases: []string{"qml", "qbs"}, @@ -14,7 +14,11 @@ var Qml = internal.Register(MustNewLexer( MimeTypes: []string{"application/x-qml", "application/x-qt.qbs+qml"}, DotAll: true, }, - Rules{ + qmlRules, +)) + +func qmlRules() Rules { + return Rules{ "commentsandwhitespace": { {`\s+`, Text, nil}, {` As for 0b0000, and also includes support for half-precision floating-point arithmetic. - f |= FPHP - f |= ASIMDHP - } - } - if procFeatures&(0xf<<16) != 0 { - f |= FP - } - - instAttrReg0, instAttrReg1 := getInstAttributes() - - // https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar0_el1 - // - // ID_AA64ISAR0_EL1 - Instruction Set Attribute Register 0 - // x--------------------------------------------------x - // | Name | bits | visible | - // |--------------------------------------------------| - // | TS | [55-52] | y | - // |--------------------------------------------------| - // | FHM | [51-48] | y | - // |--------------------------------------------------| - // | DP | [47-44] | y | - // |--------------------------------------------------| - // | SM4 | [43-40] | y | - // |--------------------------------------------------| - // | SM3 | [39-36] | y | - // |--------------------------------------------------| - // | SHA3 | [35-32] | y | - // |--------------------------------------------------| - // | RDM | [31-28] | y | - // |--------------------------------------------------| - // | ATOMICS | [23-20] | y | - // |--------------------------------------------------| - // | CRC32 | [19-16] | y | - // |--------------------------------------------------| - // | SHA2 | [15-12] | y | - // |--------------------------------------------------| - // | SHA1 | [11-8] | y | - // |--------------------------------------------------| - // | AES | [7-4] | y | - // x--------------------------------------------------x - - // if instAttrReg0&(0xf<<52) != 0 { - // fmt.Println("TS") - // } - // if instAttrReg0&(0xf<<48) != 0 { - // fmt.Println("FHM") - // } - if instAttrReg0&(0xf<<44) != 0 { - f |= ASIMDDP - } - if instAttrReg0&(0xf<<40) != 0 { - f |= SM4 - } - if instAttrReg0&(0xf<<36) != 0 { - f |= SM3 - } - if instAttrReg0&(0xf<<32) != 0 { - f |= SHA3 - } - if instAttrReg0&(0xf<<28) != 0 { - f |= ASIMDRDM - } - if instAttrReg0&(0xf<<20) != 0 { - f |= ATOMICS - } - if instAttrReg0&(0xf<<16) != 0 { - f |= CRC32 - } - if instAttrReg0&(0xf<<12) != 0 { - f |= SHA2 - } - if instAttrReg0&(0xf<<12) == 2<<12 { - // https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar0_el1 - // 0b0010 --> As 0b0001, plus SHA512H, SHA512H2, SHA512SU0, and SHA512SU1 instructions implemented. - f |= SHA512 - } - if instAttrReg0&(0xf<<8) != 0 { - f |= SHA1 - } - if instAttrReg0&(0xf<<4) != 0 { - f |= AES - } - if instAttrReg0&(0xf<<4) == 2<<4 { - // https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar0_el1 - // 0b0010 --> As for 0b0001, plus PMULL/PMULL2 instructions operating on 64-bit data quantities. - f |= PMULL - } - - // https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar1_el1 - // - // ID_AA64ISAR1_EL1 - Instruction set attribute register 1 - // x--------------------------------------------------x - // | Name | bits | visible | - // |--------------------------------------------------| - // | GPI | [31-28] | y | - // |--------------------------------------------------| - // | GPA | [27-24] | y | - // |--------------------------------------------------| - // | LRCPC | [23-20] | y | - // |--------------------------------------------------| - // | FCMA | [19-16] | y | - // |--------------------------------------------------| - // | JSCVT | [15-12] | y | - // |--------------------------------------------------| - // | API | [11-8] | y | - // |--------------------------------------------------| - // | APA | [7-4] | y | - // |--------------------------------------------------| - // | DPB | [3-0] | y | - // x--------------------------------------------------x - - // if instAttrReg1&(0xf<<28) != 0 { - // fmt.Println("GPI") - // } - if instAttrReg1&(0xf<<28) != 24 { - f |= GPA - } - if instAttrReg1&(0xf<<20) != 0 { - f |= LRCPC - } - if instAttrReg1&(0xf<<16) != 0 { - f |= FCMA - } - if instAttrReg1&(0xf<<12) != 0 { - f |= JSCVT - } - // if instAttrReg1&(0xf<<8) != 0 { - // fmt.Println("API") - // } - // if instAttrReg1&(0xf<<4) != 0 { - // fmt.Println("APA") - // } - if instAttrReg1&(0xf<<0) != 0 { - f |= DCPOP - } - c.Arm = f -} diff --git a/vendor/github.com/klauspost/cpuid/detect_intel.go b/vendor/github.com/klauspost/cpuid/detect_intel.go deleted file mode 100644 index 363951b3b25e3..0000000000000 --- a/vendor/github.com/klauspost/cpuid/detect_intel.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file. - -//+build 386,!gccgo,!noasm amd64,!gccgo,!noasm,!appengine - -package cpuid - -func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32) -func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32) -func asmXgetbv(index uint32) (eax, edx uint32) -func asmRdtscpAsm() (eax, ebx, ecx, edx uint32) - -func initCPU() { - cpuid = asmCpuid - cpuidex = asmCpuidex - xgetbv = asmXgetbv - rdtscpAsm = asmRdtscpAsm -} - -func addInfo(c *CPUInfo) { - c.maxFunc = maxFunctionID() - c.maxExFunc = maxExtendedFunction() - c.BrandName = brandName() - c.CacheLine = cacheLine() - c.Family, c.Model = familyModel() - c.Features = support() - c.SGX = hasSGX(c.Features&SGX != 0, c.Features&SGXLC != 0) - c.ThreadsPerCore = threadsPerCore() - c.LogicalCores = logicalCores() - c.PhysicalCores = physicalCores() - c.VendorID, c.VendorString = vendorID() - c.Hz = hertz(c.BrandName) - c.cacheSize() -} diff --git a/vendor/github.com/klauspost/cpuid/detect_ref.go b/vendor/github.com/klauspost/cpuid/detect_ref.go deleted file mode 100644 index 970ff3d22b57a..0000000000000 --- a/vendor/github.com/klauspost/cpuid/detect_ref.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file. - -//+build !amd64,!386,!arm64 gccgo noasm appengine - -package cpuid - -func initCPU() { - cpuid = func(uint32) (a, b, c, d uint32) { return 0, 0, 0, 0 } - cpuidex = func(x, y uint32) (a, b, c, d uint32) { return 0, 0, 0, 0 } - xgetbv = func(uint32) (a, b uint32) { return 0, 0 } - rdtscpAsm = func() (a, b, c, d uint32) { return 0, 0, 0, 0 } -} - -func addInfo(info *CPUInfo) {} diff --git a/vendor/github.com/klauspost/cpuid/go.mod b/vendor/github.com/klauspost/cpuid/go.mod deleted file mode 100644 index 55563f2a85b52..0000000000000 --- a/vendor/github.com/klauspost/cpuid/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/klauspost/cpuid - -go 1.12 diff --git a/vendor/github.com/klauspost/cpuid/v2/.goreleaser.yml b/vendor/github.com/klauspost/cpuid/v2/.goreleaser.yml new file mode 100644 index 0000000000000..944cc00075046 --- /dev/null +++ b/vendor/github.com/klauspost/cpuid/v2/.goreleaser.yml @@ -0,0 +1,74 @@ +# This is an example goreleaser.yaml file with some sane defaults. +# Make sure to check the documentation at http://goreleaser.com + +builds: + - + id: "cpuid" + binary: cpuid + main: ./cmd/cpuid/main.go + env: + - CGO_ENABLED=0 + flags: + - -ldflags=-s -w + goos: + - aix + - linux + - freebsd + - netbsd + - windows + - darwin + goarch: + - 386 + - amd64 + - arm64 + goarm: + - 7 + +archives: + - + id: cpuid + name_template: "cpuid-{{ .Os }}_{{ .Arch }}_{{ .Version }}" + replacements: + aix: AIX + darwin: OSX + linux: Linux + windows: Windows + 386: i386 + amd64: x86_64 + freebsd: FreeBSD + netbsd: NetBSD + format_overrides: + - goos: windows + format: zip + files: + - LICENSE +checksum: + name_template: 'checksums.txt' +snapshot: + name_template: "{{ .Tag }}-next" +changelog: + sort: asc + filters: + exclude: + - '^doc:' + - '^docs:' + - '^test:' + - '^tests:' + - '^Update\sREADME.md' + +nfpms: + - + file_name_template: "cpuid_package_{{ .Version }}_{{ .Os }}_{{ .Arch }}" + vendor: Klaus Post + homepage: https://github.com/klauspost/cpuid + maintainer: Klaus Post + description: CPUID Tool + license: BSD 3-Clause + formats: + - deb + - rpm + replacements: + darwin: Darwin + linux: Linux + freebsd: FreeBSD + amd64: x86_64 diff --git a/vendor/github.com/klauspost/cpuid/v2/.travis.yml b/vendor/github.com/klauspost/cpuid/v2/.travis.yml index 894bb8f69238f..aa9bad7e584d1 100644 --- a/vendor/github.com/klauspost/cpuid/v2/.travis.yml +++ b/vendor/github.com/klauspost/cpuid/v2/.travis.yml @@ -16,41 +16,52 @@ go: - 1.16.x - master +env: + - CGO_ENABLED=0 + script: - go vet ./... - go test -test.v -test.run ^TestCPUID$ - - go test -race ./... + - CGO_ENABLED=1 go test -race ./... + - go test -tags=nounsafe -test.v -test.run ^TestCPUID$ - go test -tags=noasm ./... + - go run ./cmd/cpuid/main.go + - go run ./cmd/cpuid/main.go -json matrix: allow_failures: - go: 'master' fast_finish: true include: - - stage: gofmt - go: 1.15.x + - stage: other + go: 1.16.x os: linux arch: amd64 script: - diff <(gofmt -d .) <(printf "") - diff <(gofmt -d ./private) <(printf "") - - go install github.com/klauspost/asmfmt/cmd/asmfmt + - curl -sfL https://git.io/goreleaser | VERSION=v0.157.0 sh -s -- check # check goreleaser config for deprecations + - curl -sL https://git.io/goreleaser | VERSION=v0.157.0 sh -s -- --snapshot --skip-publish --rm-dist + - go get github.com/klauspost/asmfmt&&go install github.com/klauspost/asmfmt/cmd/asmfmt - diff <(asmfmt -d .) <(printf "") - - stage: i386 - go: 1.15.x - os: linux - arch: amd64 - script: - GOOS=linux GOARCH=386 go test . - - stage: buildotherprev + - ./test-architectures.sh + - stage: other go: 1.15.x os: linux arch: amd64 script: - ./test-architectures.sh - - stage: buildother + +deploy: + - provider: script + skip_cleanup: true + script: curl -sL https://git.io/goreleaser | VERSION=v0.157.0 bash || true + on: + tags: true + condition: ($TRAVIS_OS_NAME = linux) && ($TRAVIS_CPU_ARCH = amd64) go: 1.16.x - os: linux - arch: amd64 - script: - - ./test-architectures.sh +branches: + only: + - master + - /^v\d+\.\d+(\.\d+)?(-\S*)?$/ diff --git a/vendor/github.com/klauspost/cpuid/v2/cpuid.go b/vendor/github.com/klauspost/cpuid/v2/cpuid.go index e298a9edd7bf9..43e9cc173f3d3 100644 --- a/vendor/github.com/klauspost/cpuid/v2/cpuid.go +++ b/vendor/github.com/klauspost/cpuid/v2/cpuid.go @@ -15,6 +15,7 @@ import ( "fmt" "math" "os" + "runtime" "strings" ) @@ -209,6 +210,7 @@ var cpuid func(op uint32) (eax, ebx, ecx, edx uint32) var cpuidex func(op, op2 uint32) (eax, ebx, ecx, edx uint32) var xgetbv func(index uint32) (eax, edx uint32) var rdtscpAsm func() (eax, ebx, ecx, edx uint32) +var darwinHasAVX512 = func() bool { return false } // CPU contains information about the CPU as detected on startup, // or when Detect last was called. @@ -922,7 +924,11 @@ func support() flagSet { // Verify that XCR0[7:5] = ‘111b’ (OPMASK state, upper 256-bit of ZMM0-ZMM15 and // ZMM16-ZMM31 state are enabled by OS) /// and that XCR0[2:1] = ‘11b’ (XMM state and YMM state are enabled by OS). - if (eax>>5)&7 == 7 && (eax>>1)&3 == 3 { + hasAVX512 := (eax>>5)&7 == 7 && (eax>>1)&3 == 3 + if runtime.GOOS == "darwin" { + hasAVX512 = fs.inSet(AVX) && darwinHasAVX512() + } + if hasAVX512 { fs.setIf(ebx&(1<<16) != 0, AVX512F) fs.setIf(ebx&(1<<17) != 0, AVX512DQ) fs.setIf(ebx&(1<<21) != 0, AVX512IFMA) diff --git a/vendor/github.com/klauspost/cpuid/v2/cpuid_386.s b/vendor/github.com/klauspost/cpuid/v2/cpuid_386.s index 089638f51aacc..8587c3a1fc552 100644 --- a/vendor/github.com/klauspost/cpuid/v2/cpuid_386.s +++ b/vendor/github.com/klauspost/cpuid/v2/cpuid_386.s @@ -40,3 +40,8 @@ TEXT ·asmRdtscpAsm(SB), 7, $0 MOVL CX, ecx+8(FP) MOVL DX, edx+12(FP) RET + +// func asmDarwinHasAVX512() bool +TEXT ·asmDarwinHasAVX512(SB), 7, $0 + MOVL $0, eax+0(FP) + RET diff --git a/vendor/github.com/klauspost/cpuid/v2/cpuid_amd64.s b/vendor/github.com/klauspost/cpuid/v2/cpuid_amd64.s index 3ba0559e93412..bc11f8942193f 100644 --- a/vendor/github.com/klauspost/cpuid/v2/cpuid_amd64.s +++ b/vendor/github.com/klauspost/cpuid/v2/cpuid_amd64.s @@ -40,3 +40,33 @@ TEXT ·asmRdtscpAsm(SB), 7, $0 MOVL CX, ecx+8(FP) MOVL DX, edx+12(FP) RET + +// From https://go-review.googlesource.com/c/sys/+/285572/ +// func asmDarwinHasAVX512() bool +TEXT ·asmDarwinHasAVX512(SB), 7, $0-1 + MOVB $0, ret+0(FP) // default to false + +#ifdef GOOS_darwin // return if not darwin +#ifdef GOARCH_amd64 // return if not amd64 +// These values from: +// https://github.com/apple/darwin-xnu/blob/xnu-4570.1.46/osfmk/i386/cpu_capabilities.h +#define commpage64_base_address 0x00007fffffe00000 +#define commpage64_cpu_capabilities64 (commpage64_base_address+0x010) +#define commpage64_version (commpage64_base_address+0x01E) +#define hasAVX512F 0x0000004000000000 + MOVQ $commpage64_version, BX + MOVW (BX), AX + CMPW AX, $13 // versions < 13 do not support AVX512 + JL no_avx512 + MOVQ $commpage64_cpu_capabilities64, BX + MOVQ (BX), AX + MOVQ $hasAVX512F, CX + ANDQ CX, AX + JZ no_avx512 + MOVB $1, ret+0(FP) + +no_avx512: +#endif +#endif + RET + diff --git a/vendor/github.com/klauspost/cpuid/v2/detect_x86.go b/vendor/github.com/klauspost/cpuid/v2/detect_x86.go index 381940ebdf60f..93bc20f4fecf7 100644 --- a/vendor/github.com/klauspost/cpuid/v2/detect_x86.go +++ b/vendor/github.com/klauspost/cpuid/v2/detect_x86.go @@ -8,12 +8,14 @@ func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32) func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32) func asmXgetbv(index uint32) (eax, edx uint32) func asmRdtscpAsm() (eax, ebx, ecx, edx uint32) +func asmDarwinHasAVX512() bool func initCPU() { cpuid = asmCpuid cpuidex = asmCpuidex xgetbv = asmXgetbv rdtscpAsm = asmRdtscpAsm + darwinHasAVX512 = asmDarwinHasAVX512 } func addInfo(c *CPUInfo, safe bool) { diff --git a/vendor/github.com/klauspost/cpuid/v2/os_darwin_arm64.go b/vendor/github.com/klauspost/cpuid/v2/os_darwin_arm64.go index 82d272fab30eb..8d2cb0368bcf5 100644 --- a/vendor/github.com/klauspost/cpuid/v2/os_darwin_arm64.go +++ b/vendor/github.com/klauspost/cpuid/v2/os_darwin_arm64.go @@ -11,5 +11,9 @@ func detectOS(c *CPUInfo) bool { // to all Go programs running on darwin/arm64. // TODO: Add more if we know them. c.featureSet.setIf(runtime.GOOS != "ios", AESARM, PMULL, SHA1, SHA2) + c.PhysicalCores = runtime.NumCPU() + // For now assuming 1 thread per core... + c.ThreadsPerCore = 1 + c.LogicalCores = c.PhysicalCores return true } diff --git a/vendor/github.com/klauspost/cpuid/v2/os_linux_arm64.go b/vendor/github.com/klauspost/cpuid/v2/os_linux_arm64.go index a01afad81c7f6..ee278b9e4bcfb 100644 --- a/vendor/github.com/klauspost/cpuid/v2/os_linux_arm64.go +++ b/vendor/github.com/klauspost/cpuid/v2/os_linux_arm64.go @@ -11,7 +11,6 @@ import ( "encoding/binary" "io/ioutil" "runtime" - "unsafe" ) // HWCAP bits. @@ -42,12 +41,9 @@ const ( hwcap_ASIMDFHM = 1 << 23 ) -//go:linkname hwcap internal/cpu.HWCap -var hwcap uint - func detectOS(c *CPUInfo) bool { // For now assuming no hyperthreading is reasonable. - c.LogicalCores = int(getproccount()) + c.LogicalCores = runtime.NumCPU() c.PhysicalCores = c.LogicalCores c.ThreadsPerCore = 1 if hwcap == 0 { @@ -132,30 +128,3 @@ func detectOS(c *CPUInfo) bool { func isSet(hwc uint, value uint) bool { return hwc&value != 0 } - -//go:noescape -//go:linkname sched_getaffinity runtime.sched_getaffinity -func sched_getaffinity(pid, len uintptr, buf *byte) int32 - -func getproccount() int32 { - // This buffer is huge (8 kB) but we are on the system stack - // and there should be plenty of space (64 kB). - // Also this is a leaf, so we're not holding up the memory for long. - const maxCPUs = 64 * 1024 - var buf [maxCPUs / 8]byte - r := sched_getaffinity(0, unsafe.Sizeof(buf), &buf[0]) - if r < 0 { - return 0 - } - n := int32(0) - for _, v := range buf[:r] { - for v != 0 { - n += int32(v & 1) - v >>= 1 - } - } - if n == 0 { - n = 1 - } - return n -} diff --git a/vendor/github.com/klauspost/cpuid/v2/os_other_arm64.go b/vendor/github.com/klauspost/cpuid/v2/os_other_arm64.go index df0ad06b383ee..1a951e6ca00e9 100644 --- a/vendor/github.com/klauspost/cpuid/v2/os_other_arm64.go +++ b/vendor/github.com/klauspost/cpuid/v2/os_other_arm64.go @@ -6,6 +6,12 @@ package cpuid +import "runtime" + func detectOS(c *CPUInfo) bool { + c.PhysicalCores = runtime.NumCPU() + // For now assuming 1 thread per core... + c.ThreadsPerCore = 1 + c.LogicalCores = c.PhysicalCores return false } diff --git a/vendor/github.com/klauspost/cpuid/v2/os_safe_linux_arm64.go b/vendor/github.com/klauspost/cpuid/v2/os_safe_linux_arm64.go new file mode 100644 index 0000000000000..4d0b8b465b3a6 --- /dev/null +++ b/vendor/github.com/klauspost/cpuid/v2/os_safe_linux_arm64.go @@ -0,0 +1,7 @@ +// Copyright (c) 2021 Klaus Post, released under MIT License. See LICENSE file. + +//+build nounsafe + +package cpuid + +var hwcap uint diff --git a/vendor/github.com/klauspost/cpuid/v2/os_unsafe_linux_arm64.go b/vendor/github.com/klauspost/cpuid/v2/os_unsafe_linux_arm64.go new file mode 100644 index 0000000000000..329800286e6ed --- /dev/null +++ b/vendor/github.com/klauspost/cpuid/v2/os_unsafe_linux_arm64.go @@ -0,0 +1,10 @@ +// Copyright (c) 2021 Klaus Post, released under MIT License. See LICENSE file. + +//+build !nounsafe + +package cpuid + +import _ "unsafe" // needed for go:linkname + +//go:linkname hwcap internal/cpu.HWCap +var hwcap uint diff --git a/vendor/github.com/klauspost/cpuid/v2/test-architectures.sh b/vendor/github.com/klauspost/cpuid/v2/test-architectures.sh index 50150eaabe502..471d986d2488a 100644 --- a/vendor/github.com/klauspost/cpuid/v2/test-architectures.sh +++ b/vendor/github.com/klauspost/cpuid/v2/test-architectures.sh @@ -5,11 +5,11 @@ set -e go tool dist list | while IFS=/ read os arch; do echo "Checking $os/$arch..." echo " normal" - GOARCH=$arch GOOS=$os go build -o /dev/null ./... + GOARCH=$arch GOOS=$os go build -o /dev/null . echo " noasm" - GOARCH=$arch GOOS=$os go build -tags noasm -o /dev/null ./... + GOARCH=$arch GOOS=$os go build -tags noasm -o /dev/null . echo " appengine" - GOARCH=$arch GOOS=$os go build -tags appengine -o /dev/null ./... + GOARCH=$arch GOOS=$os go build -tags appengine -o /dev/null . echo " noasm,appengine" - GOARCH=$arch GOOS=$os go build -tags 'appengine noasm' -o /dev/null ./... + GOARCH=$arch GOOS=$os go build -tags 'appengine noasm' -o /dev/null . done diff --git a/vendor/github.com/lib/pq/.travis.sh b/vendor/github.com/lib/pq/.travis.sh index ebf447030be0b..15607b50d326e 100644 --- a/vendor/github.com/lib/pq/.travis.sh +++ b/vendor/github.com/lib/pq/.travis.sh @@ -1,17 +1,15 @@ #!/bin/bash -set -eu +set -eux client_configure() { sudo chmod 600 $PQSSLCERTTEST_PATH/postgresql.key } pgdg_repository() { - local sourcelist='sources.list.d/postgresql.list' - curl -sS 'https://www.postgresql.org/media/keys/ACCC4CF8.asc' | sudo apt-key add - - echo deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main $PGVERSION | sudo tee "/etc/apt/$sourcelist" - sudo apt-get -o Dir::Etc::sourcelist="$sourcelist" -o Dir::Etc::sourceparts='-' -o APT::Get::List-Cleanup='0' update + echo "deb http://apt.postgresql.org/pub/repos/apt/ `lsb_release -cs`-pgdg main" | sudo tee /etc/apt/sources.list.d/pgdg.list + sudo apt-get update } postgresql_configure() { @@ -51,10 +49,10 @@ postgresql_configure() { } postgresql_install() { - xargs sudo apt-get -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confnew' install <<-packages + xargs sudo apt-get -y install <<-packages postgresql-$PGVERSION + postgresql-client-$PGVERSION postgresql-server-dev-$PGVERSION - postgresql-contrib-$PGVERSION packages } diff --git a/vendor/github.com/lib/pq/.travis.yml b/vendor/github.com/lib/pq/.travis.yml index f378207f2049c..283f35f234163 100644 --- a/vendor/github.com/lib/pq/.travis.yml +++ b/vendor/github.com/lib/pq/.travis.yml @@ -3,7 +3,7 @@ language: go go: - 1.14.x - 1.15.x - - master + - 1.16.x sudo: true diff --git a/vendor/github.com/lib/pq/encode.go b/vendor/github.com/lib/pq/encode.go index c4dafe2705a39..51c143ee4c10b 100644 --- a/vendor/github.com/lib/pq/encode.go +++ b/vendor/github.com/lib/pq/encode.go @@ -200,11 +200,17 @@ func appendEscapedText(buf []byte, text string) []byte { func mustParse(f string, typ oid.Oid, s []byte) time.Time { str := string(s) - // check for a 30-minute-offset timezone - if (typ == oid.T_timestamptz || typ == oid.T_timetz) && - str[len(str)-3] == ':' { - f += ":00" + // Check for a minute and second offset in the timezone. + if typ == oid.T_timestamptz || typ == oid.T_timetz { + for i := 3; i <= 6; i += 3 { + if str[len(str)-i] == ':' { + f += ":00" + continue + } + break + } } + // Special case for 24:00 time. // Unfortunately, golang does not parse 24:00 as a proper time. // In this case, we want to try "round to the next day", to differentiate. diff --git a/vendor/github.com/lib/pq/user_other.go b/vendor/github.com/lib/pq/user_other.go new file mode 100644 index 0000000000000..f1c33134dace2 --- /dev/null +++ b/vendor/github.com/lib/pq/user_other.go @@ -0,0 +1,9 @@ +// Package pq is a pure Go Postgres driver for the database/sql package. + +// +build js android hurd illumos zos + +package pq + +func userCurrent() (string, error) { + return "", ErrCouldNotDetectUsername +} diff --git a/vendor/github.com/libdns/libdns/libdns.go b/vendor/github.com/libdns/libdns/libdns.go index 5544e27667198..9a2bbcbcc2ab1 100644 --- a/vendor/github.com/libdns/libdns/libdns.go +++ b/vendor/github.com/libdns/libdns/libdns.go @@ -99,6 +99,9 @@ type Record struct { Name string // partially-qualified (relative to zone) Value string TTL time.Duration + + // type-dependent record fields + Priority int // used by MX, SRV, and URI records } // RelativeName makes fqdn relative to zone. For example, for a FQDN of diff --git a/vendor/github.com/mattn/go-isatty/isatty_others.go b/vendor/github.com/mattn/go-isatty/isatty_others.go index ff714a37615b9..3eba4cb34a24b 100644 --- a/vendor/github.com/mattn/go-isatty/isatty_others.go +++ b/vendor/github.com/mattn/go-isatty/isatty_others.go @@ -1,4 +1,4 @@ -// +build appengine js nacl +// +build appengine js nacl wasm package isatty diff --git a/vendor/github.com/mattn/go-isatty/isatty_solaris.go b/vendor/github.com/mattn/go-isatty/isatty_solaris.go index bdd5c79a07fcd..3010670783416 100644 --- a/vendor/github.com/mattn/go-isatty/isatty_solaris.go +++ b/vendor/github.com/mattn/go-isatty/isatty_solaris.go @@ -8,10 +8,9 @@ import ( ) // IsTerminal returns true if the given file descriptor is a terminal. -// see: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c +// see: https://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libc/port/gen/isatty.c func IsTerminal(fd uintptr) bool { - var termio unix.Termio - err := unix.IoctlSetTermio(int(fd), unix.TCGETA, &termio) + _, err := unix.IoctlGetTermio(int(fd), unix.TCGETA) return err == nil } diff --git a/vendor/github.com/mattn/go-isatty/isatty_tcgets.go b/vendor/github.com/mattn/go-isatty/isatty_tcgets.go index 31a1ca973c7ae..4e7b850ecfb3a 100644 --- a/vendor/github.com/mattn/go-isatty/isatty_tcgets.go +++ b/vendor/github.com/mattn/go-isatty/isatty_tcgets.go @@ -1,4 +1,4 @@ -// +build linux aix +// +build linux aix zos // +build !appengine package isatty diff --git a/vendor/github.com/mattn/go-isatty/renovate.json b/vendor/github.com/mattn/go-isatty/renovate.json deleted file mode 100644 index 5ae9d96b74b49..0000000000000 --- a/vendor/github.com/mattn/go-isatty/renovate.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": [ - "config:base" - ], - "postUpdateOptions": [ - "gomodTidy" - ] -} diff --git a/vendor/github.com/mattn/go-runewidth/go.mod b/vendor/github.com/mattn/go-runewidth/go.mod index 8a9d524ece526..62dba1bfc88c2 100644 --- a/vendor/github.com/mattn/go-runewidth/go.mod +++ b/vendor/github.com/mattn/go-runewidth/go.mod @@ -2,4 +2,4 @@ module github.com/mattn/go-runewidth go 1.9 -require github.com/rivo/uniseg v0.1.0 +require github.com/rivo/uniseg v0.2.0 diff --git a/vendor/github.com/mattn/go-runewidth/go.sum b/vendor/github.com/mattn/go-runewidth/go.sum index 02135660b6f3e..03f902d56d34c 100644 --- a/vendor/github.com/mattn/go-runewidth/go.sum +++ b/vendor/github.com/mattn/go-runewidth/go.sum @@ -1,2 +1,2 @@ -github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= diff --git a/vendor/github.com/microcosm-cc/bluemonday/go.mod b/vendor/github.com/microcosm-cc/bluemonday/go.mod index 0ff3d77b036f2..edbd585f26bd2 100644 --- a/vendor/github.com/microcosm-cc/bluemonday/go.mod +++ b/vendor/github.com/microcosm-cc/bluemonday/go.mod @@ -5,5 +5,5 @@ go 1.16 require ( github.com/aymerick/douceur v0.2.0 github.com/gorilla/css v1.0.0 // indirect - golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c + golang.org/x/net v0.0.0-20210421230115-4e50805a0758 ) diff --git a/vendor/github.com/microcosm-cc/bluemonday/go.sum b/vendor/github.com/microcosm-cc/bluemonday/go.sum index 7955d9eb02111..e195d4eafd43c 100644 --- a/vendor/github.com/microcosm-cc/bluemonday/go.sum +++ b/vendor/github.com/microcosm-cc/bluemonday/go.sum @@ -2,10 +2,10 @@ github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuP github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= -golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c h1:KHUzaHIpjWVlVVNh65G3hhuj3KB1HnjY6Cq5cTvRQT8= -golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758 h1:aEpZnXcAmXkd6AvLb2OPt+EN1Zu/8Ne3pCqPjja5PXY= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/vendor/github.com/miekg/dns/Makefile.release b/vendor/github.com/miekg/dns/Makefile.release index 8fb748e8aaef1..a0ce9b712d9bc 100644 --- a/vendor/github.com/miekg/dns/Makefile.release +++ b/vendor/github.com/miekg/dns/Makefile.release @@ -1,7 +1,7 @@ # Makefile for releasing. # # The release is controlled from version.go. The version found there is -# used to tag the git repo, we're not building any artifects so there is nothing +# used to tag the git repo, we're not building any artifacts so there is nothing # to upload to github. # # * Up the version in version.go diff --git a/vendor/github.com/miekg/dns/README.md b/vendor/github.com/miekg/dns/README.md index 3594492b7cd13..d5b78ef41b891 100644 --- a/vendor/github.com/miekg/dns/README.md +++ b/vendor/github.com/miekg/dns/README.md @@ -171,6 +171,7 @@ Example programs can be found in the `github.com/miekg/exdns` repository. * 8080 - EdDSA for DNSSEC * 8499 - DNS Terminology * 8659 - DNS Certification Authority Authorization (CAA) Resource Record +* 8914 - Extended DNS Errors * 8976 - Message Digest for DNS Zones (ZONEMD RR) ## Loosely Based Upon diff --git a/vendor/github.com/miekg/dns/client.go b/vendor/github.com/miekg/dns/client.go index 000dc013cf5b3..f907698b5d50b 100644 --- a/vendor/github.com/miekg/dns/client.go +++ b/vendor/github.com/miekg/dns/client.go @@ -379,7 +379,7 @@ func Dial(network, address string) (conn *Conn, err error) { func ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, err error) { client := Client{Net: "udp"} r, _, err = client.ExchangeContext(ctx, m, a) - // ignorint rtt to leave the original ExchangeContext API unchanged, but + // ignoring rtt to leave the original ExchangeContext API unchanged, but // this function will go away return r, err } diff --git a/vendor/github.com/miekg/dns/defaults.go b/vendor/github.com/miekg/dns/defaults.go index d874e3008c21a..d47b0b1f2be23 100644 --- a/vendor/github.com/miekg/dns/defaults.go +++ b/vendor/github.com/miekg/dns/defaults.go @@ -349,10 +349,7 @@ func ReverseAddr(addr string) (arpa string, err error) { // Add it, in reverse, to the buffer for i := len(ip) - 1; i >= 0; i-- { v := ip[i] - buf = append(buf, hexDigit[v&0xF]) - buf = append(buf, '.') - buf = append(buf, hexDigit[v>>4]) - buf = append(buf, '.') + buf = append(buf, hexDigit[v&0xF], '.', hexDigit[v>>4], '.') } // Append "ip6.arpa." and return (buf already has the final .) buf = append(buf, "ip6.arpa."...) diff --git a/vendor/github.com/miekg/dns/dnssec.go b/vendor/github.com/miekg/dns/dnssec.go index 900f6e059d89f..7880d7a8267ab 100644 --- a/vendor/github.com/miekg/dns/dnssec.go +++ b/vendor/github.com/miekg/dns/dnssec.go @@ -4,6 +4,7 @@ import ( "bytes" "crypto" "crypto/ecdsa" + "crypto/ed25519" "crypto/elliptic" "crypto/rand" "crypto/rsa" @@ -17,8 +18,6 @@ import ( "sort" "strings" "time" - - "golang.org/x/crypto/ed25519" ) // DNSSEC encryption algorithm codes. @@ -373,6 +372,8 @@ func sign(k crypto.Signer, hashed []byte, hash crypto.Hash, alg uint8) ([]byte, // Verify validates an RRSet with the signature and key. This is only the // cryptographic test, the signature validity period must be checked separately. // This function copies the rdata of some RRs (to lowercase domain names) for the validation to work. +// It also checks that the Zone Key bit (RFC 4034 2.1.1) is set on the DNSKEY +// and that the Protocol field is set to 3 (RFC 4034 2.1.2). func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error { // First the easy checks if !IsRRset(rrset) { @@ -393,6 +394,12 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error { if k.Protocol != 3 { return ErrKey } + // RFC 4034 2.1.1 If bit 7 has value 0, then the DNSKEY record holds some + // other type of DNS public key and MUST NOT be used to verify RRSIGs that + // cover RRsets. + if k.Flags&ZONE == 0 { + return ErrKey + } // IsRRset checked that we have at least one RR and that the RRs in // the set have consistent type, class, and name. Also check that type and @@ -500,7 +507,7 @@ func (rr *RRSIG) ValidityPeriod(t time.Time) bool { return ti <= utc && utc <= te } -// Return the signatures base64 encodedig sigdata as a byte slice. +// Return the signatures base64 encoding sigdata as a byte slice. func (rr *RRSIG) sigBuf() []byte { sigbuf, err := fromBase64([]byte(rr.Signature)) if err != nil { diff --git a/vendor/github.com/miekg/dns/dnssec_keygen.go b/vendor/github.com/miekg/dns/dnssec_keygen.go index 2ab7b6d73b80c..b8124b5618e96 100644 --- a/vendor/github.com/miekg/dns/dnssec_keygen.go +++ b/vendor/github.com/miekg/dns/dnssec_keygen.go @@ -3,12 +3,11 @@ package dns import ( "crypto" "crypto/ecdsa" + "crypto/ed25519" "crypto/elliptic" "crypto/rand" "crypto/rsa" "math/big" - - "golang.org/x/crypto/ed25519" ) // Generate generates a DNSKEY of the given bit size. diff --git a/vendor/github.com/miekg/dns/dnssec_keyscan.go b/vendor/github.com/miekg/dns/dnssec_keyscan.go index 6cbc28483f139..f79658169fe4c 100644 --- a/vendor/github.com/miekg/dns/dnssec_keyscan.go +++ b/vendor/github.com/miekg/dns/dnssec_keyscan.go @@ -4,13 +4,12 @@ import ( "bufio" "crypto" "crypto/ecdsa" + "crypto/ed25519" "crypto/rsa" "io" "math/big" "strconv" "strings" - - "golang.org/x/crypto/ed25519" ) // NewPrivateKey returns a PrivateKey by parsing the string s. diff --git a/vendor/github.com/miekg/dns/dnssec_privkey.go b/vendor/github.com/miekg/dns/dnssec_privkey.go index 072e445dadfa7..f160772964bcb 100644 --- a/vendor/github.com/miekg/dns/dnssec_privkey.go +++ b/vendor/github.com/miekg/dns/dnssec_privkey.go @@ -3,11 +3,10 @@ package dns import ( "crypto" "crypto/ecdsa" + "crypto/ed25519" "crypto/rsa" "math/big" "strconv" - - "golang.org/x/crypto/ed25519" ) const format = "Private-key-format: v1.3\n" diff --git a/vendor/github.com/miekg/dns/doc.go b/vendor/github.com/miekg/dns/doc.go index f7629ec3fff34..5c83f82e49d47 100644 --- a/vendor/github.com/miekg/dns/doc.go +++ b/vendor/github.com/miekg/dns/doc.go @@ -159,7 +159,7 @@ shows the options you have and what functions to call. TRANSACTION SIGNATURE An TSIG or transaction signature adds a HMAC TSIG record to each message sent. -The supported algorithms include: HmacMD5, HmacSHA1, HmacSHA256 and HmacSHA512. +The supported algorithms include: HmacSHA1, HmacSHA256 and HmacSHA512. Basic use pattern when querying with a TSIG name "axfr." (note that these key names must be fully qualified - as they are domain names) and the base64 secret @@ -174,7 +174,7 @@ changes to the RRset after calling SetTsig() the signature will be incorrect. c.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="} m := new(dns.Msg) m.SetQuestion("miek.nl.", dns.TypeMX) - m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix()) + m.SetTsig("axfr.", dns.HmacSHA256, 300, time.Now().Unix()) ... // When sending the TSIG RR is calculated and filled in before sending @@ -187,7 +187,7 @@ request an AXFR for miek.nl. with TSIG key named "axfr." and secret m := new(dns.Msg) t.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="} m.SetAxfr("miek.nl.") - m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix()) + m.SetTsig("axfr.", dns.HmacSHA256, 300, time.Now().Unix()) c, err := t.In(m, "176.58.119.54:53") for r := range c { ... } @@ -214,7 +214,7 @@ client must be configured with an implementation of the TsigProvider interface: c.TsigProvider = new(Provider) m := new(dns.Msg) m.SetQuestion("miek.nl.", dns.TypeMX) - m.SetTsig(keyname, dns.HmacSHA1, 300, time.Now().Unix()) + m.SetTsig(keyname, dns.HmacSHA256, 300, time.Now().Unix()) ... // TSIG RR is calculated by calling your Generate method @@ -231,7 +231,7 @@ Basic use pattern validating and replying to a message that has TSIG set. if r.IsTsig() != nil { if w.TsigStatus() == nil { // *Msg r has an TSIG record and it was validated - m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix()) + m.SetTsig("axfr.", dns.HmacSHA256, 300, time.Now().Unix()) } else { // *Msg r has an TSIG records and it was not validated } diff --git a/vendor/github.com/miekg/dns/edns.go b/vendor/github.com/miekg/dns/edns.go index f3fb1c6849511..844cf92b8c633 100644 --- a/vendor/github.com/miekg/dns/edns.go +++ b/vendor/github.com/miekg/dns/edns.go @@ -22,6 +22,7 @@ const ( EDNS0COOKIE = 0xa // EDNS0 Cookie EDNS0TCPKEEPALIVE = 0xb // EDNS0 tcp keep alive (See RFC 7828) EDNS0PADDING = 0xc // EDNS0 padding (See RFC 7830) + EDNS0EDE = 0xf // EDNS0 extended DNS errors (See RFC 8914) EDNS0LOCALSTART = 0xFDE9 // Beginning of range reserved for local/experimental use (See RFC 6891) EDNS0LOCALEND = 0xFFFE // End of range reserved for local/experimental use (See RFC 6891) _DO = 1 << 15 // DNSSEC OK @@ -73,6 +74,8 @@ func (rr *OPT) String() string { s += "\n; LOCAL OPT: " + o.String() case *EDNS0_PADDING: s += "\n; PADDING: " + o.String() + case *EDNS0_EDE: + s += "\n; EDE: " + o.String() } } return s @@ -148,6 +151,16 @@ func (rr *OPT) SetDo(do ...bool) { } } +// Z returns the Z part of the OPT RR as a uint16 with only the 15 least significant bits used. +func (rr *OPT) Z() uint16 { + return uint16(rr.Hdr.Ttl & 0x7FFF) +} + +// SetZ sets the Z part of the OPT RR, note only the 15 least significant bits of z are used. +func (rr *OPT) SetZ(z uint16) { + rr.Hdr.Ttl = rr.Hdr.Ttl&^0x7FFF | uint32(z&0x7FFF) +} + // EDNS0 defines an EDNS0 Option. An OPT RR can have multiple options appended to it. type EDNS0 interface { // Option returns the option code for the option. @@ -525,7 +538,7 @@ func (e *EDNS0_N3U) String() string { } func (e *EDNS0_N3U) copy() EDNS0 { return &EDNS0_N3U{e.Code, e.AlgCode} } -// EDNS0_EXPIRE implementes the EDNS0 option as described in RFC 7314. +// EDNS0_EXPIRE implements the EDNS0 option as described in RFC 7314. type EDNS0_EXPIRE struct { Code uint16 // Always EDNS0EXPIRE Expire uint32 @@ -673,3 +686,101 @@ func (e *EDNS0_PADDING) copy() EDNS0 { copy(b, e.Padding) return &EDNS0_PADDING{b} } + +// Extended DNS Error Codes (RFC 8914). +const ( + ExtendedErrorCodeOther uint16 = iota + ExtendedErrorCodeUnsupportedDNSKEYAlgorithm + ExtendedErrorCodeUnsupportedDSDigestType + ExtendedErrorCodeStaleAnswer + ExtendedErrorCodeForgedAnswer + ExtendedErrorCodeDNSSECIndeterminate + ExtendedErrorCodeDNSBogus + ExtendedErrorCodeSignatureExpired + ExtendedErrorCodeSignatureNotYetValid + ExtendedErrorCodeDNSKEYMissing + ExtendedErrorCodeRRSIGsMissing + ExtendedErrorCodeNoZoneKeyBitSet + ExtendedErrorCodeNSECMissing + ExtendedErrorCodeCachedError + ExtendedErrorCodeNotReady + ExtendedErrorCodeBlocked + ExtendedErrorCodeCensored + ExtendedErrorCodeFiltered + ExtendedErrorCodeProhibited + ExtendedErrorCodeStaleNXDOMAINAnswer + ExtendedErrorCodeNotAuthoritative + ExtendedErrorCodeNotSupported + ExtendedErrorCodeNoReachableAuthority + ExtendedErrorCodeNetworkError + ExtendedErrorCodeInvalidData +) + +// ExtendedErrorCodeToString maps extended error info codes to a human readable +// description. +var ExtendedErrorCodeToString = map[uint16]string{ + ExtendedErrorCodeOther: "Other", + ExtendedErrorCodeUnsupportedDNSKEYAlgorithm: "Unsupported DNSKEY Algorithm", + ExtendedErrorCodeUnsupportedDSDigestType: "Unsupported DS Digest Type", + ExtendedErrorCodeStaleAnswer: "Stale Answer", + ExtendedErrorCodeForgedAnswer: "Forged Answer", + ExtendedErrorCodeDNSSECIndeterminate: "DNSSEC Indeterminate", + ExtendedErrorCodeDNSBogus: "DNSSEC Bogus", + ExtendedErrorCodeSignatureExpired: "Signature Expired", + ExtendedErrorCodeSignatureNotYetValid: "Signature Not Yet Valid", + ExtendedErrorCodeDNSKEYMissing: "DNSKEY Missing", + ExtendedErrorCodeRRSIGsMissing: "RRSIGs Missing", + ExtendedErrorCodeNoZoneKeyBitSet: "No Zone Key Bit Set", + ExtendedErrorCodeNSECMissing: "NSEC Missing", + ExtendedErrorCodeCachedError: "Cached Error", + ExtendedErrorCodeNotReady: "Not Ready", + ExtendedErrorCodeBlocked: "Blocked", + ExtendedErrorCodeCensored: "Censored", + ExtendedErrorCodeFiltered: "Filtered", + ExtendedErrorCodeProhibited: "Prohibited", + ExtendedErrorCodeStaleNXDOMAINAnswer: "Stale NXDOMAIN Answer", + ExtendedErrorCodeNotAuthoritative: "Not Authoritative", + ExtendedErrorCodeNotSupported: "Not Supported", + ExtendedErrorCodeNoReachableAuthority: "No Reachable Authority", + ExtendedErrorCodeNetworkError: "Network Error", + ExtendedErrorCodeInvalidData: "Invalid Data", +} + +// StringToExtendedErrorCode is a map from human readable descriptions to +// extended error info codes. +var StringToExtendedErrorCode = reverseInt16(ExtendedErrorCodeToString) + +// EDNS0_EDE option is used to return additional information about the cause of +// DNS errors. +type EDNS0_EDE struct { + InfoCode uint16 + ExtraText string +} + +// Option implements the EDNS0 interface. +func (e *EDNS0_EDE) Option() uint16 { return EDNS0EDE } +func (e *EDNS0_EDE) copy() EDNS0 { return &EDNS0_EDE{e.InfoCode, e.ExtraText} } + +func (e *EDNS0_EDE) String() string { + info := strconv.FormatUint(uint64(e.InfoCode), 10) + if s, ok := ExtendedErrorCodeToString[e.InfoCode]; ok { + info += fmt.Sprintf(" (%s)", s) + } + return fmt.Sprintf("%s: (%s)", info, e.ExtraText) +} + +func (e *EDNS0_EDE) pack() ([]byte, error) { + b := make([]byte, 2+len(e.ExtraText)) + binary.BigEndian.PutUint16(b[0:], e.InfoCode) + copy(b[2:], []byte(e.ExtraText)) + return b, nil +} + +func (e *EDNS0_EDE) unpack(b []byte) error { + if len(b) < 2 { + return ErrBuf + } + e.InfoCode = binary.BigEndian.Uint16(b[0:]) + e.ExtraText = string(b[2:]) + return nil +} diff --git a/vendor/github.com/miekg/dns/go.mod b/vendor/github.com/miekg/dns/go.mod index 6003d0573c6eb..aff65114de26e 100644 --- a/vendor/github.com/miekg/dns/go.mod +++ b/vendor/github.com/miekg/dns/go.mod @@ -1,11 +1,9 @@ module github.com/miekg/dns -go 1.12 +go 1.14 require ( - golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 - golang.org/x/net v0.0.0-20190923162816-aa69164e4478 - golang.org/x/sync v0.0.0-20190423024810-112230192c58 - golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe - golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 // indirect + golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c + golang.org/x/sys v0.0.0-20210303074136-134d130e1a04 ) diff --git a/vendor/github.com/miekg/dns/go.sum b/vendor/github.com/miekg/dns/go.sum index 96bda3a94128a..3359ebea4e883 100644 --- a/vendor/github.com/miekg/dns/go.sum +++ b/vendor/github.com/miekg/dns/go.sum @@ -1,39 +1,10 @@ -golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4 h1:Vk3wNqEZwyGyei9yq5ekj7frek2u7HUfffJ1/opblzc= -golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472 h1:Gv7RPwsi3eZ2Fgewe3CBsuOebPwO27PoXzRpJPsvSSM= -golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392 h1:ACG4HJsFiNMf47Y4PeRoebLNy/2lXT9EtprMuTFWt1M= -golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3 h1:dgd4x4kJt7G4k4m93AYLzM8Ni6h2qLTfh9n9vXJT3/0= -golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -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/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180928133829-e4b3c5e90611 h1:O33LKL7WyJgjN9CvxfTIomjIClbd/Kq86/iipowHQU0= -golang.org/x/sys v0.0.0-20180928133829-e4b3c5e90611/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-20190904154756-749cb33beabd h1:DBH9mDw0zluJT/R+nGuV3jWFWLFaHyYZWD4tOT+cjn0= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/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/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04 h1:cEhElsAv9LUt9ZUUocxzWe05oFLVd+AA2nstydTeI8g= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA= -golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/vendor/github.com/miekg/dns/labels.go b/vendor/github.com/miekg/dns/labels.go index df1675dfd2803..f9faacfeb4101 100644 --- a/vendor/github.com/miekg/dns/labels.go +++ b/vendor/github.com/miekg/dns/labels.go @@ -10,7 +10,7 @@ package dns // escaped dots (\.) for instance. // s must be a syntactically valid domain name, see IsDomainName. func SplitDomainName(s string) (labels []string) { - if len(s) == 0 { + if s == "" { return nil } fqdnEnd := 0 // offset of the final '.' or the length of the name diff --git a/vendor/github.com/miekg/dns/listen_go_not111.go b/vendor/github.com/miekg/dns/listen_no_reuseport.go similarity index 100% rename from vendor/github.com/miekg/dns/listen_go_not111.go rename to vendor/github.com/miekg/dns/listen_no_reuseport.go diff --git a/vendor/github.com/miekg/dns/listen_go111.go b/vendor/github.com/miekg/dns/listen_reuseport.go similarity index 100% rename from vendor/github.com/miekg/dns/listen_go111.go rename to vendor/github.com/miekg/dns/listen_reuseport.go diff --git a/vendor/github.com/miekg/dns/msg.go b/vendor/github.com/miekg/dns/msg.go index 1728a98b7dc2e..ead4b6931df5b 100644 --- a/vendor/github.com/miekg/dns/msg.go +++ b/vendor/github.com/miekg/dns/msg.go @@ -742,7 +742,7 @@ func (dns *Msg) packBufferWithCompressionMap(buf []byte, compression compression } // Set extended rcode unconditionally if we have an opt, this will allow - // reseting the extended rcode bits if they need to. + // resetting the extended rcode bits if they need to. if opt := dns.IsEdns0(); opt != nil { opt.SetExtendedRcode(uint16(dns.Rcode)) } else if dns.Rcode > 0xF { diff --git a/vendor/github.com/miekg/dns/msg_helpers.go b/vendor/github.com/miekg/dns/msg_helpers.go index 47625ed0902d6..472ada5d19b6e 100644 --- a/vendor/github.com/miekg/dns/msg_helpers.go +++ b/vendor/github.com/miekg/dns/msg_helpers.go @@ -460,6 +460,8 @@ func makeDataOpt(code uint16) EDNS0 { return new(EDNS0_N3U) case EDNS0PADDING: return new(EDNS0_PADDING) + case EDNS0EDE: + return new(EDNS0_EDE) default: e := new(EDNS0_LOCAL) e.Code = code diff --git a/vendor/github.com/miekg/dns/privaterr.go b/vendor/github.com/miekg/dns/privaterr.go index cda6cae31e124..45c7f26d85948 100644 --- a/vendor/github.com/miekg/dns/privaterr.go +++ b/vendor/github.com/miekg/dns/privaterr.go @@ -6,7 +6,7 @@ import "strings" // RFC 6895. This allows one to experiment with new RR types, without requesting an // official type code. Also see dns.PrivateHandle and dns.PrivateHandleRemove. type PrivateRdata interface { - // String returns the text presentaton of the Rdata of the Private RR. + // String returns the text presentation of the Rdata of the Private RR. String() string // Parse parses the Rdata of the private RR. Parse([]string) error diff --git a/vendor/github.com/miekg/dns/scan.go b/vendor/github.com/miekg/dns/scan.go index 67161de29cda3..57be988277222 100644 --- a/vendor/github.com/miekg/dns/scan.go +++ b/vendor/github.com/miekg/dns/scan.go @@ -150,6 +150,9 @@ func ReadRR(r io.Reader, file string) (RR, error) { // The text "; this is comment" is returned from Comment. Comments inside // the RR are returned concatenated along with the RR. Comments on a line // by themselves are discarded. +// +// Callers should not assume all returned data in an Resource Record is +// syntactically correct, e.g. illegal base64 in RRSIGs will be returned as-is. type ZoneParser struct { c *zlexer @@ -1233,7 +1236,7 @@ func stringToCm(token string) (e, m uint8, ok bool) { // 'nn.1' must be treated as 'nn-meters and 10cm, not 1cm. cmeters *= 10 } - if len(s[0]) == 0 { + if s[0] == "" { // This will allow omitting the 'meter' part, like .01 (meaning 0.01m = 1cm). break } @@ -1352,7 +1355,7 @@ func stringToNodeID(l lex) (uint64, *ParseError) { if len(l.token) < 19 { return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l} } - // There must be three colons at fixes postitions, if not its a parse error + // There must be three colons at fixes positions, if not its a parse error if l.token[4] != ':' && l.token[9] != ':' && l.token[14] != ':' { return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l} } diff --git a/vendor/github.com/miekg/dns/scan_rr.go b/vendor/github.com/miekg/dns/scan_rr.go index 23b4043bcd5f4..05765aed876e0 100644 --- a/vendor/github.com/miekg/dns/scan_rr.go +++ b/vendor/github.com/miekg/dns/scan_rr.go @@ -609,7 +609,7 @@ func (rr *LOC) parse(c *zlexer, o string) *ParseError { c.Next() // zBlank l, _ = c.Next() - if i, err := strconv.ParseFloat(l.token, 32); err != nil || l.err || i < 0 || i >= 60 { + if i, err := strconv.ParseFloat(l.token, 64); err != nil || l.err || i < 0 || i >= 60 { return &ParseError{"", "bad LOC Latitude seconds", l} } else { rr.Latitude += uint32(1000 * i) @@ -645,7 +645,7 @@ East: } c.Next() // zBlank l, _ = c.Next() - if i, err := strconv.ParseFloat(l.token, 32); err != nil || l.err || i < 0 || i >= 60 { + if i, err := strconv.ParseFloat(l.token, 64); err != nil || l.err || i < 0 || i >= 60 { return &ParseError{"", "bad LOC Longitude seconds", l} } else { rr.Longitude += uint32(1000 * i) @@ -662,7 +662,7 @@ East: Altitude: c.Next() // zBlank l, _ = c.Next() - if len(l.token) == 0 || l.err { + if l.token == "" || l.err { return &ParseError{"", "bad LOC Altitude", l} } if l.token[len(l.token)-1] == 'M' || l.token[len(l.token)-1] == 'm' { @@ -722,7 +722,7 @@ func (rr *HIP) parse(c *zlexer, o string) *ParseError { c.Next() // zBlank l, _ = c.Next() // zString - if len(l.token) == 0 || l.err { + if l.token == "" || l.err { return &ParseError{"", "bad HIP Hit", l} } rr.Hit = l.token // This can not contain spaces, see RFC 5205 Section 6. @@ -730,7 +730,7 @@ func (rr *HIP) parse(c *zlexer, o string) *ParseError { c.Next() // zBlank l, _ = c.Next() // zString - if len(l.token) == 0 || l.err { + if l.token == "" || l.err { return &ParseError{"", "bad HIP PublicKey", l} } rr.PublicKey = l.token // This cannot contain spaces @@ -846,6 +846,38 @@ func (rr *CSYNC) parse(c *zlexer, o string) *ParseError { return nil } +func (rr *ZONEMD) parse(c *zlexer, o string) *ParseError { + l, _ := c.Next() + i, e := strconv.ParseUint(l.token, 10, 32) + if e != nil || l.err { + return &ParseError{"", "bad ZONEMD Serial", l} + } + rr.Serial = uint32(i) + + c.Next() // zBlank + l, _ = c.Next() + i, e1 := strconv.ParseUint(l.token, 10, 8) + if e1 != nil || l.err { + return &ParseError{"", "bad ZONEMD Scheme", l} + } + rr.Scheme = uint8(i) + + c.Next() // zBlank + l, _ = c.Next() + i, err := strconv.ParseUint(l.token, 10, 8) + if err != nil || l.err { + return &ParseError{"", "bad ZONEMD Hash Algorithm", l} + } + rr.Hash = uint8(i) + + s, e2 := endingToString(c, "bad ZONEMD Digest") + if e2 != nil { + return e2 + } + rr.Digest = s + return nil +} + func (rr *SIG) parse(c *zlexer, o string) *ParseError { return rr.RRSIG.parse(c, o) } func (rr *RRSIG) parse(c *zlexer, o string) *ParseError { @@ -997,7 +1029,7 @@ func (rr *NSEC3) parse(c *zlexer, o string) *ParseError { rr.Iterations = uint16(i) c.Next() l, _ = c.Next() - if len(l.token) == 0 || l.err { + if l.token == "" || l.err { return &ParseError{"", "bad NSEC3 Salt", l} } if l.token != "-" { @@ -1007,7 +1039,7 @@ func (rr *NSEC3) parse(c *zlexer, o string) *ParseError { c.Next() l, _ = c.Next() - if len(l.token) == 0 || l.err { + if l.token == "" || l.err { return &ParseError{"", "bad NSEC3 NextDomain", l} } rr.HashLength = 20 // Fix for NSEC3 (sha1 160 bits) diff --git a/vendor/github.com/miekg/dns/sig0.go b/vendor/github.com/miekg/dns/sig0.go index 9ef13ccf3926c..e781c9bb6c287 100644 --- a/vendor/github.com/miekg/dns/sig0.go +++ b/vendor/github.com/miekg/dns/sig0.go @@ -17,7 +17,7 @@ func (rr *SIG) Sign(k crypto.Signer, m *Msg) ([]byte, error) { if k == nil { return nil, ErrPrivKey } - if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 { + if rr.KeyTag == 0 || rr.SignerName == "" || rr.Algorithm == 0 { return nil, ErrKey } @@ -78,7 +78,7 @@ func (rr *SIG) Verify(k *KEY, buf []byte) error { if k == nil { return ErrKey } - if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 { + if rr.KeyTag == 0 || rr.SignerName == "" || rr.Algorithm == 0 { return ErrKey } diff --git a/vendor/github.com/miekg/dns/svcb.go b/vendor/github.com/miekg/dns/svcb.go index 1373fe21b7304..64800daa6dc85 100644 --- a/vendor/github.com/miekg/dns/svcb.go +++ b/vendor/github.com/miekg/dns/svcb.go @@ -321,7 +321,7 @@ func (s *SVCBAlpn) pack() ([]byte, error) { // Liberally estimate the size of an alpn as 10 octets b := make([]byte, 0, 10*len(s.Alpn)) for _, e := range s.Alpn { - if len(e) == 0 { + if e == "" { return nil, errors.New("dns: svcbalpn: empty alpn-id") } if len(e) > 255 { @@ -390,7 +390,7 @@ func (*SVCBNoDefaultAlpn) unpack(b []byte) error { } func (*SVCBNoDefaultAlpn) parse(b string) error { - if len(b) != 0 { + if b != "" { return errors.New("dns: svcbnodefaultalpn: no_default_alpn must have no value") } return nil @@ -511,8 +511,13 @@ func (s *SVCBIPv4Hint) parse(b string) error { } func (s *SVCBIPv4Hint) copy() SVCBKeyValue { + hint := make([]net.IP, len(s.Hint)) + for i, ip := range s.Hint { + hint[i] = copyIP(ip) + } + return &SVCBIPv4Hint{ - append([]net.IP(nil), s.Hint...), + Hint: hint, } } @@ -629,8 +634,13 @@ func (s *SVCBIPv6Hint) parse(b string) error { } func (s *SVCBIPv6Hint) copy() SVCBKeyValue { + hint := make([]net.IP, len(s.Hint)) + for i, ip := range s.Hint { + hint[i] = copyIP(ip) + } + return &SVCBIPv6Hint{ - append([]net.IP(nil), s.Hint...), + Hint: hint, } } diff --git a/vendor/github.com/miekg/dns/types.go b/vendor/github.com/miekg/dns/types.go index 9e379eb351bc1..99dd315bf14bc 100644 --- a/vendor/github.com/miekg/dns/types.go +++ b/vendor/github.com/miekg/dns/types.go @@ -81,6 +81,7 @@ const ( TypeCDNSKEY uint16 = 60 TypeOPENPGPKEY uint16 = 61 TypeCSYNC uint16 = 62 + TypeZONEMD uint16 = 63 TypeSVCB uint16 = 64 TypeHTTPS uint16 = 65 TypeSPF uint16 = 99 @@ -150,6 +151,17 @@ const ( OpcodeUpdate = 5 ) +// Used in ZONEMD https://tools.ietf.org/html/rfc8976 + +const ( + // ZoneMD Accepted Schemes + ZoneMDSchemeSimple = 1 + + // ZoneMD Hash Algorithms + ZoneMDHashAlgSHA384 = 1 + ZoneMDHashAlgSHA512 = 2 +) + // Header is the wire format for the DNS packet header. type Header struct { Id uint16 @@ -1361,6 +1373,23 @@ func (rr *CSYNC) len(off int, compression map[string]struct{}) int { return l } +// ZONEMD RR, from draft-ietf-dnsop-dns-zone-digest +type ZONEMD struct { + Hdr RR_Header + Serial uint32 + Scheme uint8 + Hash uint8 + Digest string `dns:"hex"` +} + +func (rr *ZONEMD) String() string { + return rr.Hdr.String() + + strconv.Itoa(int(rr.Serial)) + + " " + strconv.Itoa(int(rr.Scheme)) + + " " + strconv.Itoa(int(rr.Hash)) + + " " + rr.Digest +} + // APL RR. See RFC 3123. type APL struct { Hdr RR_Header @@ -1472,7 +1501,7 @@ func StringToTime(s string) (uint32, error) { // saltToString converts a NSECX salt to uppercase and returns "-" when it is empty. func saltToString(s string) string { - if len(s) == 0 { + if s == "" { return "-" } return strings.ToUpper(s) diff --git a/vendor/github.com/miekg/dns/version.go b/vendor/github.com/miekg/dns/version.go index 8f7cf76881280..695d1e8b48536 100644 --- a/vendor/github.com/miekg/dns/version.go +++ b/vendor/github.com/miekg/dns/version.go @@ -3,7 +3,7 @@ package dns import "fmt" // Version is current version of this library. -var Version = v{1, 1, 40} +var Version = v{1, 1, 42} // v holds the version of this library. type v struct { diff --git a/vendor/github.com/miekg/dns/zduplicate.go b/vendor/github.com/miekg/dns/zduplicate.go index 0d3b34bd9b24f..9eb1dac299b4e 100644 --- a/vendor/github.com/miekg/dns/zduplicate.go +++ b/vendor/github.com/miekg/dns/zduplicate.go @@ -1317,3 +1317,24 @@ func (r1 *X25) isDuplicate(_r2 RR) bool { } return true } + +func (r1 *ZONEMD) isDuplicate(_r2 RR) bool { + r2, ok := _r2.(*ZONEMD) + if !ok { + return false + } + _ = r2 + if r1.Serial != r2.Serial { + return false + } + if r1.Scheme != r2.Scheme { + return false + } + if r1.Hash != r2.Hash { + return false + } + if r1.Digest != r2.Digest { + return false + } + return true +} diff --git a/vendor/github.com/miekg/dns/zmsg.go b/vendor/github.com/miekg/dns/zmsg.go index d24a10fa24267..fc0822f982180 100644 --- a/vendor/github.com/miekg/dns/zmsg.go +++ b/vendor/github.com/miekg/dns/zmsg.go @@ -1118,6 +1118,26 @@ func (rr *X25) pack(msg []byte, off int, compression compressionMap, compress bo return off, nil } +func (rr *ZONEMD) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { + off, err = packUint32(rr.Serial, msg, off) + if err != nil { + return off, err + } + off, err = packUint8(rr.Scheme, msg, off) + if err != nil { + return off, err + } + off, err = packUint8(rr.Hash, msg, off) + if err != nil { + return off, err + } + off, err = packStringHex(rr.Digest, msg, off) + if err != nil { + return off, err + } + return off, nil +} + // unpack*() functions func (rr *A) unpack(msg []byte, off int) (off1 int, err error) { @@ -2821,3 +2841,35 @@ func (rr *X25) unpack(msg []byte, off int) (off1 int, err error) { } return off, nil } + +func (rr *ZONEMD) unpack(msg []byte, off int) (off1 int, err error) { + rdStart := off + _ = rdStart + + rr.Serial, off, err = unpackUint32(msg, off) + if err != nil { + return off, err + } + if off == len(msg) { + return off, nil + } + rr.Scheme, off, err = unpackUint8(msg, off) + if err != nil { + return off, err + } + if off == len(msg) { + return off, nil + } + rr.Hash, off, err = unpackUint8(msg, off) + if err != nil { + return off, err + } + if off == len(msg) { + return off, nil + } + rr.Digest, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength)) + if err != nil { + return off, err + } + return off, nil +} diff --git a/vendor/github.com/miekg/dns/ztypes.go b/vendor/github.com/miekg/dns/ztypes.go index 11b51bf217109..5d060cfee1725 100644 --- a/vendor/github.com/miekg/dns/ztypes.go +++ b/vendor/github.com/miekg/dns/ztypes.go @@ -82,6 +82,7 @@ var TypeToRR = map[uint16]func() RR{ TypeUINFO: func() RR { return new(UINFO) }, TypeURI: func() RR { return new(URI) }, TypeX25: func() RR { return new(X25) }, + TypeZONEMD: func() RR { return new(ZONEMD) }, } // TypeToString is a map of strings for each RR type. @@ -168,6 +169,7 @@ var TypeToString = map[uint16]string{ TypeUNSPEC: "UNSPEC", TypeURI: "URI", TypeX25: "X25", + TypeZONEMD: "ZONEMD", TypeNSAPPTR: "NSAP-PTR", } @@ -245,6 +247,7 @@ func (rr *UID) Header() *RR_Header { return &rr.Hdr } func (rr *UINFO) Header() *RR_Header { return &rr.Hdr } func (rr *URI) Header() *RR_Header { return &rr.Hdr } func (rr *X25) Header() *RR_Header { return &rr.Hdr } +func (rr *ZONEMD) Header() *RR_Header { return &rr.Hdr } // len() functions func (rr *A) len(off int, compression map[string]struct{}) int { @@ -684,6 +687,14 @@ func (rr *X25) len(off int, compression map[string]struct{}) int { l += len(rr.PSDNAddress) + 1 return l } +func (rr *ZONEMD) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += 4 // Serial + l++ // Scheme + l++ // Hash + l += len(rr.Digest) / 2 + return l +} // copy() functions func (rr *A) copy() RR { @@ -936,3 +947,6 @@ func (rr *URI) copy() RR { func (rr *X25) copy() RR { return &X25{rr.Hdr, rr.PSDNAddress} } +func (rr *ZONEMD) copy() RR { + return &ZONEMD{rr.Hdr, rr.Serial, rr.Scheme, rr.Hash, rr.Digest} +} diff --git a/vendor/github.com/pierrec/lz4/v4/internal/lz4block/block.go b/vendor/github.com/pierrec/lz4/v4/internal/lz4block/block.go index f38264943084f..88319105a6c83 100644 --- a/vendor/github.com/pierrec/lz4/v4/internal/lz4block/block.go +++ b/vendor/github.com/pierrec/lz4/v4/internal/lz4block/block.go @@ -41,11 +41,11 @@ func CompressBlockBound(n int) int { return n + n/255 + 16 } -func UncompressBlock(src, dst []byte) (int, error) { +func UncompressBlock(src, dst, dict []byte) (int, error) { if len(src) == 0 { return 0, nil } - if di := decodeBlock(dst, src); di >= 0 { + if di := decodeBlock(dst, src, dict); di >= 0 { return di, nil } return 0, lz4errors.ErrInvalidSourceShortBuffer @@ -187,6 +187,9 @@ func (c *Compressor) CompressBlock(src, dst []byte) (int, error) { } mLen = si - mLen + if di >= len(dst) { + return 0, lz4errors.ErrInvalidSourceShortBuffer + } if mLen < 0xF { dst[di] = byte(mLen) } else { diff --git a/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_asm.go b/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_amd64.go similarity index 59% rename from vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_asm.go rename to vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_amd64.go index e26f8cd613ed4..e16bd57937d20 100644 --- a/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_asm.go +++ b/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_amd64.go @@ -1,4 +1,3 @@ -// +build amd64 arm // +build !appengine // +build gc // +build !noasm @@ -6,4 +5,4 @@ package lz4block //go:noescape -func decodeBlock(dst, src []byte) int +func decodeBlock(dst, src, dict []byte) int diff --git a/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_amd64.s b/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_amd64.s index be79faa3fe85e..dfcca57291a42 100644 --- a/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_amd64.s +++ b/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_amd64.s @@ -16,9 +16,11 @@ // R11 &dst // R12 short output end // R13 short input end -// func decodeBlock(dst, src []byte) int -// using 50 bytes of stack currently -TEXT ·decodeBlock(SB), NOSPLIT, $64-56 +// R14 &dict +// R15 &dict + len(dict) + +// func decodeBlock(dst, src, dict []byte) int +TEXT ·decodeBlock(SB), NOSPLIT, $48-80 MOVQ dst_base+0(FP), DI MOVQ DI, R11 MOVQ dst_len+8(FP), R8 @@ -30,6 +32,10 @@ TEXT ·decodeBlock(SB), NOSPLIT, $64-56 JE err_corrupt ADDQ SI, R9 + MOVQ dict_base+48(FP), R14 + MOVQ dict_len+56(FP), R15 + ADDQ R14, R15 + // shortcut ends // short output end MOVQ R8, R12 @@ -96,6 +102,8 @@ loop: // match length, we already have the offset. CMPQ CX, $0xF JEQ match_len_loop_pre + CMPQ DX, R11 + JLT match_len_loop_pre CMPQ DX, $8 JLT match_len_loop_pre CMPQ AX, R11 @@ -174,6 +182,9 @@ copy_literal: MOVOU (SI), X0 MOVOU X0, (DI) + ADDQ CX, SI + ADDQ CX, DI + JMP finish_lit_copy memmove_lit: @@ -181,18 +192,20 @@ memmove_lit: MOVQ DI, 0(SP) MOVQ SI, 8(SP) MOVQ CX, 16(SP) - // spill + + // Spill registers. Increment SI, DI now so we don't need to save CX. + ADDQ CX, DI + ADDQ CX, SI MOVQ DI, 24(SP) MOVQ SI, 32(SP) - MOVQ CX, 40(SP) // need len to inc SI, DI after - MOVB DX, 48(SP) + MOVL DX, 40(SP) + CALL runtime·memmove(SB) // restore registers MOVQ 24(SP), DI MOVQ 32(SP), SI - MOVQ 40(SP), CX - MOVB 48(SP), DX + MOVL 40(SP), DX // recalc initial values MOVQ dst_base+0(FP), R8 @@ -206,9 +219,6 @@ memmove_lit: SUBQ $16, R13 finish_lit_copy: - ADDQ CX, SI - ADDQ CX, DI - CMPQ SI, R9 JGE end @@ -278,7 +288,7 @@ copy_match: // check BX is within dst // if BX < &dst CMPQ BX, R11 - JLT err_short_buf + JLT copy_match_from_dict // if offset + match_len < di LEAQ (BX)(CX*1), AX @@ -325,21 +335,49 @@ copy_interior_match: ADDQ CX, DI JMP loop -memmove_match: +copy_match_from_dict: + // CX = match_len + // BX = &dst + (di - offset) + + // AX = offset - di = dict_bytes_available => count of bytes potentially covered by the dictionary + MOVQ R11, AX + SUBQ BX, AX + + // BX = &dict_end - dict_bytes_available + MOVQ R15, BX + SUBQ AX, BX + + // check BX is within dict + // if BX < &dict + CMPQ BX, R14 + JLT err_short_dict + + // if match_len > dict_bytes_available, match fits entirely within external dictionary : just copy + CMPQ CX, AX + JLT memmove_match + + // The match stretches over the dictionary and our block + // 1) copy what comes from the dictionary + // AX = dict_bytes_available = copy_size + // BX = &dict_end - copy_size + // CX = match_len + // memmove(to, from, len) MOVQ DI, 0(SP) MOVQ BX, 8(SP) - MOVQ CX, 16(SP) + MOVQ AX, 16(SP) + // store extra stuff we want to recover // spill MOVQ DI, 24(SP) MOVQ SI, 32(SP) - MOVQ CX, 40(SP) // need len to inc SI, DI after + MOVQ CX, 40(SP) CALL runtime·memmove(SB) // restore registers + MOVQ 16(SP), AX // copy_size MOVQ 24(SP), DI MOVQ 32(SP), SI - MOVQ 40(SP), CX + MOVQ 40(SP), CX // match_len // recalc initial values MOVQ dst_base+0(FP), R8 @@ -347,23 +385,77 @@ memmove_match: ADDQ dst_len+8(FP), R8 MOVQ src_base+24(FP), R9 ADDQ src_len+32(FP), R9 + MOVQ dict_base+48(FP), R14 + MOVQ dict_len+56(FP), R15 + ADDQ R14, R15 MOVQ R8, R12 SUBQ $32, R12 MOVQ R9, R13 SUBQ $16, R13 + // di+=copy_size + ADDQ AX, DI + + // 2) copy the rest from the current block + // CX = match_len - copy_size = rest_size + SUBQ AX, CX + MOVQ R11, BX + + // check if we have a copy overlap + // AX = &dst + rest_size + MOVQ CX, AX + ADDQ BX, AX + // if &dst + rest_size > di, copy byte by byte + CMPQ AX, DI + + JGT copy_match_loop + +memmove_match: + // memmove(to, from, len) + MOVQ DI, 0(SP) + MOVQ BX, 8(SP) + MOVQ CX, 16(SP) + + // Spill registers. Increment DI now so we don't need to save CX. ADDQ CX, DI + MOVQ DI, 24(SP) + MOVQ SI, 32(SP) + + CALL runtime·memmove(SB) + + // restore registers + MOVQ 24(SP), DI + MOVQ 32(SP), SI + + // recalc initial values + MOVQ dst_base+0(FP), R8 + MOVQ R8, R11 // TODO: make these sensible numbers + ADDQ dst_len+8(FP), R8 + MOVQ src_base+24(FP), R9 + ADDQ src_len+32(FP), R9 + MOVQ R8, R12 + SUBQ $32, R12 + MOVQ R9, R13 + SUBQ $16, R13 + MOVQ dict_base+48(FP), R14 + MOVQ dict_len+56(FP), R15 + ADDQ R14, R15 + JMP loop err_corrupt: - MOVQ $-1, ret+48(FP) + MOVQ $-1, ret+72(FP) RET err_short_buf: - MOVQ $-2, ret+48(FP) + MOVQ $-2, ret+72(FP) + RET + +err_short_dict: + MOVQ $-3, ret+72(FP) RET end: SUBQ R11, DI - MOVQ DI, ret+48(FP) + MOVQ DI, ret+72(FP) RET diff --git a/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_arm.go b/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_arm.go new file mode 100644 index 0000000000000..c35acda0550f4 --- /dev/null +++ b/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_arm.go @@ -0,0 +1,15 @@ +// +build gc,!noasm + +package lz4block + +func decodeBlock(dst, src, dict []byte) int { + if len(dict) == 0 { + return decodeBlockNodict(dst, src) + } + return decodeBlockGo(dst, src, dict) +} + +// Assembler version of decodeBlock, without linked block support. + +//go:noescape +func decodeBlockNodict(dst, src []byte) int diff --git a/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_arm.s b/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_arm.s index 64be9adcaa871..5ae3214685092 100644 --- a/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_arm.s +++ b/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_arm.s @@ -19,12 +19,12 @@ #define minMatch $4 -// func decodeBlock(dst, src []byte) int -TEXT ·decodeBlock(SB), NOFRAME|NOSPLIT, $-4-28 - MOVW dst_base +0(FP), dst - MOVW dst_len +4(FP), dstend - MOVW src_base+12(FP), src - MOVW src_len +16(FP), srcend +// func decodeBlockNodict(dst, src []byte) int +TEXT ·decodeBlockNodict(SB), NOFRAME+NOSPLIT, $-4-28 + MOVW dst_base +0(FP), dst + MOVW dst_len +4(FP), dstend + MOVW src_base +12(FP), src + MOVW src_len +16(FP), srcend CMP $0, srcend BEQ shortSrc diff --git a/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_noasm.go b/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_noasm.go new file mode 100644 index 0000000000000..1cc703834ec91 --- /dev/null +++ b/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_noasm.go @@ -0,0 +1,7 @@ +// +build !amd64,!arm appengine !gc noasm + +package lz4block + +func decodeBlock(dst, src, dict []byte) int { + return decodeBlockGo(dst, src, dict) +} diff --git a/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_other.go b/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_other.go index 52df2f2b8ec25..4b580d4a6ff5c 100644 --- a/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_other.go +++ b/vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_other.go @@ -1,13 +1,14 @@ -// +build !amd64,!arm appengine !gc noasm - package lz4block -import "encoding/binary" +import ( + "encoding/binary" +) -func decodeBlock(dst, src []byte) (ret int) { +func decodeBlockGo(dst, src, dict []byte) (ret int) { // Restrict capacities so we don't read or write out of bounds. dst = dst[:len(dst):len(dst)] src = src[:len(src):len(src)] + dictLen := uint(len(dict)) const hasError = -2 defer func() { @@ -38,7 +39,7 @@ func decodeBlock(dst, src []byte) (ret int) { // if the match length (4..18) fits within the literals, then copy // all 18 bytes, even if not all are part of the literals. mLen += 4 - if offset := u16(src[si:]); mLen <= offset { + if offset := u16(src[si:]); mLen <= offset && offset < di { i := di - offset end := i + 18 if end > uint(len(dst)) { @@ -91,6 +92,38 @@ func decodeBlock(dst, src []byte) (ret int) { mLen += minMatch // Copy the match. + if di < offset { + // The match is beyond our block, meaning in the dictionary + if offset-di > mLen { + // The match is entirely contained in the dictionary. Just copy! + copy(dst[di:di+mLen], dict[dictLen+di-offset:dictLen+di-offset+mLen]) + di = di + mLen + } else { + // The match stretches over the dictionary and our block + copySize := offset - di + restSize := mLen - copySize + + copy(dst[di:di+copySize], dict[dictLen-copySize:]) + di = di + copySize + + if di < restSize { + // Overlap - we want to copy more than what we have available, + // so copy byte per byte. + copyFrom := 0 + endOfMatch := di + restSize + for di < endOfMatch { + dst[di] = dst[copyFrom] + di = di + 1 + copyFrom = copyFrom + 1 + } + } else { + copy(dst[di:di+restSize], dst[0:restSize]) + di = di + restSize + } + } + continue + } + expanded := dst[di-offset:] if mLen > offset { // Efficiently copy the match dst[di-offset:di] into the dst slice. diff --git a/vendor/github.com/pierrec/lz4/v4/internal/lz4stream/block.go b/vendor/github.com/pierrec/lz4/v4/internal/lz4stream/block.go index c7b929fdfec5a..e96465460c5d4 100644 --- a/vendor/github.com/pierrec/lz4/v4/internal/lz4stream/block.go +++ b/vendor/github.com/pierrec/lz4/v4/internal/lz4stream/block.go @@ -127,9 +127,11 @@ func (b *Blocks) initR(f *Frame, num int, src io.Reader) (chan []byte, error) { blocks <- c go func() { defer block.Close(f) - data, err := block.Uncompress(f, size.Get(), false) + data, err := block.Uncompress(f, size.Get(), nil, false) if err != nil { b.closeR(err) + // Close the block channel to indicate an error. + close(c) } else { c <- data } @@ -150,13 +152,24 @@ func (b *Blocks) initR(f *Frame, num int, src io.Reader) (chan []byte, error) { // on the returned channel. go func(leg bool) { defer close(blocks) + skipBlocks := false for c := range blocks { - buf := <-c + buf, ok := <-c + if !ok { + // A closed channel indicates an error. + // All remaining channels should be discarded. + skipBlocks = true + continue + } if buf == nil { // Signal to end the loop. close(c) return } + if skipBlocks { + // A previous error has occurred, skipping remaining channels. + continue + } // Perform checksum now as the blocks are received in order. if f.Descriptor.Flags.ContentChecksum() { _, _ = f.checksum.Write(buf) @@ -303,12 +316,12 @@ func (b *FrameDataBlock) Read(f *Frame, src io.Reader, cum uint32) (uint32, erro return x, nil } -func (b *FrameDataBlock) Uncompress(f *Frame, dst []byte, sum bool) ([]byte, error) { +func (b *FrameDataBlock) Uncompress(f *Frame, dst, dict []byte, sum bool) ([]byte, error) { if b.Size.Uncompressed() { n := copy(dst, b.data) dst = dst[:n] } else { - n, err := lz4block.UncompressBlock(b.data, dst) + n, err := lz4block.UncompressBlock(b.data, dst, dict) if err != nil { return nil, err } diff --git a/vendor/github.com/pierrec/lz4/v4/internal/lz4stream/frame.go b/vendor/github.com/pierrec/lz4/v4/internal/lz4stream/frame.go index cfbd5674d9d10..18192a9433d5d 100644 --- a/vendor/github.com/pierrec/lz4/v4/internal/lz4stream/frame.go +++ b/vendor/github.com/pierrec/lz4/v4/internal/lz4stream/frame.go @@ -77,16 +77,16 @@ func (f *Frame) isLegacy() bool { return f.Magic == frameMagicLegacy } -func (f *Frame) InitR(src io.Reader, num int) (chan []byte, error) { +func (f *Frame) ParseHeaders(src io.Reader) error { if f.Magic > 0 { // Header already read. - return nil, nil + return nil } newFrame: var err error if f.Magic, err = f.readUint32(src); err != nil { - return nil, err + return err } switch m := f.Magic; { case m == frameMagic || m == frameMagicLegacy: @@ -94,19 +94,23 @@ newFrame: case m>>8 == frameSkipMagic>>8: skip, err := f.readUint32(src) if err != nil { - return nil, err + return err } if _, err := io.CopyN(ioutil.Discard, src, int64(skip)); err != nil { - return nil, err + return err } goto newFrame default: - return nil, lz4errors.ErrInvalidFrame + return lz4errors.ErrInvalidFrame } if err := f.Descriptor.initR(f, src); err != nil { - return nil, err + return err } f.checksum.Reset() + return nil +} + +func (f *Frame) InitR(src io.Reader, num int) (chan []byte, error) { return f.Blocks.initR(f, num, src) } diff --git a/vendor/github.com/pierrec/lz4/v4/lz4.go b/vendor/github.com/pierrec/lz4/v4/lz4.go index c585d4064f5ce..a62022e08839c 100644 --- a/vendor/github.com/pierrec/lz4/v4/lz4.go +++ b/vendor/github.com/pierrec/lz4/v4/lz4.go @@ -35,7 +35,17 @@ func CompressBlockBound(n int) int { // // An error is returned if the source data is invalid or the destination buffer is too small. func UncompressBlock(src, dst []byte) (int, error) { - return lz4block.UncompressBlock(src, dst) + return lz4block.UncompressBlock(src, dst, nil) +} + +// UncompressBlockWithDict uncompresses the source buffer into the destination one using a +// dictionary, and returns the uncompressed size. +// +// The destination buffer must be sized appropriately. +// +// An error is returned if the source data is invalid or the destination buffer is too small. +func UncompressBlockWithDict(src, dst, dict []byte) (int, error) { + return lz4block.UncompressBlock(src, dst, dict) } // A Compressor compresses data into the LZ4 block format. diff --git a/vendor/github.com/pierrec/lz4/v4/options.go b/vendor/github.com/pierrec/lz4/v4/options.go index 4e1b6703b57d1..46a87380313f4 100644 --- a/vendor/github.com/pierrec/lz4/v4/options.go +++ b/vendor/github.com/pierrec/lz4/v4/options.go @@ -2,10 +2,11 @@ package lz4 import ( "fmt" - "github.com/pierrec/lz4/v4/internal/lz4block" - "github.com/pierrec/lz4/v4/internal/lz4errors" "reflect" "runtime" + + "github.com/pierrec/lz4/v4/internal/lz4block" + "github.com/pierrec/lz4/v4/internal/lz4errors" ) //go:generate go run golang.org/x/tools/cmd/stringer -type=BlockSize,CompressionLevel -output options_gen.go diff --git a/vendor/github.com/pierrec/lz4/v4/reader.go b/vendor/github.com/pierrec/lz4/v4/reader.go index 403aaf697a3b7..f8458807c4e7f 100644 --- a/vendor/github.com/pierrec/lz4/v4/reader.go +++ b/vendor/github.com/pierrec/lz4/v4/reader.go @@ -40,6 +40,7 @@ type Reader struct { idx int // size of pending data handler func(int) cum uint32 + dict []byte } func (*Reader) private() {} @@ -77,6 +78,15 @@ func (r *Reader) isNotConcurrent() bool { } func (r *Reader) init() error { + err := r.frame.ParseHeaders(r.src) + if err != nil { + return err + } + if !r.frame.Descriptor.Flags.BlockIndependence() { + // We can't decompress dependent blocks concurrently. + // Instead of throwing an error to the user, silently drop concurrency + r.num = 1 + } data, err := r.frame.InitR(r.src, r.num) if err != nil { return err @@ -162,10 +172,20 @@ func (r *Reader) read(buf []byte) (int, error) { direct = true dst = buf } - dst, err = block.Uncompress(r.frame, dst, true) + dst, err = block.Uncompress(r.frame, dst, r.dict, true) if err != nil { return 0, err } + if !r.frame.Descriptor.Flags.BlockIndependence() { + if len(r.dict)+len(dst) > 128*1024 { + preserveSize := 64*1024 - len(dst) + if preserveSize < 0 { + preserveSize = 0 + } + r.dict = r.dict[len(r.dict)-preserveSize:] + } + r.dict = append(r.dict, dst...) + } r.cum += uint32(len(dst)) if direct { return len(dst), nil diff --git a/vendor/github.com/pierrec/lz4/v4/writer.go b/vendor/github.com/pierrec/lz4/v4/writer.go index 44a43d251b0d8..05a6aa14a8713 100644 --- a/vendor/github.com/pierrec/lz4/v4/writer.go +++ b/vendor/github.com/pierrec/lz4/v4/writer.go @@ -89,7 +89,7 @@ func (w *Writer) Write(buf []byte) (n int, err error) { zn := len(w.data) for len(buf) > 0 { - if w.idx == 0 && len(buf) >= zn { + if w.isNotConcurrent() && w.idx == 0 && len(buf) >= zn { // Avoid a copy as there is enough data for a block. if err = w.write(buf[:zn], false); err != nil { return diff --git a/vendor/github.com/prometheus/client_golang/prometheus/expvar_collector.go b/vendor/github.com/prometheus/client_golang/prometheus/expvar_collector.go index 18a99d5faaa5b..c41ab37f3bb9b 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/expvar_collector.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/expvar_collector.go @@ -22,43 +22,10 @@ type expvarCollector struct { exports map[string]*Desc } -// NewExpvarCollector returns a newly allocated expvar Collector that still has -// to be registered with a Prometheus registry. +// NewExpvarCollector is the obsolete version of collectors.NewExpvarCollector. +// See there for documentation. // -// An expvar Collector collects metrics from the expvar interface. It provides a -// quick way to expose numeric values that are already exported via expvar as -// Prometheus metrics. Note that the data models of expvar and Prometheus are -// fundamentally different, and that the expvar Collector is inherently slower -// than native Prometheus metrics. Thus, the expvar Collector is probably great -// for experiments and prototying, but you should seriously consider a more -// direct implementation of Prometheus metrics for monitoring production -// systems. -// -// The exports map has the following meaning: -// -// The keys in the map correspond to expvar keys, i.e. for every expvar key you -// want to export as Prometheus metric, you need an entry in the exports -// map. The descriptor mapped to each key describes how to export the expvar -// value. It defines the name and the help string of the Prometheus metric -// proxying the expvar value. The type will always be Untyped. -// -// For descriptors without variable labels, the expvar value must be a number or -// a bool. The number is then directly exported as the Prometheus sample -// value. (For a bool, 'false' translates to 0 and 'true' to 1). Expvar values -// that are not numbers or bools are silently ignored. -// -// If the descriptor has one variable label, the expvar value must be an expvar -// map. The keys in the expvar map become the various values of the one -// Prometheus label. The values in the expvar map must be numbers or bools again -// as above. -// -// For descriptors with more than one variable label, the expvar must be a -// nested expvar map, i.e. where the values of the topmost map are maps again -// etc. until a depth is reached that corresponds to the number of labels. The -// leaves of that structure must be numbers or bools as above to serve as the -// sample values. -// -// Anything that does not fit into the scheme above is silently ignored. +// Deprecated: Use collectors.NewExpvarCollector instead. func NewExpvarCollector(exports map[string]*Desc) Collector { return &expvarCollector{ exports: exports, diff --git a/vendor/github.com/prometheus/client_golang/prometheus/go_collector.go b/vendor/github.com/prometheus/client_golang/prometheus/go_collector.go index db43ca5bab20e..a96ed1cee885b 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/go_collector.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/go_collector.go @@ -36,32 +36,10 @@ type goCollector struct { msMaxAge time.Duration // Maximum allowed age of old memstats. } -// NewGoCollector returns a collector that exports metrics about the current Go -// process. This includes memory stats. To collect those, runtime.ReadMemStats -// is called. This requires to “stop the world”, which usually only happens for -// garbage collection (GC). Take the following implications into account when -// deciding whether to use the Go collector: +// NewGoCollector is the obsolete version of collectors.NewGoCollector. +// See there for documentation. // -// 1. The performance impact of stopping the world is the more relevant the more -// frequently metrics are collected. However, with Go1.9 or later the -// stop-the-world time per metrics collection is very short (~25µs) so that the -// performance impact will only matter in rare cases. However, with older Go -// versions, the stop-the-world duration depends on the heap size and can be -// quite significant (~1.7 ms/GiB as per -// https://go-review.googlesource.com/c/go/+/34937). -// -// 2. During an ongoing GC, nothing else can stop the world. Therefore, if the -// metrics collection happens to coincide with GC, it will only complete after -// GC has finished. Usually, GC is fast enough to not cause problems. However, -// with a very large heap, GC might take multiple seconds, which is enough to -// cause scrape timeouts in common setups. To avoid this problem, the Go -// collector will use the memstats from a previous collection if -// runtime.ReadMemStats takes more than 1s. However, if there are no previously -// collected memstats, or their collection is more than 5m ago, the collection -// will block until runtime.ReadMemStats succeeds. -// -// NOTE: The problem is solved in Go 1.15, see -// https://github.com/golang/go/issues/19812 for the related Go issue. +// Deprecated: Use collectors.NewGoCollector instead. func NewGoCollector() Collector { return &goCollector{ goroutinesDesc: NewDesc( @@ -366,23 +344,10 @@ type memStatsMetrics []struct { valType ValueType } -// NewBuildInfoCollector returns a collector collecting a single metric -// "go_build_info" with the constant value 1 and three labels "path", "version", -// and "checksum". Their label values contain the main module path, version, and -// checksum, respectively. The labels will only have meaningful values if the -// binary is built with Go module support and from source code retrieved from -// the source repository (rather than the local file system). This is usually -// accomplished by building from outside of GOPATH, specifying the full address -// of the main package, e.g. "GO111MODULE=on go run -// github.com/prometheus/client_golang/examples/random". If built without Go -// module support, all label values will be "unknown". If built with Go module -// support but using the source code from the local file system, the "path" will -// be set appropriately, but "checksum" will be empty and "version" will be -// "(devel)". +// NewBuildInfoCollector is the obsolete version of collectors.NewBuildInfoCollector. +// See there for documentation. // -// This collector uses only the build information for the main module. See -// https://github.com/povilasv/prommod for an example of a collector for the -// module dependencies. +// Deprecated: Use collectors.NewBuildInfoCollector instead. func NewBuildInfoCollector() Collector { path, version, sum := "unknown", "unknown", "unknown" if bi, ok := debug.ReadBuildInfo(); ok { diff --git a/vendor/github.com/prometheus/client_golang/prometheus/histogram.go b/vendor/github.com/prometheus/client_golang/prometheus/histogram.go index 3346fa1c55617..8425640b390ab 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/histogram.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/histogram.go @@ -47,7 +47,12 @@ type Histogram interface { Metric Collector - // Observe adds a single observation to the histogram. + // Observe adds a single observation to the histogram. Observations are + // usually positive or zero. Negative observations are accepted but + // prevent current versions of Prometheus from properly detecting + // counter resets in the sum of observations. See + // https://prometheus.io/docs/practices/histograms/#count-and-sum-of-observations + // for details. Observe(float64) } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/process_collector.go b/vendor/github.com/prometheus/client_golang/prometheus/process_collector.go index c46702d60b52e..5bfe0ff5bbc9e 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/process_collector.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/process_collector.go @@ -54,16 +54,10 @@ type ProcessCollectorOpts struct { ReportErrors bool } -// NewProcessCollector returns a collector which exports the current state of -// process metrics including CPU, memory and file descriptor usage as well as -// the process start time. The detailed behavior is defined by the provided -// ProcessCollectorOpts. The zero value of ProcessCollectorOpts creates a -// collector for the current process with an empty namespace string and no error -// reporting. +// NewProcessCollector is the obsolete version of collectors.NewProcessCollector. +// See there for documentation. // -// The collector only works on operating systems with a Linux-style proc -// filesystem and on Microsoft Windows. On other operating systems, it will not -// collect any metrics. +// Deprecated: Use collectors.NewProcessCollector instead. func NewProcessCollector(opts ProcessCollectorOpts) Collector { ns := "" if len(opts.Namespace) > 0 { diff --git a/vendor/github.com/prometheus/client_golang/prometheus/summary.go b/vendor/github.com/prometheus/client_golang/prometheus/summary.go index fb5ce22bf60f2..c5fa8ed7c71a2 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/summary.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/summary.go @@ -55,7 +55,12 @@ type Summary interface { Metric Collector - // Observe adds a single observation to the summary. + // Observe adds a single observation to the summary. Observations are + // usually positive or zero. Negative observations are accepted but + // prevent current versions of Prometheus from properly detecting + // counter resets in the sum of observations. See + // https://prometheus.io/docs/practices/histograms/#count-and-sum-of-observations + // for details. Observe(float64) } @@ -121,7 +126,9 @@ type SummaryOpts struct { Objectives map[float64]float64 // MaxAge defines the duration for which an observation stays relevant - // for the summary. Must be positive. The default value is DefMaxAge. + // for the summary. Only applies to pre-calculated quantiles, does not + // apply to _sum and _count. Must be positive. The default value is + // DefMaxAge. MaxAge time.Duration // AgeBuckets is the number of buckets used to exclude observations that diff --git a/vendor/github.com/prometheus/common/model/labels.go b/vendor/github.com/prometheus/common/model/labels.go index 41051a01a36d4..ef89563354684 100644 --- a/vendor/github.com/prometheus/common/model/labels.go +++ b/vendor/github.com/prometheus/common/model/labels.go @@ -45,6 +45,14 @@ const ( // scrape a target. MetricsPathLabel = "__metrics_path__" + // ScrapeIntervalLabel is the name of the label that holds the scrape interval + // used to scrape a target. + ScrapeIntervalLabel = "__scrape_interval__" + + // ScrapeTimeoutLabel is the name of the label that holds the scrape + // timeout used to scrape a target. + ScrapeTimeoutLabel = "__scrape_timeout__" + // ReservedLabelPrefix is a prefix which is not legal in user-supplied // label names. ReservedLabelPrefix = "__" diff --git a/vendor/github.com/prometheus/common/model/time.go b/vendor/github.com/prometheus/common/model/time.go index 77b82b2baa20a..7f67b16e42955 100644 --- a/vendor/github.com/prometheus/common/model/time.go +++ b/vendor/github.com/prometheus/common/model/time.go @@ -14,6 +14,8 @@ package model import ( + "encoding/json" + "errors" "fmt" "math" "regexp" @@ -201,13 +203,23 @@ func ParseDuration(durationStr string) (Duration, error) { // Parse the match at pos `pos` in the regex and use `mult` to turn that // into ms, then add that value to the total parsed duration. + var overflowErr error m := func(pos int, mult time.Duration) { if matches[pos] == "" { return } n, _ := strconv.Atoi(matches[pos]) + + // Check if the provided duration overflows time.Duration (> ~ 290years). + if n > int((1<<63-1)/mult/time.Millisecond) { + overflowErr = errors.New("duration out of range") + } d := time.Duration(n) * time.Millisecond dur += d * mult + + if dur < 0 { + overflowErr = errors.New("duration out of range") + } } m(2, 1000*60*60*24*365) // y @@ -218,7 +230,7 @@ func ParseDuration(durationStr string) (Duration, error) { m(12, 1000) // s m(14, 1) // ms - return Duration(dur), nil + return Duration(dur), overflowErr } func (d Duration) String() string { @@ -254,6 +266,25 @@ func (d Duration) String() string { return r } +// MarshalJSON implements the json.Marshaler interface. +func (d Duration) MarshalJSON() ([]byte, error) { + return json.Marshal(d.String()) +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (d *Duration) UnmarshalJSON(bytes []byte) error { + var s string + if err := json.Unmarshal(bytes, &s); err != nil { + return err + } + dur, err := ParseDuration(s) + if err != nil { + return err + } + *d = dur + return nil +} + // MarshalText implements the encoding.TextMarshaler interface. func (d *Duration) MarshalText() ([]byte, error) { return []byte(d.String()), nil diff --git a/vendor/github.com/rs/xid/README.md b/vendor/github.com/rs/xid/README.md index 1f886fd7c0d2e..79818877bc150 100644 --- a/vendor/github.com/rs/xid/README.md +++ b/vendor/github.com/rs/xid/README.md @@ -2,9 +2,9 @@ [![godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/rs/xid) [![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://raw.githubusercontent.com/rs/xid/master/LICENSE) [![Build Status](https://travis-ci.org/rs/xid.svg?branch=master)](https://travis-ci.org/rs/xid) [![Coverage](http://gocover.io/_badge/github.com/rs/xid)](http://gocover.io/github.com/rs/xid) -Package xid is a globally unique id generator library, ready to be used safely directly in your server code. +Package xid is a globally unique id generator library, ready to safely be used directly in your server code. -Xid is using Mongo Object ID algorithm to generate globally unique ids with a different serialization (base64) to make it shorter when transported as a string: +Xid uses the Mongo Object ID algorithm to generate globally unique ids with a different serialization (base64) to make it shorter when transported as a string: https://docs.mongodb.org/manual/reference/object-id/ - 4-byte value representing the seconds since the Unix epoch, @@ -33,7 +33,7 @@ is required so it can be used directly in server's code. |-------------|-------------|----------------|---------------- | [UUID] | 16 bytes | 36 chars | configuration free, not sortable | [shortuuid] | 16 bytes | 22 chars | configuration free, not sortable -| [Snowflake] | 8 bytes | up to 20 chars | needs machin/DC configuration, needs central server, sortable +| [Snowflake] | 8 bytes | up to 20 chars | needs machine/DC configuration, needs central server, sortable | [MongoID] | 12 bytes | 24 chars | configuration free, sortable | xid | 12 bytes | 20 chars | configuration free, sortable @@ -57,7 +57,7 @@ Best used with [zerolog](https://github.com/rs/zerolog)'s Notes: -- Xid is dependent on the system time, a monotonic counter and so is not cryptographically secure. If unpredictability of IDs is important, you should not use Xids. It is worth noting that most of the other UUID like implementations are also not cryptographically secure. You shoud use libraries that rely on cryptographically secure sources (like /dev/urandom on unix, crypto/rand in golang), if you want a truly random ID generator. +- Xid is dependent on the system time, a monotonic counter and so is not cryptographically secure. If unpredictability of IDs is important, you should not use Xids. It is worth noting that most other UUID-like implementations are also not cryptographically secure. You should use libraries that rely on cryptographically secure sources (like /dev/urandom on unix, crypto/rand in golang), if you want a truly random ID generator. References: @@ -66,6 +66,9 @@ References: - https://blog.twitter.com/2010/announcing-snowflake - Python port by [Graham Abbott](https://github.com/graham): https://github.com/graham/python_xid - Scala port by [Egor Kolotaev](https://github.com/kolotaev): https://github.com/kolotaev/ride +- Rust port by [Jérôme Renard](https://github.com/jeromer/): https://github.com/jeromer/libxid +- Ruby port by [Valar](https://github.com/valarpirai/): https://github.com/valarpirai/ruby_xid +- Java port by [0xShamil](https://github.com/0xShamil/): https://github.com/0xShamil/java-xid ## Install @@ -105,7 +108,7 @@ BenchmarkUUIDv4-2 1000000 1427 ns/op 64 B/op 2 allocs/op BenchmarkUUIDv4-4 1000000 1452 ns/op 64 B/op 2 allocs/op ``` -Note: UUIDv1 requires a global lock, hence the performence degrading as we add more CPUs. +Note: UUIDv1 requires a global lock, hence the performance degradation as we add more CPUs. ## Licenses diff --git a/vendor/github.com/rs/xid/go.mod b/vendor/github.com/rs/xid/go.mod index 95b8338613486..3d61b506936ff 100644 --- a/vendor/github.com/rs/xid/go.mod +++ b/vendor/github.com/rs/xid/go.mod @@ -1 +1,3 @@ module github.com/rs/xid + +go 1.12 diff --git a/vendor/github.com/rs/xid/hostid_linux.go b/vendor/github.com/rs/xid/hostid_linux.go index 7d0c4a9ec4a10..837b20436c536 100644 --- a/vendor/github.com/rs/xid/hostid_linux.go +++ b/vendor/github.com/rs/xid/hostid_linux.go @@ -5,6 +5,9 @@ package xid import "io/ioutil" func readPlatformMachineID() (string, error) { - b, err := ioutil.ReadFile("/sys/class/dmi/id/product_uuid") + b, err := ioutil.ReadFile("/etc/machine-id") + if err != nil || len(b) == 0 { + b, err = ioutil.ReadFile("/sys/class/dmi/id/product_uuid") + } return string(b), err } diff --git a/vendor/github.com/rs/xid/id.go b/vendor/github.com/rs/xid/id.go index 466faf2628f99..f1db1a18e9d35 100644 --- a/vendor/github.com/rs/xid/id.go +++ b/vendor/github.com/rs/xid/id.go @@ -55,6 +55,7 @@ import ( "sort" "sync/atomic" "time" + "unsafe" ) // Code inspired from mgo/bson ObjectId @@ -177,7 +178,13 @@ func FromString(id string) (ID, error) { func (id ID) String() string { text := make([]byte, encodedLen) encode(text, id[:]) - return string(text) + return *(*string)(unsafe.Pointer(&text)) +} + +// Encode encodes the id using base32 encoding, writing 20 bytes to dst and return it. +func (id ID) Encode(dst []byte) []byte { + encode(dst, id[:]) + return dst } // MarshalText implements encoding/text TextMarshaler interface @@ -192,32 +199,37 @@ func (id ID) MarshalJSON() ([]byte, error) { if id.IsNil() { return []byte("null"), nil } - text, err := id.MarshalText() - return []byte(`"` + string(text) + `"`), err + text := make([]byte, encodedLen+2) + encode(text[1:encodedLen+1], id[:]) + text[0], text[encodedLen+1] = '"', '"' + return text, nil } // encode by unrolling the stdlib base32 algorithm + removing all safe checks func encode(dst, id []byte) { - dst[0] = encoding[id[0]>>3] - dst[1] = encoding[(id[1]>>6)&0x1F|(id[0]<<2)&0x1F] - dst[2] = encoding[(id[1]>>1)&0x1F] - dst[3] = encoding[(id[2]>>4)&0x1F|(id[1]<<4)&0x1F] - dst[4] = encoding[id[3]>>7|(id[2]<<1)&0x1F] - dst[5] = encoding[(id[3]>>2)&0x1F] - dst[6] = encoding[id[4]>>5|(id[3]<<3)&0x1F] - dst[7] = encoding[id[4]&0x1F] - dst[8] = encoding[id[5]>>3] - dst[9] = encoding[(id[6]>>6)&0x1F|(id[5]<<2)&0x1F] - dst[10] = encoding[(id[6]>>1)&0x1F] - dst[11] = encoding[(id[7]>>4)&0x1F|(id[6]<<4)&0x1F] - dst[12] = encoding[id[8]>>7|(id[7]<<1)&0x1F] - dst[13] = encoding[(id[8]>>2)&0x1F] - dst[14] = encoding[(id[9]>>5)|(id[8]<<3)&0x1F] - dst[15] = encoding[id[9]&0x1F] - dst[16] = encoding[id[10]>>3] - dst[17] = encoding[(id[11]>>6)&0x1F|(id[10]<<2)&0x1F] - dst[18] = encoding[(id[11]>>1)&0x1F] + _ = dst[19] + _ = id[11] + dst[19] = encoding[(id[11]<<4)&0x1F] + dst[18] = encoding[(id[11]>>1)&0x1F] + dst[17] = encoding[(id[11]>>6)&0x1F|(id[10]<<2)&0x1F] + dst[16] = encoding[id[10]>>3] + dst[15] = encoding[id[9]&0x1F] + dst[14] = encoding[(id[9]>>5)|(id[8]<<3)&0x1F] + dst[13] = encoding[(id[8]>>2)&0x1F] + dst[12] = encoding[id[8]>>7|(id[7]<<1)&0x1F] + dst[11] = encoding[(id[7]>>4)&0x1F|(id[6]<<4)&0x1F] + dst[10] = encoding[(id[6]>>1)&0x1F] + dst[9] = encoding[(id[6]>>6)&0x1F|(id[5]<<2)&0x1F] + dst[8] = encoding[id[5]>>3] + dst[7] = encoding[id[4]&0x1F] + dst[6] = encoding[id[4]>>5|(id[3]<<3)&0x1F] + dst[5] = encoding[(id[3]>>2)&0x1F] + dst[4] = encoding[id[3]>>7|(id[2]<<1)&0x1F] + dst[3] = encoding[(id[2]>>4)&0x1F|(id[1]<<4)&0x1F] + dst[2] = encoding[(id[1]>>1)&0x1F] + dst[1] = encoding[(id[1]>>6)&0x1F|(id[0]<<2)&0x1F] + dst[0] = encoding[id[0]>>3] } // UnmarshalText implements encoding/text TextUnmarshaler interface @@ -246,18 +258,21 @@ func (id *ID) UnmarshalJSON(b []byte) error { // decode by unrolling the stdlib base32 algorithm + removing all safe checks func decode(id *ID, src []byte) { - id[0] = dec[src[0]]<<3 | dec[src[1]]>>2 - id[1] = dec[src[1]]<<6 | dec[src[2]]<<1 | dec[src[3]]>>4 - id[2] = dec[src[3]]<<4 | dec[src[4]]>>1 - id[3] = dec[src[4]]<<7 | dec[src[5]]<<2 | dec[src[6]]>>3 - id[4] = dec[src[6]]<<5 | dec[src[7]] - id[5] = dec[src[8]]<<3 | dec[src[9]]>>2 - id[6] = dec[src[9]]<<6 | dec[src[10]]<<1 | dec[src[11]]>>4 - id[7] = dec[src[11]]<<4 | dec[src[12]]>>1 - id[8] = dec[src[12]]<<7 | dec[src[13]]<<2 | dec[src[14]]>>3 - id[9] = dec[src[14]]<<5 | dec[src[15]] - id[10] = dec[src[16]]<<3 | dec[src[17]]>>2 + _ = src[19] + _ = id[11] + id[11] = dec[src[17]]<<6 | dec[src[18]]<<1 | dec[src[19]]>>4 + id[10] = dec[src[16]]<<3 | dec[src[17]]>>2 + id[9] = dec[src[14]]<<5 | dec[src[15]] + id[8] = dec[src[12]]<<7 | dec[src[13]]<<2 | dec[src[14]]>>3 + id[7] = dec[src[11]]<<4 | dec[src[12]]>>1 + id[6] = dec[src[9]]<<6 | dec[src[10]]<<1 | dec[src[11]]>>4 + id[5] = dec[src[8]]<<3 | dec[src[9]]>>2 + id[4] = dec[src[6]]<<5 | dec[src[7]] + id[3] = dec[src[4]]<<7 | dec[src[5]]<<2 | dec[src[6]]>>3 + id[2] = dec[src[3]]<<4 | dec[src[4]]>>1 + id[1] = dec[src[1]]<<6 | dec[src[2]]<<1 | dec[src[3]]>>4 + id[0] = dec[src[0]]<<3 | dec[src[1]]>>2 } // Time returns the timestamp part of the id. diff --git a/vendor/github.com/unrolled/render/.gitignore b/vendor/github.com/unrolled/render/.gitignore deleted file mode 100644 index 05f4eaf615f7d..0000000000000 --- a/vendor/github.com/unrolled/render/.gitignore +++ /dev/null @@ -1,27 +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 -*.test - - -*.pem -.DS_Store diff --git a/vendor/github.com/unrolled/render/.golangci.yaml b/vendor/github.com/unrolled/render/.golangci.yaml new file mode 100644 index 0000000000000..a8d0feef46ebb --- /dev/null +++ b/vendor/github.com/unrolled/render/.golangci.yaml @@ -0,0 +1,56 @@ +issues: + exclude: + - G203 + +linters: + disable-all: true + enable: + - bodyclose + - deadcode + - depguard + - dogsled + - dupl + - errcheck + - exhaustive + - goconst + - gocritic + - gocyclo + - goimports + - goprintffuncname + - gosec + - gosimple + - govet + - ineffassign + - nakedret + - nolintlint + - rowserrcheck + - staticcheck + - structcheck + - stylecheck + - typecheck + - unconvert + - unparam + - unused + - varcheck + - whitespace + - noctx + - misspell + # - golint + # - gochecknoinits + # - funlen + # - gofmt + # - scopelint + # - interfacer + # - gomnd + # - lll + # - asciicheck + # - gochecknoglobals + # - gocognit + # - godot + # - godox + # - goerr113 + # - maligned + # - nestif + # - prealloc + # - testpackage + # - wsl diff --git a/vendor/github.com/unrolled/render/Makefile b/vendor/github.com/unrolled/render/Makefile new file mode 100644 index 0000000000000..25b5d1e11f3c2 --- /dev/null +++ b/vendor/github.com/unrolled/render/Makefile @@ -0,0 +1,14 @@ +.PHONY: help test +.DEFAULT_GOAL := help + +help: ## Displays this help message. + @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) + +test: ## Runs the linter, tests, and vetting. + golangci-lint run ./... + go test -cover -race -count=1 ./... + go vet ./... + +ci: ## Runs on the tests and vetting for CI. + go test -cover -race -count=1 ./... + go vet ./... diff --git a/vendor/github.com/unrolled/render/README.md b/vendor/github.com/unrolled/render/README.md index abc210ea4b69f..a3de9bc56a91b 100644 --- a/vendor/github.com/unrolled/render/README.md +++ b/vendor/github.com/unrolled/render/README.md @@ -1,10 +1,7 @@ # Render [![GoDoc](http://godoc.org/github.com/unrolled/render?status.svg)](http://godoc.org/github.com/unrolled/render) [![Test](https://github.com/unrolled/render/workflows/Test/badge.svg?branch=v1)](https://github.com/unrolled/render/actions) -Render is a package that provides functionality for easily rendering JSON, XML, text, binary data, and HTML templates. This package is based on the [Martini](https://github.com/go-martini/martini) [render](https://github.com/martini-contrib/render) work. - -## Block Deprecation Notice -Go 1.6 introduces a new [block](https://github.com/golang/go/blob/release-branch.go1.6/src/html/template/example_test.go#L128) action. This conflicts with Render's included `block` template function. To provide an easy migration path, a new function was created called `partial`. It is a duplicate of the old `block` function. It is advised that all users of the `block` function update their code to avoid any issues in the future. Previous to Go 1.6, Render's `block` functionality will continue to work but a message will be logged urging you to migrate to the new `partial` function. +Render is a package that provides functionality for easily rendering JSON, XML, text, binary data, and HTML templates. ## Usage Render can be used with pretty much any web framework providing you can access the `http.ResponseWriter` from your handler. The rendering functions simply wraps Go's existing functionality for marshaling and rendering data. @@ -23,7 +20,7 @@ import ( "encoding/xml" "net/http" - "github.com/unrolled/render" // or "gopkg.in/unrolled/render.v1" + "github.com/unrolled/render" ) type ExampleXml struct { @@ -101,6 +98,7 @@ r := render.New(render.Options{ PrefixXML: []byte(""), // Prefixes XML responses with the given bytes. HTMLContentType: "application/xhtml+xml", // Output XHTML content type instead of default "text/html". IsDevelopment: true, // Render will now recompile the templates on every HTML response. + UseMutexLock: true, // Overrides the default no lock implementation and uses the standard `sync.RWMutex` lock. UnEscapeHTML: true, // Replace ensure '&<>' are output correctly (JSON only). StreamingJSON: true, // Streams the JSON response via json.Encoder. RequirePartials: true, // Return an error if a template is missing a partial used in a layout. @@ -139,10 +137,13 @@ r := render.New(render.Options{ TextContentType: "text/plain", XMLContentType: "application/xhtml+xml", IsDevelopment: false, + UseMutexLock: false, UnEscapeHTML: false, StreamingJSON: false, RequirePartials: false, DisableHTTPErrorRendering: false, + RenderPartialsWithoutPrefix: false, + BufferPool: GenericBufferPool, }) ~~~ @@ -171,7 +172,27 @@ admin/edit home ~~~ -You can also load templates from memory by providing the Asset and AssetNames options, +Templates can be loaded from an `embed.FS`. + +~~~ go +// ... + +//go:embed templates/*.html templates/*.tmpl +var embeddedTemplates embed.FS + +// ... + +r := render.New(render.Options{ + Directory: "templates", + FileSystem: &render.EmbedFileSystem{ + FS: embeddedTemplates, + }, + Extensions: []string{".html", ".tmpl"}, +}) +// ... +~~~ + +You can also load templates from memory by providing the `Asset` and `AssetNames` options, e.g. when generating an asset file using [go-bindata](https://github.com/jteeuwen/go-bindata). ### Layouts @@ -243,7 +264,7 @@ import ( "encoding/xml" "net/http" - "github.com/unrolled/render" // or "gopkg.in/unrolled/render.v1" + "github.com/unrolled/render" ) type ExampleXml struct { @@ -297,7 +318,7 @@ import ( "encoding/xml" "net/http" - "github.com/unrolled/render" // or "gopkg.in/unrolled/render.v1" + "github.com/unrolled/render" ) type ExampleXml struct { @@ -376,7 +397,7 @@ import ( "net/http" "github.com/labstack/echo" - "github.com/unrolled/render" // or "gopkg.in/unrolled/render.v1" + "github.com/unrolled/render" ) type RenderWrapper struct { // We need to wrap the renderer because we need a different signature for echo. @@ -398,7 +419,7 @@ func main() { return c.Render(http.StatusOK, "TemplateName", "TemplateData") }) - e.Logger.Fatal(e.Start(":1323")) + e.Logger.Fatal(e.Start("127.0.0.1:8080")) } ~~~ @@ -411,7 +432,7 @@ import ( "net/http" "github.com/gin-gonic/gin" - "github.com/unrolled/render" // or "gopkg.in/unrolled/render.v1" + "github.com/unrolled/render" ) func main() { @@ -425,7 +446,7 @@ func main() { r.JSON(c.Writer, http.StatusOK, map[string]string{"welcome": "This is rendered JSON!"}) }) - router.Run(":3000") + router.Run("127.0.0.1:8080") } ~~~ @@ -439,7 +460,7 @@ import ( "github.com/zenazn/goji" "github.com/zenazn/goji/web" - "github.com/unrolled/render" // or "gopkg.in/unrolled/render.v1" + "github.com/unrolled/render" ) func main() { @@ -463,7 +484,7 @@ import ( "net/http" "github.com/urfave/negroni" - "github.com/unrolled/render" // or "gopkg.in/unrolled/render.v1" + "github.com/unrolled/render" ) func main() { @@ -478,7 +499,7 @@ func main() { n := negroni.Classic() n.UseHandler(mux) - n.Run(":3000") + n.Run("127.0.0.1:8080") } ~~~ @@ -491,7 +512,7 @@ import ( "net/http" "github.com/pilu/traffic" - "github.com/unrolled/render" // or "gopkg.in/unrolled/render.v1" + "github.com/unrolled/render" ) func main() { @@ -504,6 +525,6 @@ func main() { r.JSON(w, http.StatusOK, map[string]string{"welcome": "This is rendered JSON!"}) }) - router.Run() + router.Run() // Defaults to "127.0.0.1:3000". } ~~~ diff --git a/vendor/github.com/unrolled/render/buffer.go b/vendor/github.com/unrolled/render/buffer.go deleted file mode 100644 index 804d775b7d59c..0000000000000 --- a/vendor/github.com/unrolled/render/buffer.go +++ /dev/null @@ -1,38 +0,0 @@ -package render - -import "bytes" - -// BufferPool implements a pool of bytes.Buffers in the form of a bounded channel. -// Pulled from the github.com/oxtoacart/bpool package (Apache licensed). -type BufferPool struct { - c chan *bytes.Buffer -} - -// NewBufferPool creates a new BufferPool bounded to the given size. -func NewBufferPool(size int) (bp *BufferPool) { - return &BufferPool{ - c: make(chan *bytes.Buffer, size), - } -} - -// Get gets a Buffer from the BufferPool, or creates a new one if none are -// available in the pool. -func (bp *BufferPool) Get() (b *bytes.Buffer) { - select { - case b = <-bp.c: - // reuse existing buffer - default: - // create new buffer - b = bytes.NewBuffer([]byte{}) - } - return -} - -// Put returns the given Buffer to the BufferPool. -func (bp *BufferPool) Put(b *bytes.Buffer) { - b.Reset() - select { - case bp.c <- b: - default: // Discard the buffer if the pool is full. - } -} diff --git a/vendor/github.com/unrolled/render/doc.go b/vendor/github.com/unrolled/render/doc.go index d3487ffba264b..e84975c0cbd0c 100644 --- a/vendor/github.com/unrolled/render/doc.go +++ b/vendor/github.com/unrolled/render/doc.go @@ -6,7 +6,7 @@ "encoding/xml" "net/http" - "github.com/unrolled/render" // or "gopkg.in/unrolled/render.v1" + "github.com/unrolled/render" ) type ExampleXml struct { @@ -49,7 +49,7 @@ r.HTML(w, http.StatusOK, "example", nil) }) - http.ListenAndServe("0.0.0.0:3000", mux) + http.ListenAndServe("127.0.0.1:3000", mux) } */ package render diff --git a/vendor/github.com/unrolled/render/engine.go b/vendor/github.com/unrolled/render/engine.go index 283a3772cdd99..ec4f5885a7e7f 100644 --- a/vendor/github.com/unrolled/render/engine.go +++ b/vendor/github.com/unrolled/render/engine.go @@ -78,7 +78,7 @@ func (d Data) Render(w io.Writer, v interface{}) error { d.Head.Write(hw) } - w.Write(v.([]byte)) + _, _ = w.Write(v.([]byte)) return nil } @@ -99,7 +99,7 @@ func (h HTML) Render(w io.Writer, binding interface{}) error { if hw, ok := w.(http.ResponseWriter); ok { h.Head.Write(hw) } - buf.WriteTo(w) + _, _ = buf.WriteTo(w) return nil } @@ -125,9 +125,9 @@ func (j JSON) Render(w io.Writer, v interface{}) error { // Unescape HTML if needed. if j.UnEscapeHTML { - result = bytes.Replace(result, []byte("\\u003c"), []byte("<"), -1) - result = bytes.Replace(result, []byte("\\u003e"), []byte(">"), -1) - result = bytes.Replace(result, []byte("\\u0026"), []byte("&"), -1) + result = bytes.ReplaceAll(result, []byte("\\u003c"), []byte("<")) + result = bytes.ReplaceAll(result, []byte("\\u003e"), []byte(">")) + result = bytes.ReplaceAll(result, []byte("\\u0026"), []byte("&")) } // JSON marshaled fine, write out the result. @@ -135,9 +135,9 @@ func (j JSON) Render(w io.Writer, v interface{}) error { j.Head.Write(hw) } if len(j.Prefix) > 0 { - w.Write(j.Prefix) + _, _ = w.Write(j.Prefix) } - w.Write(result) + _, _ = w.Write(result) return nil } @@ -146,7 +146,7 @@ func (j JSON) renderStreamingJSON(w io.Writer, v interface{}) error { j.Head.Write(hw) } if len(j.Prefix) > 0 { - w.Write(j.Prefix) + _, _ = w.Write(j.Prefix) } return json.NewEncoder(w).Encode(v) @@ -170,13 +170,13 @@ func (j JSONP) Render(w io.Writer, v interface{}) error { if hw, ok := w.(http.ResponseWriter); ok { j.Head.Write(hw) } - w.Write([]byte(j.Callback + "(")) - w.Write(result) - w.Write([]byte(");")) + _, _ = w.Write([]byte(j.Callback + "(")) + _, _ = w.Write(result) + _, _ = w.Write([]byte(");")) // If indenting, append a new line. if j.Indent { - w.Write([]byte("\n")) + _, _ = w.Write([]byte("\n")) } return nil } @@ -191,7 +191,7 @@ func (t Text) Render(w io.Writer, v interface{}) error { t.Head.Write(hw) } - w.Write([]byte(v.(string))) + _, _ = w.Write([]byte(v.(string))) return nil } @@ -215,8 +215,8 @@ func (x XML) Render(w io.Writer, v interface{}) error { x.Head.Write(hw) } if len(x.Prefix) > 0 { - w.Write(x.Prefix) + _, _ = w.Write(x.Prefix) } - w.Write(result) + _, _ = w.Write(result) return nil } diff --git a/vendor/github.com/unrolled/render/fs_embed.go b/vendor/github.com/unrolled/render/fs_embed.go new file mode 100644 index 0000000000000..5dd2d2ba02125 --- /dev/null +++ b/vendor/github.com/unrolled/render/fs_embed.go @@ -0,0 +1,31 @@ +// +build go1.16 + +package render + +import ( + "embed" + "io/fs" + "path/filepath" +) + +// EmbedFileSystem implements FileSystem on top of an embed.FS +type EmbedFileSystem struct { + embed.FS +} + +var _ FileSystem = &EmbedFileSystem{} + +func (e *EmbedFileSystem) Walk(root string, walkFn filepath.WalkFunc) error { + return fs.WalkDir(e.FS, root, func(path string, d fs.DirEntry, _ error) error { + if d == nil { + return nil + } + + info, err := d.Info() + if err != nil { + return err + } + + return walkFn(path, info, err) + }) +} diff --git a/vendor/github.com/unrolled/render/go.mod b/vendor/github.com/unrolled/render/go.mod index 22d793cbcfb7d..628e54e5f0117 100644 --- a/vendor/github.com/unrolled/render/go.mod +++ b/vendor/github.com/unrolled/render/go.mod @@ -1,5 +1,8 @@ module github.com/unrolled/render -go 1.12 +go 1.16 -require github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 +require ( + github.com/fsnotify/fsnotify v1.4.9 + golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea // indirect +) diff --git a/vendor/github.com/unrolled/render/go.sum b/vendor/github.com/unrolled/render/go.sum index 2199959075def..4ed2330005ec3 100644 --- a/vendor/github.com/unrolled/render/go.sum +++ b/vendor/github.com/unrolled/render/go.sum @@ -1,2 +1,5 @@ -github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o= -github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea h1:+WiDlPBBaO+h9vPNZi8uJ3k4BkKQB7Iow3aqwHVA5hI= +golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/vendor/github.com/unrolled/render/helpers.go b/vendor/github.com/unrolled/render/helpers.go index 699508a4910d0..50b234a5a116f 100644 --- a/vendor/github.com/unrolled/render/helpers.go +++ b/vendor/github.com/unrolled/render/helpers.go @@ -1,5 +1,3 @@ -// +build go1.6 - package render import ( diff --git a/vendor/github.com/unrolled/render/helpers_pre16.go b/vendor/github.com/unrolled/render/helpers_pre16.go deleted file mode 100644 index 999d9af497a2e..0000000000000 --- a/vendor/github.com/unrolled/render/helpers_pre16.go +++ /dev/null @@ -1,26 +0,0 @@ -// +build !go1.6 - -package render - -import ( - "fmt" - "html/template" -) - -// Included helper functions for use when rendering HTML. -var helperFuncs = template.FuncMap{ - "yield": func() (string, error) { - return "", fmt.Errorf("yield called with no layout defined") - }, - // `block` is deprecated! Use the `partial` below if you need this functionality still. - // Otherwise, checkout Go's `block` implementation introduced in 1.6 - "block": func() (string, error) { - return "", fmt.Errorf("block called with no layout defined") - }, - "partial": func() (string, error) { - return "", fmt.Errorf("block called with no layout defined") - }, - "current": func() (string, error) { - return "", nil - }, -} diff --git a/vendor/github.com/unrolled/render/lock.go b/vendor/github.com/unrolled/render/lock.go new file mode 100644 index 0000000000000..44f7beb276cc2 --- /dev/null +++ b/vendor/github.com/unrolled/render/lock.go @@ -0,0 +1,25 @@ +package render + +import "sync" + +// rwLock represents an interface for sync.RWMutex. +type rwLock interface { + Lock() + Unlock() + RLock() + RUnlock() +} + +var ( + // Ensure our interface is correct. + _ rwLock = &sync.RWMutex{} + _ rwLock = emptyLock{} +) + +// emptyLock is a noop RWLock implementation. +type emptyLock struct{} + +func (emptyLock) Lock() {} +func (emptyLock) Unlock() {} +func (emptyLock) RLock() {} +func (emptyLock) RUnlock() {} diff --git a/vendor/github.com/unrolled/render/render.go b/vendor/github.com/unrolled/render/render.go index ad2e77ee19571..ff9593c34ea6c 100644 --- a/vendor/github.com/unrolled/render/render.go +++ b/vendor/github.com/unrolled/render/render.go @@ -11,6 +11,8 @@ import ( "path/filepath" "strings" "sync" + + "github.com/fsnotify/fsnotify" ) const ( @@ -90,6 +92,9 @@ type Options struct { XMLContentType string // If IsDevelopment is set to true, this will recompile the templates on every request. Default is false. IsDevelopment bool + // If UseMutexLock is set to true, the standard `sync.RWMutex` lock will be used instead of the lock free implementation. Default is false. + // Note that when `IsDevelopment` is true, the standard `sync.RWMutex` lock is always used. Lock free is only a production feature. + UseMutexLock bool // Unescape HTML characters "&<>" to their original values. Default is false. UnEscapeHTML bool // Streams JSON responses instead of marshalling prior to sending. Default is false. @@ -103,7 +108,6 @@ type Options struct { // Enables using partials without the current filename suffix which allows use of the same template in multiple files. e.g {{ partial "carosuel" }} inside the home template will match carosel-home or carosel. // ***NOTE*** - This option should be named RenderPartialsWithoutSuffix as that is what it does. "Prefix" is a typo. Maintaining the existing name for backwards compatibility. RenderPartialsWithoutPrefix bool - // BufferPool to use when rendering HTML templates. If none is supplied // defaults to SizedBufferPool of size 32 with 512KiB buffers. BufferPool GenericBufferPool @@ -120,11 +124,13 @@ type HTMLOptions struct { // Render is a service that provides functions for easily writing JSON, XML, // binary data, and HTML templates out to a HTTP Response. type Render struct { + lock rwLock + // Customize Secure with an Options struct. opt Options templates *template.Template - templatesLk sync.RWMutex compiledCharset string + hasWatcher bool } // New constructs a new Render instance with the supplied options. @@ -134,12 +140,10 @@ func New(options ...Options) *Render { o = options[0] } - r := Render{ - opt: o, - } + r := Render{opt: o} r.prepareOptions() - r.compileTemplates() + r.CompileTemplates() return &r } @@ -149,10 +153,9 @@ func (r *Render) prepareOptions() { if len(r.opt.Charset) == 0 { r.opt.Charset = defaultCharset } - if r.opt.DisableCharset == false { + if !r.opt.DisableCharset { r.compiledCharset = "; charset=" + r.opt.Charset } - if len(r.opt.Directory) == 0 { r.opt.Directory = "templates" } @@ -181,16 +184,21 @@ func (r *Render) prepareOptions() { r.opt.XMLContentType = ContentXML } if r.opt.BufferPool == nil { - // 32 buffers of size 512KiB each - r.opt.BufferPool = NewSizedBufferPool(32, 1<<19) + r.opt.BufferPool = NewSizedBufferPool(32, 1<<19) // 32 buffers of size 512KiB each + } + if r.opt.IsDevelopment || r.opt.UseMutexLock { + r.lock = &sync.RWMutex{} + } else { + r.lock = &emptyLock{} } } -func (r *Render) compileTemplates() { +func (r *Render) CompileTemplates() { if r.opt.Asset == nil || r.opt.AssetNames == nil { r.compileTemplatesFromDir() return } + r.compileTemplatesFromAsset() } @@ -199,12 +207,24 @@ func (r *Render) compileTemplatesFromDir() { tmpTemplates := template.New(dir) tmpTemplates.Delims(r.opt.Delims.Left, r.opt.Delims.Right) + var watcher *fsnotify.Watcher + if r.opt.IsDevelopment { + var err error + watcher, err = fsnotify.NewWatcher() + if err != nil { + log.Printf("Unable to create new watcher for template files. Templates will be recompiled on every render. Error: %v\n", err) + } + } + // Walk the supplied directory and compile any files that match our extension list. - r.opt.FileSystem.Walk(dir, func(path string, info os.FileInfo, err error) error { + _ = r.opt.FileSystem.Walk(dir, func(path string, info os.FileInfo, _ error) error { // Fix same-extension-dirs bug: some dir might be named to: "users.tmpl", "local.html". // These dirs should be excluded as they are not valid golang templates, but files under // them should be treat as normal. // If is a dir, return immediately (dir is not a valid golang template). + if info != nil && watcher != nil { + _ = watcher.Add(path) + } if info == nil || info.IsDir() { return nil } @@ -215,7 +235,7 @@ func (r *Render) compileTemplatesFromDir() { } ext := "" - if strings.Index(rel, ".") != -1 { + if strings.Contains(rel, ".") { ext = filepath.Ext(rel) } @@ -242,9 +262,25 @@ func (r *Render) compileTemplatesFromDir() { return nil }) - r.templatesLk.Lock() + r.lock.Lock() + defer r.lock.Unlock() r.templates = tmpTemplates - r.templatesLk.Unlock() + if r.hasWatcher = watcher != nil; r.hasWatcher { + go func() { + select { + case _, ok := <-watcher.Events: + if !ok { + return + } + case _, ok := <-watcher.Errors: + if !ok { + return + } + } + watcher.Close() + r.CompileTemplates() + }() + } } func (r *Render) compileTemplatesFromAsset() { @@ -263,13 +299,12 @@ func (r *Render) compileTemplatesFromAsset() { } ext := "" - if strings.Index(rel, ".") != -1 { + if strings.Contains(rel, ".") { ext = "." + strings.Join(strings.Split(rel, ".")[1:], ".") } for _, extension := range r.opt.Extensions { if ext == extension { - buf, err := r.opt.Asset(path) if err != nil { panic(err) @@ -289,28 +324,29 @@ func (r *Render) compileTemplatesFromAsset() { } } } - - r.templatesLk.Lock() + r.lock.Lock() + defer r.lock.Unlock() r.templates = tmpTemplates - r.templatesLk.Unlock() } // TemplateLookup is a wrapper around template.Lookup and returns // the template with the given name that is associated with t, or nil // if there is no such template. func (r *Render) TemplateLookup(t string) *template.Template { + r.lock.RLock() + defer r.lock.RUnlock() return r.templates.Lookup(t) } -func (r *Render) execute(name string, binding interface{}) (*bytes.Buffer, error) { +func (r *Render) execute(templates *template.Template, name string, binding interface{}) (*bytes.Buffer, error) { buf := new(bytes.Buffer) - return buf, r.templates.ExecuteTemplate(buf, name, binding) + return buf, templates.ExecuteTemplate(buf, name, binding) } -func (r *Render) layoutFuncs(name string, binding interface{}) template.FuncMap { +func (r *Render) layoutFuncs(templates *template.Template, name string, binding interface{}) template.FuncMap { return template.FuncMap{ "yield": func() (template.HTML, error) { - buf, err := r.execute(name, binding) + buf, err := r.execute(templates, name, binding) // Return safe HTML here since we are rendering our own template. return template.HTML(buf.String()), err }, @@ -318,13 +354,13 @@ func (r *Render) layoutFuncs(name string, binding interface{}) template.FuncMap return name, nil }, "block": func(partialName string) (template.HTML, error) { - log.Print("Render's `block` implementation is now depericated. Use `partial` as a drop in replacement.") + log.Println("Render's `block` implementation is now depericated. Use `partial` as a drop in replacement.") fullPartialName := fmt.Sprintf("%s-%s", partialName, name) - if r.TemplateLookup(fullPartialName) == nil && r.opt.RenderPartialsWithoutPrefix { + if templates.Lookup(fullPartialName) == nil && r.opt.RenderPartialsWithoutPrefix { fullPartialName = partialName } - if r.opt.RequireBlocks || r.TemplateLookup(fullPartialName) != nil { - buf, err := r.execute(fullPartialName, binding) + if r.opt.RequireBlocks || templates.Lookup(fullPartialName) != nil { + buf, err := r.execute(templates, fullPartialName, binding) // Return safe HTML here since we are rendering our own template. return template.HTML(buf.String()), err } @@ -332,11 +368,11 @@ func (r *Render) layoutFuncs(name string, binding interface{}) template.FuncMap }, "partial": func(partialName string) (template.HTML, error) { fullPartialName := fmt.Sprintf("%s-%s", partialName, name) - if r.TemplateLookup(fullPartialName) == nil && r.opt.RenderPartialsWithoutPrefix { + if templates.Lookup(fullPartialName) == nil && r.opt.RenderPartialsWithoutPrefix { fullPartialName = partialName } - if r.opt.RequirePartials || r.TemplateLookup(fullPartialName) != nil { - buf, err := r.execute(fullPartialName, binding) + if r.opt.RequirePartials || templates.Lookup(fullPartialName) != nil { + buf, err := r.execute(templates, fullPartialName, binding) // Return safe HTML here since we are rendering our own template. return template.HTML(buf.String()), err } @@ -397,19 +433,20 @@ func (r *Render) Data(w io.Writer, status int, v []byte) error { // HTML builds up the response from the specified template and bindings. func (r *Render) HTML(w io.Writer, status int, name string, binding interface{}, htmlOpt ...HTMLOptions) error { - // If we are in development mode, recompile the templates on every HTML request. - if r.opt.IsDevelopment { - r.compileTemplates() + r.lock.RLock() // rlock here because we're reading the hasWatcher + if r.opt.IsDevelopment && !r.hasWatcher { + r.lock.RUnlock() // runlock here because CompileTemplates will lock + r.CompileTemplates() + r.lock.RLock() } - - r.templatesLk.RLock() - defer r.templatesLk.RUnlock() + templates := r.templates + r.lock.RUnlock() opt := r.prepareHTMLOptions(htmlOpt) - if tpl := r.templates.Lookup(name); tpl != nil { + if tpl := templates.Lookup(name); tpl != nil { if len(opt.Layout) > 0 { - tpl.Funcs(r.layoutFuncs(name, binding)) + tpl.Funcs(r.layoutFuncs(templates, name, binding)) name = opt.Layout } @@ -426,7 +463,7 @@ func (r *Render) HTML(w io.Writer, status int, name string, binding interface{}, h := HTML{ Head: head, Name: name, - Templates: r.templates, + Templates: templates, bp: r.opt.BufferPool, } diff --git a/vendor/github.com/unrolled/render/testdata/basic/admin/index.tmpl b/vendor/github.com/unrolled/render/testdata/basic/admin/index.tmpl new file mode 100644 index 0000000000000..047ff3411e25a --- /dev/null +++ b/vendor/github.com/unrolled/render/testdata/basic/admin/index.tmpl @@ -0,0 +1 @@ +

Admin {{.}}

diff --git a/vendor/github.com/unrolled/render/testdata/basic/another_layout.tmpl b/vendor/github.com/unrolled/render/testdata/basic/another_layout.tmpl new file mode 100644 index 0000000000000..3d71f5fb20145 --- /dev/null +++ b/vendor/github.com/unrolled/render/testdata/basic/another_layout.tmpl @@ -0,0 +1,3 @@ +another head +{{ yield }} +another foot diff --git a/vendor/github.com/unrolled/render/testdata/basic/content.tmpl b/vendor/github.com/unrolled/render/testdata/basic/content.tmpl new file mode 100644 index 0000000000000..f8fa1b39d056f --- /dev/null +++ b/vendor/github.com/unrolled/render/testdata/basic/content.tmpl @@ -0,0 +1 @@ +

{{ . }}

diff --git a/vendor/github.com/unrolled/render/testdata/basic/current_layout.tmpl b/vendor/github.com/unrolled/render/testdata/basic/current_layout.tmpl new file mode 100644 index 0000000000000..877524e62dcfc --- /dev/null +++ b/vendor/github.com/unrolled/render/testdata/basic/current_layout.tmpl @@ -0,0 +1,3 @@ +{{ current }} head +{{ yield }} +{{ current }} foot diff --git a/vendor/github.com/unrolled/render/testdata/basic/delims.tmpl b/vendor/github.com/unrolled/render/testdata/basic/delims.tmpl new file mode 100644 index 0000000000000..c6fc9c05c0739 --- /dev/null +++ b/vendor/github.com/unrolled/render/testdata/basic/delims.tmpl @@ -0,0 +1 @@ +

Hello {[{.}]}

\ No newline at end of file diff --git a/vendor/github.com/unrolled/render/testdata/basic/hello.tmpl b/vendor/github.com/unrolled/render/testdata/basic/hello.tmpl new file mode 100644 index 0000000000000..e4a4cea39615b --- /dev/null +++ b/vendor/github.com/unrolled/render/testdata/basic/hello.tmpl @@ -0,0 +1 @@ +

Hello {{.}}

diff --git a/vendor/github.com/unrolled/render/testdata/basic/hypertext.html b/vendor/github.com/unrolled/render/testdata/basic/hypertext.html new file mode 100644 index 0000000000000..8521165186e4c --- /dev/null +++ b/vendor/github.com/unrolled/render/testdata/basic/hypertext.html @@ -0,0 +1 @@ +Hypertext! diff --git a/vendor/github.com/unrolled/render/testdata/basic/layout.tmpl b/vendor/github.com/unrolled/render/testdata/basic/layout.tmpl new file mode 100644 index 0000000000000..b131425097815 --- /dev/null +++ b/vendor/github.com/unrolled/render/testdata/basic/layout.tmpl @@ -0,0 +1,3 @@ +head +{{ yield }} +foot diff --git a/vendor/github.com/unrolled/render/testdata/blocks/content-partial.tmpl b/vendor/github.com/unrolled/render/testdata/blocks/content-partial.tmpl new file mode 100644 index 0000000000000..a4f97af1892cc --- /dev/null +++ b/vendor/github.com/unrolled/render/testdata/blocks/content-partial.tmpl @@ -0,0 +1 @@ +{{define "after-content-partial"}}after {{ . }}{{end}} diff --git a/vendor/github.com/unrolled/render/testdata/blocks/content.tmpl b/vendor/github.com/unrolled/render/testdata/blocks/content.tmpl new file mode 100644 index 0000000000000..3fac1bb84af26 --- /dev/null +++ b/vendor/github.com/unrolled/render/testdata/blocks/content.tmpl @@ -0,0 +1,2 @@ +{{define "before-content"}}before {{ . }}{{end}} +{{define "after-content"}}after {{ . }}{{end}} diff --git a/vendor/github.com/unrolled/render/testdata/blocks/layout.tmpl b/vendor/github.com/unrolled/render/testdata/blocks/layout.tmpl new file mode 100644 index 0000000000000..0ebbe6a6d9826 --- /dev/null +++ b/vendor/github.com/unrolled/render/testdata/blocks/layout.tmpl @@ -0,0 +1,3 @@ +{{ block "before" }} +

during

+{{ block "after" }} diff --git a/vendor/github.com/unrolled/render/testdata/custom_funcs/index.tmpl b/vendor/github.com/unrolled/render/testdata/custom_funcs/index.tmpl new file mode 100644 index 0000000000000..7b553fc5a5bcf --- /dev/null +++ b/vendor/github.com/unrolled/render/testdata/custom_funcs/index.tmpl @@ -0,0 +1 @@ +{{ myCustomFunc }} diff --git a/vendor/github.com/unrolled/render/testdata/partials/content-partial.tmpl b/vendor/github.com/unrolled/render/testdata/partials/content-partial.tmpl new file mode 100644 index 0000000000000..a4f97af1892cc --- /dev/null +++ b/vendor/github.com/unrolled/render/testdata/partials/content-partial.tmpl @@ -0,0 +1 @@ +{{define "after-content-partial"}}after {{ . }}{{end}} diff --git a/vendor/github.com/unrolled/render/testdata/partials/content.tmpl b/vendor/github.com/unrolled/render/testdata/partials/content.tmpl new file mode 100644 index 0000000000000..3fac1bb84af26 --- /dev/null +++ b/vendor/github.com/unrolled/render/testdata/partials/content.tmpl @@ -0,0 +1,2 @@ +{{define "before-content"}}before {{ . }}{{end}} +{{define "after-content"}}after {{ . }}{{end}} diff --git a/vendor/github.com/unrolled/render/testdata/partials/layout.tmpl b/vendor/github.com/unrolled/render/testdata/partials/layout.tmpl new file mode 100644 index 0000000000000..069f9710c2f10 --- /dev/null +++ b/vendor/github.com/unrolled/render/testdata/partials/layout.tmpl @@ -0,0 +1,3 @@ +{{ partial "before" }} +

during

+{{ partial "after" }} diff --git a/vendor/github.com/unrolled/render/testdata/template-dir-test/0.tmpl b/vendor/github.com/unrolled/render/testdata/template-dir-test/0.tmpl new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/vendor/github.com/unrolled/render/testdata/template-dir-test/dedicated.tmpl/notbad.tmpl b/vendor/github.com/unrolled/render/testdata/template-dir-test/dedicated.tmpl/notbad.tmpl new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/vendor/github.com/unrolled/render/testdata/template-dir-test/subdir/1.tmpl b/vendor/github.com/unrolled/render/testdata/template-dir-test/subdir/1.tmpl new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/vendor/github.com/willf/bitset/go.mod b/vendor/github.com/willf/bitset/go.mod deleted file mode 100644 index 583ecab78f745..0000000000000 --- a/vendor/github.com/willf/bitset/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/willf/bitset - -go 1.14 diff --git a/vendor/github.com/xanzy/go-gitlab/client_options.go b/vendor/github.com/xanzy/go-gitlab/client_options.go index b09ae078173bf..9f740460a0ec8 100644 --- a/vendor/github.com/xanzy/go-gitlab/client_options.go +++ b/vendor/github.com/xanzy/go-gitlab/client_options.go @@ -40,6 +40,14 @@ func WithCustomBackoff(backoff retryablehttp.Backoff) ClientOptionFunc { } } +// WithCustomLogger can be used to configure a custom retryablehttp leveled logger +func WithCustomLeveledLogger(leveledLogger retryablehttp.LeveledLogger) ClientOptionFunc { + return func(c *Client) error { + c.client.Logger = leveledLogger + return nil + } +} + // WithCustomLimiter injects a custom rate limiter to the client. func WithCustomLimiter(limiter RateLimiter) ClientOptionFunc { return func(c *Client) error { @@ -50,6 +58,14 @@ func WithCustomLimiter(limiter RateLimiter) ClientOptionFunc { } } +// WithCustomLogger can be used to configure a custom retryablehttp logger +func WithCustomLogger(logger retryablehttp.Logger) ClientOptionFunc { + return func(c *Client) error { + c.client.Logger = logger + return nil + } +} + // WithCustomRetry can be used to configure a custom retry policy. func WithCustomRetry(checkRetry retryablehttp.CheckRetry) ClientOptionFunc { return func(c *Client) error { diff --git a/vendor/github.com/xanzy/go-gitlab/commits.go b/vendor/github.com/xanzy/go-gitlab/commits.go index 504d5c5d32b78..acc0998705d70 100644 --- a/vendor/github.com/xanzy/go-gitlab/commits.go +++ b/vendor/github.com/xanzy/go-gitlab/commits.go @@ -185,7 +185,7 @@ type CreateCommitOptions struct { StartBranch *string `url:"start_branch,omitempty" json:"start_branch,omitempty"` StartSHA *string `url:"start_sha,omitempty" json:"start_sha,omitempty"` StartProject *string `url:"start_project,omitempty" json:"start_project,omitempty"` - Actions []*CommitActionOptions `url:"actions,omitempty" json:"actions,omitempty"` + Actions []*CommitActionOptions `url:"actions" json:"actions"` AuthorEmail *string `url:"author_email,omitempty" json:"author_email,omitempty"` AuthorName *string `url:"author_name,omitempty" json:"author_name,omitempty"` Stats *bool `url:"stats,omitempty" json:"stats,omitempty"` diff --git a/vendor/github.com/xanzy/go-gitlab/gitlab.go b/vendor/github.com/xanzy/go-gitlab/gitlab.go index 265aaf200fbb7..feed61088f015 100644 --- a/vendor/github.com/xanzy/go-gitlab/gitlab.go +++ b/vendor/github.com/xanzy/go-gitlab/gitlab.go @@ -122,6 +122,7 @@ type Client struct { GitIgnoreTemplates *GitIgnoreTemplatesService GroupBadges *GroupBadgesService GroupCluster *GroupClustersService + GroupImportExport *GroupImportExportService GroupIssueBoards *GroupIssueBoardsService GroupLabels *GroupLabelsService GroupMembers *GroupMembersService @@ -140,6 +141,7 @@ type Client struct { Labels *LabelsService License *LicenseService LicenseTemplates *LicenseTemplatesService + ManagedLicenses *ManagedLicensesService MergeRequestApprovals *MergeRequestApprovalsService MergeRequests *MergeRequestsService Milestones *MilestonesService @@ -293,6 +295,7 @@ func newClient(options ...ClientOptionFunc) (*Client, error) { c.GitIgnoreTemplates = &GitIgnoreTemplatesService{client: c} c.GroupBadges = &GroupBadgesService{client: c} c.GroupCluster = &GroupClustersService{client: c} + c.GroupImportExport = &GroupImportExportService{client: c} c.GroupIssueBoards = &GroupIssueBoardsService{client: c} c.GroupLabels = &GroupLabelsService{client: c} c.GroupMembers = &GroupMembersService{client: c} @@ -311,6 +314,7 @@ func newClient(options ...ClientOptionFunc) (*Client, error) { c.Labels = &LabelsService{client: c} c.License = &LicenseService{client: c} c.LicenseTemplates = &LicenseTemplatesService{client: c} + c.ManagedLicenses = &ManagedLicensesService{client: c} c.MergeRequestApprovals = &MergeRequestApprovalsService{client: c} c.MergeRequests = &MergeRequestsService{client: c, timeStats: timeStats} c.Milestones = &MilestonesService{client: c} diff --git a/vendor/github.com/xanzy/go-gitlab/go.mod b/vendor/github.com/xanzy/go-gitlab/go.mod index eb13bcef72406..e363c27377bd1 100644 --- a/vendor/github.com/xanzy/go-gitlab/go.mod +++ b/vendor/github.com/xanzy/go-gitlab/go.mod @@ -5,9 +5,9 @@ require ( github.com/hashicorp/go-cleanhttp v0.5.1 github.com/hashicorp/go-retryablehttp v0.6.8 github.com/stretchr/testify v1.4.0 - golang.org/x/net v0.0.0-20181108082009-03003ca0c849 // indirect + golang.org/x/net v0.0.0-20201021035429-f5854403a974 // indirect golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288 - golang.org/x/sync v0.0.0-20181108010431-42b317875d0f // indirect + golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 // indirect golang.org/x/time v0.0.0-20191024005414-555d28b269f0 google.golang.org/appengine v1.3.0 // indirect ) diff --git a/vendor/github.com/xanzy/go-gitlab/go.sum b/vendor/github.com/xanzy/go-gitlab/go.sum index 74ea228e9185d..671236d704273 100644 --- a/vendor/github.com/xanzy/go-gitlab/go.sum +++ b/vendor/github.com/xanzy/go-gitlab/go.sum @@ -17,16 +17,24 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181108082009-03003ca0c849 h1:FSqE2GGG7wzsYUsWiQ8MZrvEd1EOyU3NCF0AW3Wtltg= -golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288 h1:JIqe8uIcRBHXDQVvZtHwp80ai3Lw3IJAeJEs55Dc1W0= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= google.golang.org/appengine v1.3.0 h1:FBSsiFRMz3LBeXIomRnVzrQwSDj4ibvcRexLG0LZGQk= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= diff --git a/vendor/github.com/xanzy/go-gitlab/group_import_export.go b/vendor/github.com/xanzy/go-gitlab/group_import_export.go new file mode 100644 index 0000000000000..c57880583345d --- /dev/null +++ b/vendor/github.com/xanzy/go-gitlab/group_import_export.go @@ -0,0 +1,180 @@ +// +// Copyright 2021, Sander van Harmelen +// +// 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 gitlab + +import ( + "bytes" + "fmt" + "io" + "mime/multipart" + "net/http" + "os" + "path/filepath" + "strconv" +) + +// GroupImportExportService handles communication with the group import export +// related methods of the GitLab API. +// +// GitLab API docs: https://docs.gitlab.com/ce/api/group_import_export.html +type GroupImportExportService struct { + client *Client +} + +// ScheduleExport starts a new group export. +// +// GitLab API docs: +// https://docs.gitlab.com/ce/api/group_import_export.html#schedule-new-export +func (s *GroupImportExportService) ScheduleExport(gid interface{}, options ...RequestOptionFunc) (*Response, error) { + group, err := parseID(gid) + if err != nil { + return nil, err + } + u := fmt.Sprintf("groups/%s/export", pathEscape(group)) + + req, err := s.client.NewRequest(http.MethodPost, u, nil, options) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// ExportDownload downloads the finished export. +// +// GitLab API docs: +// https://docs.gitlab.com/ce/api/group_import_export.html#export-download +func (s *GroupImportExportService) ExportDownload(gid interface{}, options ...RequestOptionFunc) (*bytes.Reader, *Response, error) { + group, err := parseID(gid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf("groups/%s/export/download", pathEscape(group)) + + req, err := s.client.NewRequest(http.MethodGet, u, nil, options) + if err != nil { + return nil, nil, err + } + + exportDownload := new(bytes.Buffer) + resp, err := s.client.Do(req, exportDownload) + if err != nil { + return nil, resp, err + } + + return bytes.NewReader(exportDownload.Bytes()), resp, err +} + +// GroupImportFileOptions represents the available ImportFile() options. +// +// GitLab API docs: +// https://docs.gitlab.com/ce/api/group_import_export.html#import-a-file +type GroupImportFileOptions struct { + Name *string `url:"name,omitempty" json:"name,omitempty"` + Path *string `url:"path,omitempty" json:"path,omitempty"` + File *string `url:"file,omitempty" json:"file,omitempty"` + ParentID *int `url:"parent_id,omitempty" json:"parent_id,omitempty"` +} + +// ImportFile imports a file. +// +// GitLab API docs: +// https://docs.gitlab.com/ce/api/group_import_export.html#import-a-file +func (s *GroupImportExportService) ImportFile(opt *GroupImportFileOptions, options ...RequestOptionFunc) (*Response, error) { + // First check if we got all required options. + if opt.Name == nil || *opt.Name == "" { + return nil, fmt.Errorf("Missing required option: Name") + } + if opt.Path == nil || *opt.Path == "" { + return nil, fmt.Errorf("Missing required option: Path") + } + if opt.File == nil || *opt.File == "" { + return nil, fmt.Errorf("Missing required option: File") + } + + f, err := os.Open(*opt.File) + if err != nil { + return nil, err + } + defer f.Close() + + b := &bytes.Buffer{} + w := multipart.NewWriter(b) + + _, filename := filepath.Split(*opt.File) + fw, err := w.CreateFormFile("file", filename) + if err != nil { + return nil, err + } + + _, err = io.Copy(fw, f) + if err != nil { + return nil, err + } + + // Populate the additional fields. + fw, err = w.CreateFormField("name") + if err != nil { + return nil, err + } + + _, err = fw.Write([]byte(*opt.Name)) + if err != nil { + return nil, err + } + + fw, err = w.CreateFormField("path") + if err != nil { + return nil, err + } + + _, err = fw.Write([]byte(*opt.Path)) + if err != nil { + return nil, err + } + + if opt.ParentID != nil { + fw, err = w.CreateFormField("parent_id") + if err != nil { + return nil, err + } + + _, err = fw.Write([]byte(strconv.Itoa(*opt.ParentID))) + if err != nil { + return nil, err + } + } + + if err = w.Close(); err != nil { + return nil, err + } + + req, err := s.client.NewRequest(http.MethodPost, "groups/import", nil, options) + if err != nil { + return nil, err + } + + // Set the buffer as the request body. + if err = req.SetBody(b); err != nil { + return nil, err + } + + // Overwrite the default content type. + req.Header.Set("Content-Type", w.FormDataContentType()) + + return s.client.Do(req, nil) +} diff --git a/vendor/github.com/xanzy/go-gitlab/group_variables.go b/vendor/github.com/xanzy/go-gitlab/group_variables.go index bcc7b4928be2c..4cf9714813e8b 100644 --- a/vendor/github.com/xanzy/go-gitlab/group_variables.go +++ b/vendor/github.com/xanzy/go-gitlab/group_variables.go @@ -36,11 +36,12 @@ type GroupVariablesService struct { // GitLab API docs: // https://docs.gitlab.com/ee/api/group_level_variables.html type GroupVariable struct { - Key string `json:"key"` - Value string `json:"value"` - VariableType VariableTypeValue `json:"variable_type"` - Protected bool `json:"protected"` - Masked bool `json:"masked"` + Key string `json:"key"` + Value string `json:"value"` + VariableType VariableTypeValue `json:"variable_type"` + Protected bool `json:"protected"` + Masked bool `json:"masked"` + EnvironmentScope string `json:"environment_scope"` } func (v GroupVariable) String() string { @@ -110,11 +111,12 @@ func (s *GroupVariablesService) GetVariable(gid interface{}, key string, options // GitLab API docs: // https://docs.gitlab.com/ee/api/group_level_variables.html#create-variable type CreateGroupVariableOptions struct { - Key *string `url:"key,omitempty" json:"key,omitempty"` - Value *string `url:"value,omitempty" json:"value,omitempty"` - VariableType *VariableTypeValue `url:"variable_type,omitempty" json:"variable_type,omitempty"` - Protected *bool `url:"protected,omitempty" json:"protected,omitempty"` - Masked *bool `url:"masked,omitempty" json:"masked,omitempty"` + Key *string `url:"key,omitempty" json:"key,omitempty"` + Value *string `url:"value,omitempty" json:"value,omitempty"` + VariableType *VariableTypeValue `url:"variable_type,omitempty" json:"variable_type,omitempty"` + Protected *bool `url:"protected,omitempty" json:"protected,omitempty"` + Masked *bool `url:"masked,omitempty" json:"masked,omitempty"` + EnvironmentScope *string `url:"environment_scope,omitempty" json:"environment_scope,omitempty"` } // CreateVariable creates a new group variable. @@ -148,10 +150,11 @@ func (s *GroupVariablesService) CreateVariable(gid interface{}, opt *CreateGroup // GitLab API docs: // https://docs.gitlab.com/ee/api/group_level_variables.html#update-variable type UpdateGroupVariableOptions struct { - Value *string `url:"value,omitempty" json:"value,omitempty"` - VariableType *VariableTypeValue `url:"variable_type,omitempty" json:"variable_type,omitempty"` - Protected *bool `url:"protected,omitempty" json:"protected,omitempty"` - Masked *bool `url:"masked,omitempty" json:"masked,omitempty"` + Value *string `url:"value,omitempty" json:"value,omitempty"` + VariableType *VariableTypeValue `url:"variable_type,omitempty" json:"variable_type,omitempty"` + Protected *bool `url:"protected,omitempty" json:"protected,omitempty"` + Masked *bool `url:"masked,omitempty" json:"masked,omitempty"` + EnvironmentScope *string `url:"environment_scope,omitempty" json:"environment_scope,omitempty"` } // UpdateVariable updates the position of an existing diff --git a/vendor/github.com/xanzy/go-gitlab/issues.go b/vendor/github.com/xanzy/go-gitlab/issues.go index e01544a4fb1d6..397e8c20bf914 100644 --- a/vendor/github.com/xanzy/go-gitlab/issues.go +++ b/vendor/github.com/xanzy/go-gitlab/issues.go @@ -580,6 +580,33 @@ func (s *IssuesService) UnsubscribeFromIssue(pid interface{}, issue int, options return i, resp, err } +// CreateTodo creates a todo for the current user for an issue. +// If there already exists a todo for the user on that issue, status code +// 304 is returned. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/issues.html#create-a-to-do-item +func (s *IssuesService) CreateTodo(pid interface{}, issue int, options ...RequestOptionFunc) (*Todo, *Response, error) { + project, err := parseID(pid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf("projects/%s/issues/%d/todo", pathEscape(project), issue) + + req, err := s.client.NewRequest(http.MethodPost, u, nil, options) + if err != nil { + return nil, nil, err + } + + t := new(Todo) + resp, err := s.client.Do(req, t) + if err != nil { + return nil, resp, err + } + + return t, resp, err +} + // ListMergeRequestsClosingIssueOptions represents the available // ListMergeRequestsClosingIssue() options. // diff --git a/vendor/github.com/xanzy/go-gitlab/jobs.go b/vendor/github.com/xanzy/go-gitlab/jobs.go index 410e5a6f10c8f..2d3c110fa4a65 100644 --- a/vendor/github.com/xanzy/go-gitlab/jobs.go +++ b/vendor/github.com/xanzy/go-gitlab/jobs.go @@ -103,7 +103,8 @@ type Bridge struct { // ListJobsOptions are options for two list apis type ListJobsOptions struct { ListOptions - Scope []BuildStateValue `url:"scope[],omitempty" json:"scope,omitempty"` + Scope []BuildStateValue `url:"scope[],omitempty" json:"scope,omitempty"` + IncludeRetried bool `url:"include_retried,omitempty" json:"include_retried,omitempty"` } // ListProjectJobs gets a list of jobs in a project. @@ -186,6 +187,31 @@ func (s *JobsService) ListPipelineBridges(pid interface{}, pipelineID int, opts return bridges, resp, err } +// GetJobTokensJobOptions represents the available GetJobTokensJob() options. +// +// GitLab API docs: https://docs.gitlab.com/ce/api/jobs.html#get-job-tokens-job +type GetJobTokensJobOptions struct { + JobToken *string `url:"job_token,omitempty" json:"job_token,omitempty"` +} + +// GetJobTokensJob retrieves the job that generated a job token. +// +// GitLab API docs: https://docs.gitlab.com/ce/api/jobs.html#get-job-tokens-job +func (s *JobsService) GetJobTokensJob(opts *GetJobTokensJobOptions, options ...RequestOptionFunc) (*Job, *Response, error) { + req, err := s.client.NewRequest(http.MethodGet, "job", opts, options) + if err != nil { + return nil, nil, err + } + + job := new(Job) + resp, err := s.client.Do(req, job) + if err != nil { + return nil, resp, err + } + + return job, resp, err +} + // GetJob gets a single job of a project. // // GitLab API docs: diff --git a/vendor/github.com/xanzy/go-gitlab/merge_requests.go b/vendor/github.com/xanzy/go-gitlab/merge_requests.go index 9b9b2361b39a4..3ec2a7e717f09 100644 --- a/vendor/github.com/xanzy/go-gitlab/merge_requests.go +++ b/vendor/github.com/xanzy/go-gitlab/merge_requests.go @@ -100,6 +100,7 @@ type MergeRequest struct { RebaseInProgress bool `json:"rebase_in_progress"` ApprovalsBeforeMerge int `json:"approvals_before_merge"` Reference string `json:"reference"` + FirstContribution bool `json:"first_contribution"` TaskCompletionStatus struct { Count int `json:"count"` CompletedCount int `json:"completed_count"` diff --git a/vendor/github.com/xanzy/go-gitlab/project_access_tokens.go b/vendor/github.com/xanzy/go-gitlab/project_access_tokens.go index 9bca815d54b1c..ea545870d8050 100644 --- a/vendor/github.com/xanzy/go-gitlab/project_access_tokens.go +++ b/vendor/github.com/xanzy/go-gitlab/project_access_tokens.go @@ -118,7 +118,7 @@ func (s *ProjectAccessTokensService) CreateProjectAccessToken(pid interface{}, o return pat, resp, err } -// RemoveProjectAccessToken deletes a Project Access Token. +// DeleteProjectAccessToken deletes a Project Access Token. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_access_tokens.html#revoke-a-project-access-token diff --git a/vendor/github.com/xanzy/go-gitlab/project_managed_licenses.go b/vendor/github.com/xanzy/go-gitlab/project_managed_licenses.go new file mode 100644 index 0000000000000..186bdef57ed1a --- /dev/null +++ b/vendor/github.com/xanzy/go-gitlab/project_managed_licenses.go @@ -0,0 +1,188 @@ +// +// Copyright 2021, Andrea Perizzato +// +// 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 gitlab + +import ( + "fmt" + "net/http" +) + +// ManagedLicensesService handles communication with the managed licenses +// methods of the GitLab API. +// +// GitLab API docs: https://docs.gitlab.com/ee/api/managed_licenses.html +type ManagedLicensesService struct { + client *Client +} + +// ManagedLicense represents a managed license. +// +// GitLab API docs: https://docs.gitlab.com/ee/api/managed_licenses.html +type ManagedLicense struct { + ID int `json:"id"` + Name string `json:"name"` + ApprovalStatus LicenseApprovalStatusValue `json:"approval_status"` +} + +// ListManagedLicenses returns a list of managed licenses from a project. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/managed_licenses.html#list-managed-licenses +func (s *ManagedLicensesService) ListManagedLicenses(pid interface{}, options ...RequestOptionFunc) ([]*ManagedLicense, *Response, error) { + project, err := parseID(pid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf("projects/%s/managed_licenses", pathEscape(project)) + + req, err := s.client.NewRequest(http.MethodGet, u, nil, options) + if err != nil { + return nil, nil, err + } + + var mls []*ManagedLicense + resp, err := s.client.Do(req, &mls) + if err != nil { + return nil, resp, err + } + + return mls, resp, err +} + +// GetManagedLicense returns an existing managed license. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/managed_licenses.html#show-an-existing-managed-license +func (s *ManagedLicensesService) GetManagedLicense(pid, mlid interface{}, options ...RequestOptionFunc) (*ManagedLicense, *Response, error) { + project, err := parseID(pid) + if err != nil { + return nil, nil, err + } + license, err := parseID(mlid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf("projects/%s/managed_licenses/%s", pathEscape(project), pathEscape(license)) + + req, err := s.client.NewRequest(http.MethodGet, u, nil, options) + if err != nil { + return nil, nil, err + } + + ml := new(ManagedLicense) + resp, err := s.client.Do(req, ml) + if err != nil { + return nil, resp, err + } + + return ml, resp, err +} + +// AddManagedLicenseOptions represents the available AddManagedLicense() options. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/managed_licenses.html#create-a-new-managed-license +type AddManagedLicenseOptions struct { + Name *string `url:"name,omitempty" json:"name,omitempty"` + ApprovalStatus *LicenseApprovalStatusValue `url:"approval_status,omitempty" json:"approval_status,omitempty"` +} + +// AddManagedLicense adds a managed license to a project. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/managed_licenses.html#create-a-new-managed-license +func (s *ManagedLicensesService) AddManagedLicense(pid interface{}, opt *AddManagedLicenseOptions, options ...RequestOptionFunc) (*ManagedLicense, *Response, error) { + project, err := parseID(pid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf("projects/%s/managed_licenses", pathEscape(project)) + + req, err := s.client.NewRequest(http.MethodPost, u, opt, options) + if err != nil { + return nil, nil, err + } + + ml := new(ManagedLicense) + resp, err := s.client.Do(req, ml) + if err != nil { + return nil, resp, err + } + + return ml, resp, err +} + +// DeleteManagedLicense deletes a managed license with a given ID. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/managed_licenses.html#delete-a-managed-license +func (s *ManagedLicensesService) DeleteManagedLicense(pid, mlid interface{}, options ...RequestOptionFunc) (*Response, error) { + project, err := parseID(pid) + if err != nil { + return nil, err + } + license, err := parseID(mlid) + if err != nil { + return nil, err + } + u := fmt.Sprintf("projects/%s/managed_licenses/%s", pathEscape(project), pathEscape(license)) + + req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// EditManagedLicenceOptions represents the available EditManagedLicense() options. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/managed_licenses.html#edit-an-existing-managed-license +type EditManagedLicenceOptions struct { + ApprovalStatus *LicenseApprovalStatusValue `url:"approval_status,omitempty" json:"approval_status,omitempty"` +} + +// EditManagedLicense updates an existing managed license with a new approval +// status. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/managed_licenses.html#edit-an-existing-managed-license +func (s *ManagedLicensesService) EditManagedLicense(pid, mlid interface{}, opt *EditManagedLicenceOptions, options ...RequestOptionFunc) (*ManagedLicense, *Response, error) { + project, err := parseID(pid) + if err != nil { + return nil, nil, err + } + license, err := parseID(mlid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf("projects/%s/managed_licenses/%s", pathEscape(project), pathEscape(license)) + + req, err := s.client.NewRequest(http.MethodPatch, u, opt, options) + if err != nil { + return nil, nil, err + } + + ml := new(ManagedLicense) + resp, err := s.client.Do(req, ml) + if err != nil { + return nil, resp, err + } + + return ml, resp, err +} diff --git a/vendor/github.com/xanzy/go-gitlab/projects.go b/vendor/github.com/xanzy/go-gitlab/projects.go index fb58eefbcfb4f..c58907cee9828 100644 --- a/vendor/github.com/xanzy/go-gitlab/projects.go +++ b/vendor/github.com/xanzy/go-gitlab/projects.go @@ -75,6 +75,8 @@ type Project struct { EmptyRepo bool `json:"empty_repo"` Archived bool `json:"archived"` AvatarURL string `json:"avatar_url"` + LicenseURL string `json:"license_url"` + License *ProjectLicense `json:"license"` SharedRunnersEnabled bool `json:"shared_runners_enabled"` ForksCount int `json:"forks_count"` StarCount int `json:"star_count"` @@ -113,13 +115,15 @@ type Project struct { GroupName string `json:"group_name"` GroupAccessLevel int `json:"group_access_level"` } `json:"shared_with_groups"` - Statistics *ProjectStatistics `json:"statistics"` - Links *Links `json:"_links,omitempty"` - CIConfigPath string `json:"ci_config_path"` - CIDefaultGitDepth int `json:"ci_default_git_depth"` - CustomAttributes []*CustomAttribute `json:"custom_attributes"` - ComplianceFrameworks []string `json:"compliance_frameworks"` - BuildCoverageRegex string `json:"build_coverage_regex"` + Statistics *ProjectStatistics `json:"statistics"` + Links *Links `json:"_links,omitempty"` + CIConfigPath string `json:"ci_config_path"` + CIDefaultGitDepth int `json:"ci_default_git_depth"` + CustomAttributes []*CustomAttribute `json:"custom_attributes"` + ComplianceFrameworks []string `json:"compliance_frameworks"` + BuildCoverageRegex string `json:"build_coverage_regex"` + IssuesTemplate string `json:"issues_template"` + MergeRequestsTemplate string `json:"merge_requests_template"` } // ContainerExpirationPolicy represents the container expiration policy. @@ -133,6 +137,73 @@ type ContainerExpirationPolicy struct { NextRunAt *time.Time `json:"next_run_at"` } +// ForkParent represents the parent project when this is a fork. +type ForkParent struct { + HTTPURLToRepo string `json:"http_url_to_repo"` + ID int `json:"id"` + Name string `json:"name"` + NameWithNamespace string `json:"name_with_namespace"` + Path string `json:"path"` + PathWithNamespace string `json:"path_with_namespace"` + WebURL string `json:"web_url"` +} + +// GroupAccess represents group access. +type GroupAccess struct { + AccessLevel AccessLevelValue `json:"access_level"` + NotificationLevel NotificationLevelValue `json:"notification_level"` +} + +// Links represents a project web links for self, issues, merge_requests, +// repo_branches, labels, events, members. +type Links struct { + Self string `json:"self"` + Issues string `json:"issues"` + MergeRequests string `json:"merge_requests"` + RepoBranches string `json:"repo_branches"` + Labels string `json:"labels"` + Events string `json:"events"` + Members string `json:"members"` +} + +// Permissions represents permissions. +type Permissions struct { + ProjectAccess *ProjectAccess `json:"project_access"` + GroupAccess *GroupAccess `json:"group_access"` +} + +// ProjectAccess represents project access. +type ProjectAccess struct { + AccessLevel AccessLevelValue `json:"access_level"` + NotificationLevel NotificationLevelValue `json:"notification_level"` +} + +// ProjectLicense represent the license for a project. +type ProjectLicense struct { + Key string `json:"key"` + Name string `json:"name"` + Nickname string `json:"nickname"` + HTMLURL string `json:"html_url"` + SourceURL string `json:"source_url"` +} + +// ProjectNamespace represents a project namespace. +type ProjectNamespace struct { + ID int `json:"id"` + Name string `json:"name"` + Path string `json:"path"` + Kind string `json:"kind"` + FullPath string `json:"full_path"` + AvatarURL string `json:"avatar_url"` + WebURL string `json:"web_url"` +} + +// ProjectStatistics represents a statistics record for a project. +type ProjectStatistics struct { + StorageStatistics + CommitCount int `json:"commit_count"` +} + // Repository represents a repository. type Repository struct { Name string `json:"name"` @@ -151,17 +222,6 @@ type Repository struct { HTTPURL string `json:"http_url"` } -// ProjectNamespace represents a project namespace. -type ProjectNamespace struct { - ID int `json:"id"` - Name string `json:"name"` - Path string `json:"path"` - Kind string `json:"kind"` - FullPath string `json:"full_path"` - AvatarURL string `json:"avatar_url"` - WebURL string `json:"web_url"` -} - // StorageStatistics represents a statistics record for a group or project. type StorageStatistics struct { StorageSize int64 `json:"storage_size"` @@ -170,53 +230,6 @@ type StorageStatistics struct { JobArtifactsSize int64 `json:"job_artifacts_size"` } -// ProjectStatistics represents a statistics record for a project. -type ProjectStatistics struct { - StorageStatistics - CommitCount int `json:"commit_count"` -} - -// Permissions represents permissions. -type Permissions struct { - ProjectAccess *ProjectAccess `json:"project_access"` - GroupAccess *GroupAccess `json:"group_access"` -} - -// ProjectAccess represents project access. -type ProjectAccess struct { - AccessLevel AccessLevelValue `json:"access_level"` - NotificationLevel NotificationLevelValue `json:"notification_level"` -} - -// GroupAccess represents group access. -type GroupAccess struct { - AccessLevel AccessLevelValue `json:"access_level"` - NotificationLevel NotificationLevelValue `json:"notification_level"` -} - -// ForkParent represents the parent project when this is a fork. -type ForkParent struct { - HTTPURLToRepo string `json:"http_url_to_repo"` - ID int `json:"id"` - Name string `json:"name"` - NameWithNamespace string `json:"name_with_namespace"` - Path string `json:"path"` - PathWithNamespace string `json:"path_with_namespace"` - WebURL string `json:"web_url"` -} - -// Links represents a project web links for self, issues, merge_requests, -// repo_branches, labels, events, members. -type Links struct { - Self string `json:"self"` - Issues string `json:"issues"` - MergeRequests string `json:"merge_requests"` - RepoBranches string `json:"repo_branches"` - Labels string `json:"labels"` - Events string `json:"events"` - Members string `json:"members"` -} - func (s Project) String() string { return Stringify(s) } @@ -356,6 +369,53 @@ func (s *ProjectsService) ListProjectsUsers(pid interface{}, opt *ListProjectUse return p, resp, err } +// ProjectGroup represents a GitLab project group. +type ProjectGroup struct { + ID int `json:"id"` + Name string `json:"name"` + AvatarURL string `json:"avatar_url"` + WebURL string `json:"web_url"` + FullName string `json:"full_name"` + FullPath string `json:"full_path"` +} + +// ListProjectGroupOptions represents the available ListProjectsGroups() options. +// +// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#list-a-projects-groups +type ListProjectGroupOptions struct { + ListOptions + Search *string `url:"search,omitempty" json:"search,omitempty"` + SkipGroups []int `url:"skip_groups,omitempty" json:"skip_groups,omitempty"` + WithShared *bool `url:"with_shared,omitempty" json:"with_shared,omitempty"` + SharedMinAccessLevel *AccessLevelValue `url:"shared_min_access_level,omitempty" json:"shared_min_access_level,omitempty"` + SharedVisiableOnly *bool `url:"shared_visible_only,omitempty" json:"shared_visible_only,omitempty"` +} + +// ListProjectsGroups gets a list of groups for the given project. +// +// GitLab API docs: +// https://docs.gitlab.com/ce/api/projects.html#list-a-projects-groups +func (s *ProjectsService) ListProjectsGroups(pid interface{}, opt *ListProjectGroupOptions, options ...RequestOptionFunc) ([]*ProjectGroup, *Response, error) { + project, err := parseID(pid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf("projects/%s/groups", pathEscape(project)) + + req, err := s.client.NewRequest(http.MethodGet, u, opt, options) + if err != nil { + return nil, nil, err + } + + var p []*ProjectGroup + resp, err := s.client.Do(req, &p) + if err != nil { + return nil, resp, err + } + + return p, resp, err +} + // ProjectLanguages is a map of strings because the response is arbitrary // // Gitlab API docs: https://docs.gitlab.com/ce/api/projects.html#languages @@ -537,6 +597,8 @@ type CreateProjectOptions struct { ServiceDeskEnabled *bool `url:"service_desk_enabled,omitempty" json:"service_desk_enabled,omitempty"` AutocloseReferencedIssues *bool `url:"autoclose_referenced_issues,omitempty" json:"autoclose_referenced_issues,omitempty"` SuggestionCommitMessage *string `url:"suggestion_commit_message,omitempty" json:"suggestion_commit_message,omitempty"` + IssuesTemplate *string `url:"issues_template,omitempty" json:"issues_template,omitempty"` + MergeRequestsTemplate *string `url:"merge_requests_template,omitempty" json:"merge_requests_template,omitempty"` // Deprecated members IssuesEnabled *bool `url:"issues_enabled,omitempty" json:"issues_enabled,omitempty"` @@ -675,6 +737,8 @@ type EditProjectOptions struct { ServiceDeskEnabled *bool `url:"service_desk_enabled,omitempty" json:"service_desk_enabled,omitempty"` AutocloseReferencedIssues *bool `url:"autoclose_referenced_issues,omitempty" json:"autoclose_referenced_issues,omitempty"` SuggestionCommitMessage *string `url:"suggestion_commit_message,omitempty" json:"suggestion_commit_message,omitempty"` + IssuesTemplate *string `url:"issues_template,omitempty" json:"issues_template,omitempty"` + MergeRequestsTemplate *string `url:"merge_requests_template,omitempty" json:"merge_requests_template,omitempty"` // Deprecated members IssuesEnabled *bool `url:"issues_enabled,omitempty" json:"issues_enabled,omitempty"` @@ -1431,6 +1495,7 @@ type ProjectApprovals struct { DisableOverridingApproversPerMergeRequest bool `json:"disable_overriding_approvers_per_merge_request"` MergeRequestsAuthorApproval bool `json:"merge_requests_author_approval"` MergeRequestsDisableCommittersApproval bool `json:"merge_requests_disable_committers_approval"` + RequirePasswordToApprove bool `json:"require_password_to_approve"` } // GetApprovalConfiguration get the approval configuration for a project. @@ -1469,6 +1534,7 @@ type ChangeApprovalConfigurationOptions struct { DisableOverridingApproversPerMergeRequest *bool `url:"disable_overriding_approvers_per_merge_request,omitempty" json:"disable_overriding_approvers_per_merge_request,omitempty"` MergeRequestsAuthorApproval *bool `url:"merge_requests_author_approval,omitempty" json:"merge_requests_author_approval,omitempty"` MergeRequestsDisableCommittersApproval *bool `url:"merge_requests_disable_committers_approval,omitempty" json:"merge_requests_disable_committers_approval,omitempty"` + RequirePasswordToApprove *bool `url:"require_password_to_approve,omitempty" json:"require_password_to_approve,omitempty"` } // ChangeApprovalConfiguration updates the approval configuration for a project. diff --git a/vendor/github.com/xanzy/go-gitlab/protected_branches.go b/vendor/github.com/xanzy/go-gitlab/protected_branches.go index bcc0369dbd785..af3e7f2111f71 100644 --- a/vendor/github.com/xanzy/go-gitlab/protected_branches.go +++ b/vendor/github.com/xanzy/go-gitlab/protected_branches.go @@ -41,6 +41,7 @@ type ProtectedBranch struct { PushAccessLevels []*BranchAccessDescription `json:"push_access_levels"` MergeAccessLevels []*BranchAccessDescription `json:"merge_access_levels"` UnprotectAccessLevels []*BranchAccessDescription `json:"unprotect_access_levels"` + AllowForcePush bool `json:"allow_force_push"` CodeOwnerApprovalRequired bool `json:"code_owner_approval_required"` } @@ -123,6 +124,7 @@ type ProtectRepositoryBranchesOptions struct { PushAccessLevel *AccessLevelValue `url:"push_access_level,omitempty" json:"push_access_level,omitempty"` MergeAccessLevel *AccessLevelValue `url:"merge_access_level,omitempty" json:"merge_access_level,omitempty"` UnprotectAccessLevel *AccessLevelValue `url:"unprotect_access_level,omitempty" json:"unprotect_access_level,omitempty"` + AllowForcePush *bool `url:"allow_force_push,omitempty" json:"allow_force_push,omitempty"` AllowedToPush []*BranchPermissionOptions `url:"allowed_to_push,omitempty" json:"allowed_to_push,omitempty"` AllowedToMerge []*BranchPermissionOptions `url:"allowed_to_merge,omitempty" json:"allowed_to_merge,omitempty"` AllowedToUnprotect []*BranchPermissionOptions `url:"allowed_to_unprotect,omitempty" json:"allowed_to_unprotect,omitempty"` @@ -194,7 +196,7 @@ type RequireCodeOwnerApprovalsOptions struct { CodeOwnerApprovalRequired *bool `url:"code_owner_approval_required,omitempty" json:"code_owner_approval_required,omitempty"` } -// RequireCodeOwnerApprovals updates the code owner approval. +// RequireCodeOwnerApprovals updates the code owner approval option. // // Gitlab API docs: // https://docs.gitlab.com/ee/api/protected_branches.html#require-code-owner-approvals-for-a-single-branch diff --git a/vendor/github.com/xanzy/go-gitlab/releaselinks.go b/vendor/github.com/xanzy/go-gitlab/releaselinks.go index 4ea1af8f31699..7d56ff5bf3249 100644 --- a/vendor/github.com/xanzy/go-gitlab/releaselinks.go +++ b/vendor/github.com/xanzy/go-gitlab/releaselinks.go @@ -33,10 +33,12 @@ type ReleaseLinksService struct { // // GitLab API docs: https://docs.gitlab.com/ee/api/releases/links.html type ReleaseLink struct { - ID int `json:"id"` - Name string `json:"name"` - URL string `json:"url"` - External bool `json:"external"` + ID int `json:"id"` + Name string `json:"name"` + URL string `json:"url"` + DirectAssetURL string `json:"direct_asset_url"` + External bool `json:"external"` + LinkType LinkTypeValue `json:"link_type"` } // ListReleaseLinksOptions represents ListReleaseLinks() options. @@ -99,8 +101,10 @@ func (s *ReleaseLinksService) GetReleaseLink(pid interface{}, tagName string, li // // GitLab API docs: https://docs.gitlab.com/ee/api/releases/links.html#create-a-link type CreateReleaseLinkOptions struct { - Name *string `url:"name" json:"name"` - URL *string `url:"url" json:"url"` + Name *string `url:"name,omitempty" json:"name,omitempty"` + URL *string `url:"url,omitempty" json:"url,omitempty"` + FilePath *string `url:"filepath,omitempty" json:"filepath,omitempty"` + LinkType *LinkTypeValue `url:"link_type,omitempty" json:"link_type,omitempty"` } // CreateReleaseLink creates a link. @@ -133,8 +137,10 @@ func (s *ReleaseLinksService) CreateReleaseLink(pid interface{}, tagName string, // // GitLab API docs: https://docs.gitlab.com/ee/api/releases/links.html#update-a-link type UpdateReleaseLinkOptions struct { - Name *string `url:"name,omitempty" json:"name,omitempty"` - URL *string `url:"url,omitempty" json:"url,omitempty"` + Name *string `url:"name,omitempty" json:"name,omitempty"` + URL *string `url:"url,omitempty" json:"url,omitempty"` + FilePath *string `url:"filepath,omitempty" json:"filepath,omitempty"` + LinkType *LinkTypeValue `url:"link_type,omitempty" json:"link_type,omitempty"` } // UpdateReleaseLink updates an asset link. diff --git a/vendor/github.com/xanzy/go-gitlab/releases.go b/vendor/github.com/xanzy/go-gitlab/releases.go index e4d16524ef749..bb91ab47a8eee 100644 --- a/vendor/github.com/xanzy/go-gitlab/releases.go +++ b/vendor/github.com/xanzy/go-gitlab/releases.go @@ -37,9 +37,10 @@ type ReleasesService struct { type Release struct { TagName string `json:"tag_name"` Name string `json:"name"` - Description string `json:"description,omitempty"` - DescriptionHTML string `json:"description_html,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` + Description string `json:"description"` + DescriptionHTML string `json:"description_html"` + CreatedAt *time.Time `json:"created_at"` + ReleasedAt *time.Time `json:"released_at"` Author struct { ID int `json:"id"` Name string `json:"name"` diff --git a/vendor/github.com/xanzy/go-gitlab/services.go b/vendor/github.com/xanzy/go-gitlab/services.go index ecfed70135de3..973a30cb8d753 100644 --- a/vendor/github.com/xanzy/go-gitlab/services.go +++ b/vendor/github.com/xanzy/go-gitlab/services.go @@ -596,10 +596,13 @@ func (s *ServicesService) GetJenkinsCIService(pid interface{}, options ...Reques // GitLab API docs: // https://docs.gitlab.com/ee/api/services.html#jenkins-ci type SetJenkinsCIServiceOptions struct { - URL *string `url:"jenkins_url,omitempty" json:"jenkins_url,omitempty"` - ProjectName *string `url:"project_name,omitempty" json:"project_name,omitempty"` - Username *string `url:"username,omitempty" json:"username,omitempty"` - Password *string `url:"password,omitempty" json:"password,omitempty"` + URL *string `url:"jenkins_url,omitempty" json:"jenkins_url,omitempty"` + ProjectName *string `url:"project_name,omitempty" json:"project_name,omitempty"` + Username *string `url:"username,omitempty" json:"username,omitempty"` + Password *string `url:"password,omitempty" json:"password,omitempty"` + PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"` + MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"` + TagPushEvents *bool `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"` } // SetJenkinsCIService sets Jenkins service for a project @@ -1327,3 +1330,98 @@ func (s *ServicesService) DeleteSlackService(pid interface{}, options ...Request return s.client.Do(req, nil) } + +// YouTrackService represents YouTrack service settings. +// +// GitLab API docs: +// https://docs.gitlab.com/ce/api/services.html#youtrack +type YouTrackService struct { + Service + Properties *YouTrackServiceProperties `json:"properties"` +} + +// YouTrackServiceProperties represents YouTrack specific properties. +// +// GitLab API docs: +// https://docs.gitlab.com/ce/api/services.html#youtrack +type YouTrackServiceProperties struct { + IssuesURL string `json:"issues_url"` + ProjectURL string `json:"project_url"` + Description string `json:"description"` + PushEvents bool `json:"push_events"` +} + +// GetYouTrackService gets YouTrack service settings for a project. +// +// GitLab API docs: +// https://docs.gitlab.com/ce/api/services.html#get-youtrack-service-settings +func (s *ServicesService) GetYouTrackService(pid interface{}, options ...RequestOptionFunc) (*YouTrackService, *Response, error) { + project, err := parseID(pid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf("projects/%s/services/youtrack", pathEscape(project)) + + req, err := s.client.NewRequest(http.MethodGet, u, nil, options) + if err != nil { + return nil, nil, err + } + + svc := new(YouTrackService) + resp, err := s.client.Do(req, svc) + if err != nil { + return nil, resp, err + } + + return svc, resp, err +} + +// SetYouTrackServiceOptions represents the available SetYouTrackService() +// options. +// +// GitLab API docs: +// https://docs.gitlab.com/ce/api/services.html#createedit-youtrack-service +type SetYouTrackServiceOptions struct { + IssuesURL *string `url:"issues_url,omitempty" json:"issues_url,omitempty"` + ProjectURL *string `url:"project_url,omitempty" json:"project_url,omitempty"` + Description *string `url:"description,omitempty" json:"description,omitempty"` + PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"` +} + +// SetYouTrackService sets YouTrack service for a project +// +// GitLab API docs: +// https://docs.gitlab.com/ce/api/services.html#createedit-youtrack-service +func (s *ServicesService) SetYouTrackService(pid interface{}, opt *SetYouTrackServiceOptions, options ...RequestOptionFunc) (*Response, error) { + project, err := parseID(pid) + if err != nil { + return nil, err + } + u := fmt.Sprintf("projects/%s/services/youtrack", pathEscape(project)) + + req, err := s.client.NewRequest(http.MethodPut, u, opt, options) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// DeleteYouTrackService deletes YouTrack service settings for a project. +// +// GitLab API docs: +// https://docs.gitlab.com/ce/api/services.html#delete-youtrack-service +func (s *ServicesService) DeleteYouTrackService(pid interface{}, options ...RequestOptionFunc) (*Response, error) { + project, err := parseID(pid) + if err != nil { + return nil, err + } + u := fmt.Sprintf("projects/%s/services/youtrack", pathEscape(project)) + + req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/vendor/github.com/xanzy/go-gitlab/types.go b/vendor/github.com/xanzy/go-gitlab/types.go index a893def051ef4..805cd5b9149bd 100644 --- a/vendor/github.com/xanzy/go-gitlab/types.go +++ b/vendor/github.com/xanzy/go-gitlab/types.go @@ -121,6 +121,42 @@ func DeploymentStatus(v DeploymentStatusValue) *DeploymentStatusValue { return p } +// EventTypeValue represents actions type for contribution events +type EventTypeValue string + +// List of available action type +// +// GitLab API docs: https://docs.gitlab.com/ce/api/events.html#action-types +const ( + CreatedEventType EventTypeValue = "created" + UpdatedEventType EventTypeValue = "updated" + ClosedEventType EventTypeValue = "closed" + ReopenedEventType EventTypeValue = "reopened" + PushedEventType EventTypeValue = "pushed" + CommentedEventType EventTypeValue = "commented" + MergedEventType EventTypeValue = "merged" + JoinedEventType EventTypeValue = "joined" + LeftEventType EventTypeValue = "left" + DestroyedEventType EventTypeValue = "destroyed" + ExpiredEventType EventTypeValue = "expired" +) + +// EventTargetTypeValue represents actions type value for contribution events +type EventTargetTypeValue string + +// List of available action type +// +// GitLab API docs: https://docs.gitlab.com/ce/api/events.html#target-types +const ( + IssueEventTargetType EventTargetTypeValue = "issue" + MilestoneEventTargetType EventTargetTypeValue = "milestone" + MergeRequestEventTargetType EventTargetTypeValue = "merge_request" + NoteEventTargetType EventTargetTypeValue = "note" + ProjectEventTargetType EventTargetTypeValue = "project" + SnippetEventTargetType EventTargetTypeValue = "snippet" + UserEventTargetType EventTargetTypeValue = "user" +) + // FileActionValue represents the available actions that can be performed on a file. // // GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions @@ -191,6 +227,69 @@ func (t ISOTime) String() string { return time.Time(t).Format(iso8601) } +// LinkTypeValue represents a release link type. +type LinkTypeValue string + +// List of available release link types +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/releases/links.html#create-a-link +const ( + ImageLinkType LinkTypeValue = "image" + OtherLinkType LinkTypeValue = "other" + PackageLinkType LinkTypeValue = "package" + RunbookLinkType LinkTypeValue = "runbook" +) + +// LinkType is a helper routine that allocates a new LinkType value +// to store v and returns a pointer to it. +func LinkType(v LinkTypeValue) *LinkTypeValue { + p := new(LinkTypeValue) + *p = v + return p +} + +// LicenseApprovalStatusValue describe the approval statuses of a license. +// +// GitLab API docs: https://docs.gitlab.com/ee/api/managed_licenses.html +type LicenseApprovalStatusValue string + +// List of available license approval statuses. +const ( + LicenseApproved LicenseApprovalStatusValue = "approved" + LicenseBlacklisted LicenseApprovalStatusValue = "blacklisted" +) + +// LicenseApprovalStatus is a helper routine that allocates a new license +// approval status value to store v and returns a pointer to it. +func LicenseApprovalStatus(v LicenseApprovalStatusValue) *LicenseApprovalStatusValue { + p := new(LicenseApprovalStatusValue) + *p = v + return p +} + +// MergeMethodValue represents a project merge type within GitLab. +// +// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#project-merge-method +type MergeMethodValue string + +// List of available merge type +// +// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#project-merge-method +const ( + NoFastForwardMerge MergeMethodValue = "merge" + FastForwardMerge MergeMethodValue = "ff" + RebaseMerge MergeMethodValue = "rebase_merge" +) + +// MergeMethod is a helper routine that allocates a new MergeMethod +// to sotre v and returns a pointer to it. +func MergeMethod(v MergeMethodValue) *MergeMethodValue { + p := new(MergeMethodValue) + *p = v + return p +} + // NotificationLevelValue represents a notification level. type NotificationLevelValue int @@ -261,28 +360,6 @@ func NotificationLevel(v NotificationLevelValue) *NotificationLevelValue { return p } -// VisibilityValue represents a visibility level within GitLab. -// -// GitLab API docs: https://docs.gitlab.com/ce/api/ -type VisibilityValue string - -// List of available visibility levels. -// -// GitLab API docs: https://docs.gitlab.com/ce/api/ -const ( - PrivateVisibility VisibilityValue = "private" - InternalVisibility VisibilityValue = "internal" - PublicVisibility VisibilityValue = "public" -) - -// Visibility is a helper routine that allocates a new VisibilityValue -// to store v and returns a pointer to it. -func Visibility(v VisibilityValue) *VisibilityValue { - p := new(VisibilityValue) - *p = v - return p -} - // ProjectCreationLevelValue represents a project creation level within GitLab. // // GitLab API docs: https://docs.gitlab.com/ce/api/ @@ -347,6 +424,28 @@ func VariableType(v VariableTypeValue) *VariableTypeValue { return p } +// VisibilityValue represents a visibility level within GitLab. +// +// GitLab API docs: https://docs.gitlab.com/ce/api/ +type VisibilityValue string + +// List of available visibility levels. +// +// GitLab API docs: https://docs.gitlab.com/ce/api/ +const ( + PrivateVisibility VisibilityValue = "private" + InternalVisibility VisibilityValue = "internal" + PublicVisibility VisibilityValue = "public" +) + +// Visibility is a helper routine that allocates a new VisibilityValue +// to store v and returns a pointer to it. +func Visibility(v VisibilityValue) *VisibilityValue { + p := new(VisibilityValue) + *p = v + return p +} + // WikiFormatValue represents the available wiki formats. // // GitLab API docs: https://docs.gitlab.com/ce/api/wikis.html @@ -368,64 +467,6 @@ func WikiFormat(v WikiFormatValue) *WikiFormatValue { return p } -// MergeMethodValue represents a project merge type within GitLab. -// -// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#project-merge-method -type MergeMethodValue string - -// List of available merge type -// -// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#project-merge-method -const ( - NoFastForwardMerge MergeMethodValue = "merge" - FastForwardMerge MergeMethodValue = "ff" - RebaseMerge MergeMethodValue = "rebase_merge" -) - -// MergeMethod is a helper routine that allocates a new MergeMethod -// to sotre v and returns a pointer to it. -func MergeMethod(v MergeMethodValue) *MergeMethodValue { - p := new(MergeMethodValue) - *p = v - return p -} - -// EventTypeValue represents actions type for contribution events -type EventTypeValue string - -// List of available action type -// -// GitLab API docs: https://docs.gitlab.com/ce/api/events.html#action-types -const ( - CreatedEventType EventTypeValue = "created" - UpdatedEventType EventTypeValue = "updated" - ClosedEventType EventTypeValue = "closed" - ReopenedEventType EventTypeValue = "reopened" - PushedEventType EventTypeValue = "pushed" - CommentedEventType EventTypeValue = "commented" - MergedEventType EventTypeValue = "merged" - JoinedEventType EventTypeValue = "joined" - LeftEventType EventTypeValue = "left" - DestroyedEventType EventTypeValue = "destroyed" - ExpiredEventType EventTypeValue = "expired" -) - -// EventTargetTypeValue represents actions type value for contribution events -type EventTargetTypeValue string - -// List of available action type -// -// GitLab API docs: https://docs.gitlab.com/ce/api/events.html#target-types -const ( - IssueEventTargetType EventTargetTypeValue = "issue" - MilestoneEventTargetType EventTargetTypeValue = "milestone" - MergeRequestEventTargetType EventTargetTypeValue = "merge_request" - NoteEventTargetType EventTargetTypeValue = "note" - ProjectEventTargetType EventTargetTypeValue = "project" - SnippetEventTargetType EventTargetTypeValue = "snippet" - UserEventTargetType EventTargetTypeValue = "user" -) - // Bool is a helper routine that allocates a new bool value // to store v and returns a pointer to it. func Bool(v bool) *bool { diff --git a/vendor/github.com/xanzy/go-gitlab/users.go b/vendor/github.com/xanzy/go-gitlab/users.go index 965a0ed13beb0..d43e802e19ab5 100644 --- a/vendor/github.com/xanzy/go-gitlab/users.go +++ b/vendor/github.com/xanzy/go-gitlab/users.go @@ -143,13 +143,20 @@ func (s *UsersService) ListUsers(opt *ListUsersOptions, options ...RequestOption return usr, resp, err } +// GetUsersOptions represents the available GetUser() options. +// +// GitLab API docs: https://docs.gitlab.com/ce/api/users.html#single-user +type GetUsersOptions struct { + WithCustomAttributes *bool `url:"with_custom_attributes,omitempty" json:"with_custom_attributes,omitempty"` +} + // GetUser gets a single user. // // GitLab API docs: https://docs.gitlab.com/ce/api/users.html#single-user -func (s *UsersService) GetUser(user int, options ...RequestOptionFunc) (*User, *Response, error) { +func (s *UsersService) GetUser(user int, opt GetUsersOptions, options ...RequestOptionFunc) (*User, *Response, error) { u := fmt.Sprintf("users/%d", user) - req, err := s.client.NewRequest(http.MethodGet, u, nil, options) + req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } @@ -838,7 +845,7 @@ type PersonalAccessToken struct { Revoked bool `json:"revoked"` CreatedAt *time.Time `json:"created_at"` Scopes []string `json:"scopes"` - UserID string `json:"user_id"` + UserID int `json:"user_id"` Active bool `json:"active"` ExpiresAt *ISOTime `json:"expires_at"` Token string `json:"token"` diff --git a/vendor/github.com/yuin/goldmark-highlighting/go.mod b/vendor/github.com/yuin/goldmark-highlighting/go.mod index 87a5bc0092ee7..0fe74375b286c 100644 --- a/vendor/github.com/yuin/goldmark-highlighting/go.mod +++ b/vendor/github.com/yuin/goldmark-highlighting/go.mod @@ -3,12 +3,7 @@ module github.com/yuin/goldmark-highlighting go 1.13 require ( - github.com/GeertJohan/go.rice v1.0.0 // indirect github.com/alecthomas/chroma v0.7.2-0.20200305040604-4f3623dce67a - github.com/alecthomas/kong-hcl v0.1.8-0.20190615233001-b21fea9723c8 // indirect github.com/dlclark/regexp2 v1.2.0 // indirect - github.com/gorilla/csrf v1.6.0 // indirect - github.com/gorilla/handlers v1.4.1 // indirect - github.com/gorilla/mux v1.7.3 // indirect - github.com/yuin/goldmark v1.1.22 + github.com/yuin/goldmark v1.3.6 ) diff --git a/vendor/github.com/yuin/goldmark-highlighting/go.sum b/vendor/github.com/yuin/goldmark-highlighting/go.sum index 68947e114e1db..cedf339a3be4b 100644 --- a/vendor/github.com/yuin/goldmark-highlighting/go.sum +++ b/vendor/github.com/yuin/goldmark-highlighting/go.sum @@ -1,58 +1,34 @@ -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/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U= github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI= -github.com/alecthomas/chroma v0.7.0 h1:z+0HgTUmkpRDRz0SRSdMaqOLfJV4F+N1FPDZUZIDUzw= -github.com/alecthomas/chroma v0.7.0/go.mod h1:1U/PfCsTALWWYHDnsIQkxEBM0+6LLe0v8+RSVMOwxeY= -github.com/alecthomas/chroma v0.7.1 h1:G1i02OhUbRi2nJxcNkwJaY/J1gHXj9tt72qN6ZouLFQ= -github.com/alecthomas/chroma v0.7.1/go.mod h1:gHw09mkX1Qp80JlYbmN9L3+4R5o6DJJ3GRShh+AICNc= github.com/alecthomas/chroma v0.7.2-0.20200305040604-4f3623dce67a h1:3v1NrYWWqp2S72e4HLgxKt83B3l0lnORDholH/ihoMM= github.com/alecthomas/chroma v0.7.2-0.20200305040604-4f3623dce67a/go.mod h1:fv5SzZPFJbwp2NXJWpFIX7DZS4HgV1K4ew4Pc2OZD9s= github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo= github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0= -github.com/alecthomas/kong v0.1.17-0.20190424132513-439c674f7ae0/go.mod h1:+inYUSluD+p4L8KdviBSgzcqEjUQOfC5fQDRFuc36lI= github.com/alecthomas/kong v0.2.1-0.20190708041108-0548c6b1afae/go.mod h1:+inYUSluD+p4L8KdviBSgzcqEjUQOfC5fQDRFuc36lI= -github.com/alecthomas/kong-hcl v0.1.8-0.20190615233001-b21fea9723c8/go.mod h1:MRgZdU3vrFd05IQ89AxUZ0aYdF39BYoNFa324SodPCA= github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 h1:p9Sln00KOTlrYkxI1zYWl1QLnEqAqEARBEYa8FQnQcY= github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= -github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E= github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ= github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk= 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/dlclark/regexp2 v1.1.6 h1:CqB4MjHw0MFCDj+PHHjiESmHX+N7t0tJzKvC6M97BRg= github.com/dlclark/regexp2 v1.1.6/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk= github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/gorilla/csrf v1.6.0/go.mod h1:7tSf8kmjNYr7IWDCYhd3U8Ck34iQ/Yw5CJu7bAkHEGI= -github.com/gorilla/handlers v1.4.1/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= -github.com/yuin/goldmark v1.1.7 h1:XiwWADvxJeIM1JbXqthrEhDc19hTMui+o+QaY1hGXlk= -github.com/yuin/goldmark v1.1.7/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.22 h1:0e0f6Zee9SAQ5yOZGNMWaOxqVvcc/9/kUWu/Kl91Jk8= -github.com/yuin/goldmark v1.1.22/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.6 h1:rvdBidUJAJM2O9VLcNTB4oRwxG33uIxY+zUq6yWUT8c= +github.com/yuin/goldmark v1.3.6/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35 h1:YAFjXN64LMvktoUZH9zgY4lGc/msGN7HQfoSuKCgaDU= golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/vendor/github.com/yuin/goldmark-highlighting/highlighting.go b/vendor/github.com/yuin/goldmark-highlighting/highlighting.go index 3b39d2ced7164..2937e3fa30483 100644 --- a/vendor/github.com/yuin/goldmark-highlighting/highlighting.go +++ b/vendor/github.com/yuin/goldmark-highlighting/highlighting.go @@ -112,6 +112,9 @@ type Config struct { // Supported styles are defined under https://github.com/alecthomas/chroma/tree/master/formatters. Style string + // Pass in a custom Chroma style. If this is not nil, the Style string will be ignored + CustomStyle *chroma.Style + // If set, will try to guess language if none provided. // If the guessing fails, we will fall back to a text lexer. // Note that while Chroma's API supports language guessing, the implementation @@ -150,6 +153,8 @@ func (c *Config) SetOption(name renderer.OptionName, value interface{}) { switch name { case optStyle: c.Style = value.(string) + case optCustomStyle: + c.CustomStyle = value.(*chroma.Style) case optFormatOptions: if value != nil { c.FormatOptions = value.([]chromahtml.Option) @@ -200,6 +205,7 @@ func WithHTMLOptions(opts ...html.Option) Option { } const optStyle renderer.OptionName = "HighlightingStyle" +const optCustomStyle renderer.OptionName = "HighlightingCustomStyle" var highlightLinesAttrName = []byte("hl_lines") @@ -227,6 +233,23 @@ func WithStyle(style string) Option { return &withStyle{style} } +type withCustomStyle struct { + value *chroma.Style +} + +func (o *withCustomStyle) SetConfig(c *renderer.Config) { + c.Options[optCustomStyle] = o.value +} + +func (o *withCustomStyle) SetHighlightingOption(c *Config) { + c.CustomStyle = o.value +} + +// WithStyle is a functional option that changes highlighting style. +func WithCustomStyle(style *chroma.Style) Option { + return &withCustomStyle{style} +} + const optCSSWriter renderer.OptionName = "HighlightingCSSWriter" type withCSSWriter struct { @@ -316,7 +339,7 @@ func (o *withFormatOptions) SetConfig(c *renderer.Config) { if _, ok := c.Options[optFormatOptions]; !ok { c.Options[optFormatOptions] = []chromahtml.Option{} } - c.Options[optStyle] = append(c.Options[optFormatOptions].([]chromahtml.Option), o.value...) + c.Options[optFormatOptions] = append(c.Options[optFormatOptions].([]chromahtml.Option), o.value...) } func (o *withFormatOptions) SetHighlightingOption(c *Config) { @@ -385,7 +408,11 @@ func (r *HTMLRenderer) renderFencedCodeBlock(w util.BufWriter, source []byte, no chromaFormatterOptions := make([]chromahtml.Option, len(r.FormatOptions)) copy(chromaFormatterOptions, r.FormatOptions) - style := styles.Get(r.Style) + + style := r.CustomStyle + if style == nil { + style = styles.Get(r.Style) + } nohl := false var info []byte diff --git a/vendor/github.com/yuin/goldmark/README.md b/vendor/github.com/yuin/goldmark/README.md index f7a66f66f7e28..5520346acdb59 100644 --- a/vendor/github.com/yuin/goldmark/README.md +++ b/vendor/github.com/yuin/goldmark/README.md @@ -423,6 +423,7 @@ Extensions - [goldmark-emoji](https://github.com/yuin/goldmark-emoji): An emoji extension for the goldmark Markdown parser. - [goldmark-mathjax](https://github.com/litao91/goldmark-mathjax): Mathjax support for the goldmark markdown parser +- [goldmark-pdf](https://github.com/stephenafamo/goldmark-pdf): A PDF renderer that can be passed to `goldmark.WithRenderer()`. goldmark internal(for extension developers) ---------------------------------------------- diff --git a/vendor/github.com/yuin/goldmark/parser/attribute.go b/vendor/github.com/yuin/goldmark/parser/attribute.go index ea8c0645df8a3..4c7cc2492271d 100644 --- a/vendor/github.com/yuin/goldmark/parser/attribute.go +++ b/vendor/github.com/yuin/goldmark/parser/attribute.go @@ -129,6 +129,11 @@ func parseAttribute(reader text.Reader) (Attribute, bool) { if !ok { return Attribute{}, false } + if bytes.Equal(name, attrNameClass) { + if _, ok = value.([]byte); !ok { + return Attribute{}, false + } + } return Attribute{Name: name, Value: value}, true } diff --git a/vendor/github.com/yuin/goldmark/parser/parser.go b/vendor/github.com/yuin/goldmark/parser/parser.go index e58b5ee936cb6..6e0e935674466 100644 --- a/vendor/github.com/yuin/goldmark/parser/parser.go +++ b/vendor/github.com/yuin/goldmark/parser/parser.go @@ -1129,21 +1129,27 @@ func (p *parser) parseBlock(block text.BlockReader, parent ast.Node, pc Context) break } lineLength := len(line) + softLinebreak := false hardlineBreak := false - softLinebreak := line[lineLength-1] == '\n' - if lineLength >= 2 && line[lineLength-2] == '\\' && softLinebreak { // ends with \\n + hasNewLine := line[lineLength-1] == '\n' + if lineLength >= 2 && line[lineLength-2] == '\\' && hasNewLine { // ends with \\n lineLength -= 2 hardlineBreak = true - } else if lineLength >= 3 && line[lineLength-3] == '\\' && line[lineLength-2] == '\r' && softLinebreak { // ends with \\r\n + } else if lineLength >= 3 && line[lineLength-3] == '\\' && line[lineLength-2] == '\r' && hasNewLine { // ends with \\r\n lineLength -= 3 hardlineBreak = true - } else if lineLength >= 3 && line[lineLength-3] == ' ' && line[lineLength-2] == ' ' && softLinebreak { // ends with [space][space]\n + } else if lineLength >= 3 && line[lineLength-3] == ' ' && line[lineLength-2] == ' ' && hasNewLine { // ends with [space][space]\n lineLength -= 3 hardlineBreak = true - } else if lineLength >= 4 && line[lineLength-4] == ' ' && line[lineLength-3] == ' ' && line[lineLength-2] == '\r' && softLinebreak { // ends with [space][space]\r\n + } else if lineLength >= 4 && line[lineLength-4] == ' ' && line[lineLength-3] == ' ' && line[lineLength-2] == '\r' && hasNewLine { // ends with [space][space]\r\n lineLength -= 4 hardlineBreak = true + } else if hasNewLine { + // If the line ends with a newline character, but it is not a hardlineBreak, then it is a softLinebreak + // If the line ends with a hardlineBreak, then it cannot end with a softLinebreak + // See https://spec.commonmark.org/0.29/#soft-line-breaks + softLinebreak = true } l, startPosition := block.Position() diff --git a/vendor/go.etcd.io/bbolt/.gitignore b/vendor/go.etcd.io/bbolt/.gitignore index 3bcd8cbaf02dd..18312f0043adc 100644 --- a/vendor/go.etcd.io/bbolt/.gitignore +++ b/vendor/go.etcd.io/bbolt/.gitignore @@ -3,3 +3,5 @@ *.swp /bin/ cover.out +/.idea +*.iml diff --git a/vendor/go.etcd.io/bbolt/.travis.yml b/vendor/go.etcd.io/bbolt/.travis.yml index 257dfdfee487a..452601e49d628 100644 --- a/vendor/go.etcd.io/bbolt/.travis.yml +++ b/vendor/go.etcd.io/bbolt/.travis.yml @@ -4,9 +4,10 @@ go_import_path: go.etcd.io/bbolt sudo: false go: -- 1.12 +- 1.15 before_install: +- go get -v golang.org/x/sys/unix - go get -v honnef.co/go/tools/... - go get -v github.com/kisielk/errcheck diff --git a/vendor/go.etcd.io/bbolt/Makefile b/vendor/go.etcd.io/bbolt/Makefile index 2968aaa61dd3d..21ecf48f61008 100644 --- a/vendor/go.etcd.io/bbolt/Makefile +++ b/vendor/go.etcd.io/bbolt/Makefile @@ -2,8 +2,6 @@ BRANCH=`git rev-parse --abbrev-ref HEAD` COMMIT=`git rev-parse --short HEAD` GOLDFLAGS="-X main.branch $(BRANCH) -X main.commit $(COMMIT)" -default: build - race: @TEST_FREELIST_TYPE=hashmap go test -v -race -test.run="TestSimulate_(100op|1000op)" @echo "array freelist test" diff --git a/vendor/go.etcd.io/bbolt/README.md b/vendor/go.etcd.io/bbolt/README.md index c9e64b1a6150b..f1b4a7b2bf885 100644 --- a/vendor/go.etcd.io/bbolt/README.md +++ b/vendor/go.etcd.io/bbolt/README.md @@ -908,12 +908,14 @@ Below is a list of public, open source projects that use Bolt: * [BoltStore](https://github.com/yosssi/boltstore) - Session store using Bolt. * [Boltdb Boilerplate](https://github.com/bobintornado/boltdb-boilerplate) - Boilerplate wrapper around bolt aiming to make simple calls one-liners. * [BoltDbWeb](https://github.com/evnix/boltdbweb) - A web based GUI for BoltDB files. +* [BoltDB Viewer](https://github.com/zc310/rich_boltdb) - A BoltDB Viewer Can run on Windows、Linux、Android system. * [bleve](http://www.blevesearch.com/) - A pure Go search engine similar to ElasticSearch that uses Bolt as the default storage backend. * [btcwallet](https://github.com/btcsuite/btcwallet) - A bitcoin wallet. * [buckets](https://github.com/joyrexus/buckets) - a bolt wrapper streamlining simple tx and key scans. * [cayley](https://github.com/google/cayley) - Cayley is an open-source graph database using Bolt as optional backend. * [ChainStore](https://github.com/pressly/chainstore) - Simple key-value interface to a variety of storage engines organized as a chain of operations. +* [🌰 Chestnut](https://github.com/jrapoport/chestnut) - Chestnut is encrypted storage for Go. * [Consul](https://github.com/hashicorp/consul) - Consul is service discovery and configuration made easy. Distributed, highly available, and datacenter-aware. * [DVID](https://github.com/janelia-flyem/dvid) - Added Bolt as optional storage engine and testing it against Basho-tuned leveldb. * [dcrwallet](https://github.com/decred/dcrwallet) - A wallet for the Decred cryptocurrency. @@ -938,9 +940,8 @@ Below is a list of public, open source projects that use Bolt: * [MetricBase](https://github.com/msiebuhr/MetricBase) - Single-binary version of Graphite. * [MuLiFS](https://github.com/dankomiocevic/mulifs) - Music Library Filesystem creates a filesystem to organise your music files. * [NATS](https://github.com/nats-io/nats-streaming-server) - NATS Streaming uses bbolt for message and metadata storage. -* [Operation Go: A Routine Mission](http://gocode.io) - An online programming game for Golang using Bolt for user accounts and a leaderboard. -* [photosite/session](https://godoc.org/bitbucket.org/kardianos/photosite/session) - Sessions for a photo viewing site. * [Prometheus Annotation Server](https://github.com/oliver006/prom_annotation_server) - Annotation server for PromDash & Prometheus service monitoring system. +* [Rain](https://github.com/cenkalti/rain) - BitTorrent client and library. * [reef-pi](https://github.com/reef-pi/reef-pi) - reef-pi is an award winning, modular, DIY reef tank controller using easy to learn electronics based on a Raspberry Pi. * [Request Baskets](https://github.com/darklynx/request-baskets) - A web service to collect arbitrary HTTP requests and inspect them via REST API or simple web UI, similar to [RequestBin](http://requestb.in/) service * [Seaweed File System](https://github.com/chrislusf/seaweedfs) - Highly scalable distributed key~file system with O(1) disk read. diff --git a/vendor/go.etcd.io/bbolt/bolt_unix.go b/vendor/go.etcd.io/bbolt/bolt_unix.go index 2938fed58457d..4e5f65ccc845d 100644 --- a/vendor/go.etcd.io/bbolt/bolt_unix.go +++ b/vendor/go.etcd.io/bbolt/bolt_unix.go @@ -7,6 +7,8 @@ import ( "syscall" "time" "unsafe" + + "golang.org/x/sys/unix" ) // flock acquires an advisory lock on a file descriptor. @@ -49,13 +51,13 @@ func funlock(db *DB) error { // mmap memory maps a DB's data file. func mmap(db *DB, sz int) error { // Map the data file to memory. - b, err := syscall.Mmap(int(db.file.Fd()), 0, sz, syscall.PROT_READ, syscall.MAP_SHARED|db.MmapFlags) + b, err := unix.Mmap(int(db.file.Fd()), 0, sz, syscall.PROT_READ, syscall.MAP_SHARED|db.MmapFlags) if err != nil { return err } // Advise the kernel that the mmap is accessed randomly. - err = madvise(b, syscall.MADV_RANDOM) + err = unix.Madvise(b, syscall.MADV_RANDOM) if err != nil && err != syscall.ENOSYS { // Ignore not implemented error in kernel because it still works. return fmt.Errorf("madvise: %s", err) @@ -76,18 +78,9 @@ func munmap(db *DB) error { } // Unmap using the original byte slice. - err := syscall.Munmap(db.dataref) + err := unix.Munmap(db.dataref) db.dataref = nil db.data = nil db.datasz = 0 return err } - -// NOTE: This function is copied from stdlib because it is not available on darwin. -func madvise(b []byte, advice int) (err error) { - _, _, e1 := syscall.Syscall(syscall.SYS_MADVISE, uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), uintptr(advice)) - if e1 != 0 { - err = e1 - } - return -} diff --git a/vendor/go.etcd.io/bbolt/compact.go b/vendor/go.etcd.io/bbolt/compact.go new file mode 100644 index 0000000000000..e4fe91b046dd5 --- /dev/null +++ b/vendor/go.etcd.io/bbolt/compact.go @@ -0,0 +1,114 @@ +package bbolt + +// Compact will create a copy of the source DB and in the destination DB. This may +// reclaim space that the source database no longer has use for. txMaxSize can be +// used to limit the transactions size of this process and may trigger intermittent +// commits. A value of zero will ignore transaction sizes. +// TODO: merge with: https://github.com/etcd-io/etcd/blob/b7f0f52a16dbf83f18ca1d803f7892d750366a94/mvcc/backend/backend.go#L349 +func Compact(dst, src *DB, txMaxSize int64) error { + // commit regularly, or we'll run out of memory for large datasets if using one transaction. + var size int64 + tx, err := dst.Begin(true) + if err != nil { + return err + } + defer tx.Rollback() + + if err := walk(src, func(keys [][]byte, k, v []byte, seq uint64) error { + // On each key/value, check if we have exceeded tx size. + sz := int64(len(k) + len(v)) + if size+sz > txMaxSize && txMaxSize != 0 { + // Commit previous transaction. + if err := tx.Commit(); err != nil { + return err + } + + // Start new transaction. + tx, err = dst.Begin(true) + if err != nil { + return err + } + size = 0 + } + size += sz + + // Create bucket on the root transaction if this is the first level. + nk := len(keys) + if nk == 0 { + bkt, err := tx.CreateBucket(k) + if err != nil { + return err + } + if err := bkt.SetSequence(seq); err != nil { + return err + } + return nil + } + + // Create buckets on subsequent levels, if necessary. + b := tx.Bucket(keys[0]) + if nk > 1 { + for _, k := range keys[1:] { + b = b.Bucket(k) + } + } + + // Fill the entire page for best compaction. + b.FillPercent = 1.0 + + // If there is no value then this is a bucket call. + if v == nil { + bkt, err := b.CreateBucket(k) + if err != nil { + return err + } + if err := bkt.SetSequence(seq); err != nil { + return err + } + return nil + } + + // Otherwise treat it as a key/value pair. + return b.Put(k, v) + }); err != nil { + return err + } + + return tx.Commit() +} + +// walkFunc is the type of the function called for keys (buckets and "normal" +// values) discovered by Walk. keys is the list of keys to descend to the bucket +// owning the discovered key/value pair k/v. +type walkFunc func(keys [][]byte, k, v []byte, seq uint64) error + +// walk walks recursively the bolt database db, calling walkFn for each key it finds. +func walk(db *DB, walkFn walkFunc) error { + return db.View(func(tx *Tx) error { + return tx.ForEach(func(name []byte, b *Bucket) error { + return walkBucket(b, nil, name, nil, b.Sequence(), walkFn) + }) + }) +} + +func walkBucket(b *Bucket, keypath [][]byte, k, v []byte, seq uint64, fn walkFunc) error { + // Execute callback. + if err := fn(keypath, k, v, seq); err != nil { + return err + } + + // If this is not a bucket then stop. + if v != nil { + return nil + } + + // Iterate over each child key/value. + keypath = append(keypath, k) + return b.ForEach(func(k, v []byte) error { + if v == nil { + bkt := b.Bucket(k) + return walkBucket(bkt, keypath, k, nil, bkt.Sequence(), fn) + } + return walkBucket(b, keypath, k, v, b.Sequence(), fn) + }) +} diff --git a/vendor/go.etcd.io/bbolt/db.go b/vendor/go.etcd.io/bbolt/db.go index 80b0095cc348e..a798c390a2053 100644 --- a/vendor/go.etcd.io/bbolt/db.go +++ b/vendor/go.etcd.io/bbolt/db.go @@ -120,6 +120,12 @@ type DB struct { // of truncate() and fsync() when growing the data file. AllocSize int + // Mlock locks database file in memory when set to true. + // It prevents major page faults, however used memory can't be reclaimed. + // + // Supported only on Unix via mlock/munlock syscalls. + Mlock bool + path string openFile func(string, int, os.FileMode) (*os.File, error) file *os.File @@ -188,6 +194,7 @@ func Open(path string, mode os.FileMode, options *Options) (*DB, error) { db.MmapFlags = options.MmapFlags db.NoFreelistSync = options.NoFreelistSync db.FreelistType = options.FreelistType + db.Mlock = options.Mlock // Set default values for later DB operations. db.MaxBatchSize = DefaultMaxBatchSize @@ -337,7 +344,8 @@ func (db *DB) mmap(minsz int) error { } // Ensure the size is at least the minimum size. - var size = int(info.Size()) + fileSize := int(info.Size()) + var size = fileSize if size < minsz { size = minsz } @@ -346,6 +354,13 @@ func (db *DB) mmap(minsz int) error { return err } + if db.Mlock { + // Unlock db memory + if err := db.munlock(fileSize); err != nil { + return err + } + } + // Dereference all mmap references before unmapping. if db.rwtx != nil { db.rwtx.root.dereference() @@ -361,6 +376,13 @@ func (db *DB) mmap(minsz int) error { return err } + if db.Mlock { + // Don't allow swapping of data file + if err := db.mlock(fileSize); err != nil { + return err + } + } + // Save references to the meta pages. db.meta0 = db.page(0).meta() db.meta1 = db.page(1).meta() @@ -422,12 +444,36 @@ func (db *DB) mmapSize(size int) (int, error) { return int(sz), nil } +func (db *DB) munlock(fileSize int) error { + if err := munlock(db, fileSize); err != nil { + return fmt.Errorf("munlock error: " + err.Error()) + } + return nil +} + +func (db *DB) mlock(fileSize int) error { + if err := mlock(db, fileSize); err != nil { + return fmt.Errorf("mlock error: " + err.Error()) + } + return nil +} + +func (db *DB) mrelock(fileSizeFrom, fileSizeTo int) error { + if err := db.munlock(fileSizeFrom); err != nil { + return err + } + if err := db.mlock(fileSizeTo); err != nil { + return err + } + return nil +} + // init creates a new database file and initializes its meta pages. func (db *DB) init() error { // Create two meta pages on a buffer. buf := make([]byte, db.pageSize*4) for i := 0; i < 2; i++ { - p := db.pageInBuffer(buf[:], pgid(i)) + p := db.pageInBuffer(buf, pgid(i)) p.id = pgid(i) p.flags = metaPageFlag @@ -444,13 +490,13 @@ func (db *DB) init() error { } // Write an empty freelist at page 3. - p := db.pageInBuffer(buf[:], pgid(2)) + p := db.pageInBuffer(buf, pgid(2)) p.id = pgid(2) p.flags = freelistPageFlag p.count = 0 // Write an empty leaf page at page 4. - p = db.pageInBuffer(buf[:], pgid(3)) + p = db.pageInBuffer(buf, pgid(3)) p.id = pgid(3) p.flags = leafPageFlag p.count = 0 @@ -462,6 +508,7 @@ func (db *DB) init() error { if err := fdatasync(db); err != nil { return err } + db.filesz = len(buf) return nil } @@ -973,6 +1020,12 @@ func (db *DB) grow(sz int) error { if err := db.file.Sync(); err != nil { return fmt.Errorf("file sync error: %s", err) } + if db.Mlock { + // unlock old file and lock new one + if err := db.mrelock(db.filesz, sz); err != nil { + return fmt.Errorf("mlock/munlock error: %s", err) + } + } } db.filesz = sz @@ -1064,6 +1117,11 @@ type Options struct { // OpenFile is used to open files. It defaults to os.OpenFile. This option // is useful for writing hermetic tests. OpenFile func(string, int, os.FileMode) (*os.File, error) + + // Mlock locks database file in memory when set to true. + // It prevents potential page faults, however + // used memory can't be reclaimed. (UNIX only) + Mlock bool } // DefaultOptions represent the options used if nil options are passed into Open(). diff --git a/vendor/go.etcd.io/bbolt/freelist_hmap.go b/vendor/go.etcd.io/bbolt/freelist_hmap.go index 02ef2be044179..dbd67a1e7361a 100644 --- a/vendor/go.etcd.io/bbolt/freelist_hmap.go +++ b/vendor/go.etcd.io/bbolt/freelist_hmap.go @@ -4,7 +4,7 @@ import "sort" // hashmapFreeCount returns count of free pages(hashmap version) func (f *freelist) hashmapFreeCount() int { - // use the forwardmap to get the total count + // use the forwardMap to get the total count count := 0 for _, size := range f.forwardMap { count += int(size) @@ -41,7 +41,7 @@ func (f *freelist) hashmapAllocate(txid txid, n int) pgid { for pid := range bm { // remove the initial - f.delSpan(pid, uint64(size)) + f.delSpan(pid, size) f.allocs[pid] = txid @@ -51,7 +51,7 @@ func (f *freelist) hashmapAllocate(txid txid, n int) pgid { f.addSpan(pid+pgid(n), remain) for i := pgid(0); i < pgid(n); i++ { - delete(f.cache, pid+pgid(i)) + delete(f.cache, pid+i) } return pid } diff --git a/vendor/go.etcd.io/bbolt/go.mod b/vendor/go.etcd.io/bbolt/go.mod index c2366daef6b89..96355a69b9c0e 100644 --- a/vendor/go.etcd.io/bbolt/go.mod +++ b/vendor/go.etcd.io/bbolt/go.mod @@ -2,4 +2,4 @@ module go.etcd.io/bbolt go 1.12 -require golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 +require golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d diff --git a/vendor/go.etcd.io/bbolt/go.sum b/vendor/go.etcd.io/bbolt/go.sum index 4ad15a488c70e..c13f8f4700692 100644 --- a/vendor/go.etcd.io/bbolt/go.sum +++ b/vendor/go.etcd.io/bbolt/go.sum @@ -1,2 +1,2 @@ -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d h1:L/IKR6COd7ubZrs2oTnTi73IhgqJ71c9s80WsQnh0Es= +golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/vendor/go.etcd.io/bbolt/mlock_unix.go b/vendor/go.etcd.io/bbolt/mlock_unix.go new file mode 100644 index 0000000000000..6a6c7b3537ebe --- /dev/null +++ b/vendor/go.etcd.io/bbolt/mlock_unix.go @@ -0,0 +1,36 @@ +// +build !windows + +package bbolt + +import "golang.org/x/sys/unix" + +// mlock locks memory of db file +func mlock(db *DB, fileSize int) error { + sizeToLock := fileSize + if sizeToLock > db.datasz { + // Can't lock more than mmaped slice + sizeToLock = db.datasz + } + if err := unix.Mlock(db.dataref[:sizeToLock]); err != nil { + return err + } + return nil +} + +//munlock unlocks memory of db file +func munlock(db *DB, fileSize int) error { + if db.dataref == nil { + return nil + } + + sizeToUnlock := fileSize + if sizeToUnlock > db.datasz { + // Can't unlock more than mmaped slice + sizeToUnlock = db.datasz + } + + if err := unix.Munlock(db.dataref[:sizeToUnlock]); err != nil { + return err + } + return nil +} diff --git a/vendor/go.etcd.io/bbolt/mlock_windows.go b/vendor/go.etcd.io/bbolt/mlock_windows.go new file mode 100644 index 0000000000000..b4a36a493db13 --- /dev/null +++ b/vendor/go.etcd.io/bbolt/mlock_windows.go @@ -0,0 +1,11 @@ +package bbolt + +// mlock locks memory of db file +func mlock(_ *DB, _ int) error { + panic("mlock is supported only on UNIX systems") +} + +//munlock unlocks memory of db file +func munlock(_ *DB, _ int) error { + panic("munlock is supported only on UNIX systems") +} diff --git a/vendor/go.etcd.io/bbolt/tx.go b/vendor/go.etcd.io/bbolt/tx.go index 4b1a64a8b8aa6..869d41200819d 100644 --- a/vendor/go.etcd.io/bbolt/tx.go +++ b/vendor/go.etcd.io/bbolt/tx.go @@ -188,7 +188,6 @@ func (tx *Tx) Commit() error { } // If strict mode is enabled then perform a consistency check. - // Only the first consistency error is reported in the panic. if tx.db.StrictMode { ch := tx.Check() var errs []string @@ -393,7 +392,7 @@ func (tx *Tx) CopyFile(path string, mode os.FileMode) error { return err } - err = tx.Copy(f) + _, err = tx.WriteTo(f) if err != nil { _ = f.Close() return err diff --git a/vendor/go.opentelemetry.io/otel/.gitignore b/vendor/go.opentelemetry.io/otel/.gitignore deleted file mode 100644 index 69f09e575fce4..0000000000000 --- a/vendor/go.opentelemetry.io/otel/.gitignore +++ /dev/null @@ -1,19 +0,0 @@ -.DS_Store -Thumbs.db - -.tools/ -.idea/ -.vscode/ -*.iml -*.so -coverage.* - -gen/ - -/example/jaeger/jaeger -/example/namedtracer/namedtracer -/example/opencensus/opencensus -/example/prometheus/prometheus -/example/prom-collector/prom-collector -/example/zipkin/zipkin -/example/otel-collector/otel-collector diff --git a/vendor/go.opentelemetry.io/otel/.gitmodules b/vendor/go.opentelemetry.io/otel/.gitmodules deleted file mode 100644 index 38a1f56982bad..0000000000000 --- a/vendor/go.opentelemetry.io/otel/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "opentelemetry-proto"] - path = exporters/otlp/internal/opentelemetry-proto - url = https://github.com/open-telemetry/opentelemetry-proto diff --git a/vendor/go.opentelemetry.io/otel/.golangci.yml b/vendor/go.opentelemetry.io/otel/.golangci.yml deleted file mode 100644 index 2ef168198c272..0000000000000 --- a/vendor/go.opentelemetry.io/otel/.golangci.yml +++ /dev/null @@ -1,32 +0,0 @@ -# See https://github.com/golangci/golangci-lint#config-file -run: - issues-exit-code: 1 #Default - tests: true #Default - -linters: - enable: - - misspell - - goimports - - golint - - gofmt - -issues: - exclude-rules: - # helpers in tests often (rightfully) pass a *testing.T as their first argument - - path: _test\.go - text: "context.Context should be the first parameter of a function" - linters: - - golint - # Yes, they are, but it's okay in a test - - path: _test\.go - text: "exported func.*returns unexported type.*which can be annoying to use" - linters: - - golint - -linters-settings: - misspell: - locale: US - ignore-words: - - cancelled - goimports: - local-prefixes: go.opentelemetry.io diff --git a/vendor/go.opentelemetry.io/otel/CHANGELOG.md b/vendor/go.opentelemetry.io/otel/CHANGELOG.md deleted file mode 100644 index b430b960a4bfa..0000000000000 --- a/vendor/go.opentelemetry.io/otel/CHANGELOG.md +++ /dev/null @@ -1,1188 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - -This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -## [0.19.0] - 2020-03-18 - -### Added - -- Added `Marshaler` config option to `otlphttp` to enable otlp over json or protobufs. (#1586) -- A `ForceFlush` method to the `"go.opentelemetry.io/otel/sdk/trace".TracerProvider` to flush all registered `SpanProcessor`s. (#1608) -- Added `WithSampler` and `WithSpanLimits` to tracer provider. (#1633, #1702) -- `"go.opentelemetry.io/otel/trace".SpanContext` now has a `remote` property, and `IsRemote()` predicate, that is true when the `SpanContext` has been extracted from remote context data. (#1701) -- A `Valid` method to the `"go.opentelemetry.io/otel/attribute".KeyValue` type. (#1703) - -### Changed - -- `trace.SpanContext` is now immutable and has no exported fields. (#1573) - - `trace.NewSpanContext()` can be used in conjunction with the `trace.SpanContextConfig` struct to initialize a new `SpanContext` where all values are known. -- Update the `ForceFlush` method signature to the `"go.opentelemetry.io/otel/sdk/trace".SpanProcessor` to accept a `context.Context` and return an error. (#1608) -- Update the `Shutdown` method to the `"go.opentelemetry.io/otel/sdk/trace".TracerProvider` return an error on shutdown failure. (#1608) -- The SimpleSpanProcessor will now shut down the enclosed `SpanExporter` and gracefully ignore subsequent calls to `OnEnd` after `Shutdown` is called. (#1612) -- `"go.opentelemetry.io/sdk/metric/controller.basic".WithPusher` is replaced with `WithExporter` to provide consistent naming across project. (#1656) -- Added non-empty string check for trace `Attribute` keys. (#1659) -- Add `description` to SpanStatus only when `StatusCode` is set to error. (#1662) -- Jaeger exporter falls back to `resource.Default`'s `service.name` if the exported Span does not have one. (#1673) -- Jaeger exporter populates Jaeger's Span Process from Resource. (#1673) -- Renamed the `LabelSet` method of `"go.opentelemetry.io/otel/sdk/resource".Resource` to `Set`. (#1692) -- Changed `WithSDK` to `WithSDKOptions` to accept variadic arguments of `TracerProviderOption` type in `go.opentelemetry.io/otel/exporters/trace/jaeger` package. (#1693) -- Changed `WithSDK` to `WithSDKOptions` to accept variadic arguments of `TracerProviderOption` type in `go.opentelemetry.io/otel/exporters/trace/zipkin` package. (#1693) -- `"go.opentelemetry.io/otel/sdk/resource".NewWithAttributes` will now drop any invalid attributes passed. (#1703) -- `"go.opentelemetry.io/otel/sdk/resource".StringDetector` will now error if the produced attribute is invalid. (#1703) - -### Removed - -- Removed `serviceName` parameter from Zipkin exporter and uses resource instead. (#1549) -- Removed `WithConfig` from tracer provider to avoid overriding configuration. (#1633) -- Removed the exported `SimpleSpanProcessor` and `BatchSpanProcessor` structs. - These are now returned as a SpanProcessor interface from their respective constructors. (#1638) -- Removed `WithRecord()` from `trace.SpanOption` when creating a span. (#1660) -- Removed setting status to `Error` while recording an error as a span event in `RecordError`. (#1663) -- Removed `jaeger.WithProcess` configuration option. (#1673) -- Removed `ApplyConfig` method from `"go.opentelemetry.io/otel/sdk/trace".TracerProvider` and the now unneeded `Config` struct. (#1693) - -### Fixed - -- Jaeger Exporter: Ensure mapping between OTEL and Jaeger span data complies with the specification. (#1626) -- `SamplingResult.TraceState` is correctly propagated to a newly created span's `SpanContext`. (#1655) -- The `otel-collector` example now correctly flushes metric events prior to shutting down the exporter. (#1678) -- Do not set span status message in `SpanStatusFromHTTPStatusCode` if it can be inferred from `http.status_code`. (#1681) -- Synchronization issues in global trace delegate implementation. (#1686) -- Reduced excess memory usage by global `TracerProvider`. (#1687) - -## [0.18.0] - 2020-03-03 - -### Added - -- Added `resource.Default()` for use with meter and tracer providers. (#1507) -- `AttributePerEventCountLimit` and `AttributePerLinkCountLimit` for `SpanLimits`. (#1535) -- Added `Keys()` method to `propagation.TextMapCarrier` and `propagation.HeaderCarrier` to adapt `http.Header` to this interface. (#1544) -- Added `code` attributes to `go.opentelemetry.io/otel/semconv` package. (#1558) -- Compatibility testing suite in the CI system for the following systems. (#1567) - | OS | Go Version | Architecture | - | ------- | ---------- | ------------ | - | Ubuntu | 1.15 | amd64 | - | Ubuntu | 1.14 | amd64 | - | Ubuntu | 1.15 | 386 | - | Ubuntu | 1.14 | 386 | - | MacOS | 1.15 | amd64 | - | MacOS | 1.14 | amd64 | - | Windows | 1.15 | amd64 | - | Windows | 1.14 | amd64 | - | Windows | 1.15 | 386 | - | Windows | 1.14 | 386 | - -### Changed - -- Replaced interface `oteltest.SpanRecorder` with its existing implementation - `StandardSpanRecorder`. (#1542) -- Default span limit values to 128. (#1535) -- Rename `MaxEventsPerSpan`, `MaxAttributesPerSpan` and `MaxLinksPerSpan` to `EventCountLimit`, `AttributeCountLimit` and `LinkCountLimit`, and move these fields into `SpanLimits`. (#1535) -- Renamed the `otel/label` package to `otel/attribute`. (#1541) -- Vendor the Jaeger exporter's dependency on Apache Thrift. (#1551) -- Parallelize the CI linting and testing. (#1567) -- Stagger timestamps in exact aggregator tests. (#1569) -- Changed all examples to use `WithBatchTimeout(5 * time.Second)` rather than `WithBatchTimeout(5)`. (#1621) -- Prevent end-users from implementing some interfaces (#1575) -``` - "otel/exporters/otlp/otlphttp".Option - "otel/exporters/stdout".Option - "otel/oteltest".Option - "otel/trace".TracerOption - "otel/trace".SpanOption - "otel/trace".EventOption - "otel/trace".LifeCycleOption - "otel/trace".InstrumentationOption - "otel/sdk/resource".Option - "otel/sdk/trace".ParentBasedSamplerOption - "otel/sdk/trace".ReadOnlySpan - "otel/sdk/trace".ReadWriteSpan -``` -### Removed - -- Removed attempt to resample spans upon changing the span name with `span.SetName()`. (#1545) -- The `test-benchmark` is no longer a dependency of the `precommit` make target. (#1567) -- Removed the `test-386` make target. - This was replaced with a full compatibility testing suite (i.e. multi OS/arch) in the CI system. (#1567) - -### Fixed - -- The sequential timing check of timestamps in the stdout exporter are now setup explicitly to be sequential (#1571). (#1572) -- Windows build of Jaeger tests now compiles with OS specific functions (#1576). (#1577) -- The sequential timing check of timestamps of go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue are now setup explicitly to be sequential (#1578). (#1579) -- Validate tracestate header keys with vendors according to the W3C TraceContext specification (#1475). (#1581) -- The OTLP exporter includes related labels for translations of a GaugeArray (#1563). (#1570) - -## [0.17.0] - 2020-02-12 - -### Changed - -- Rename project default branch from `master` to `main`. (#1505) -- Reverse order in which `Resource` attributes are merged, per change in spec. (#1501) -- Add tooling to maintain "replace" directives in go.mod files automatically. (#1528) -- Create new modules: otel/metric, otel/trace, otel/oteltest, otel/sdk/export/metric, otel/sdk/metric (#1528) -- Move metric-related public global APIs from otel to otel/metric/global. (#1528) - -## Fixed - -- Fixed otlpgrpc reconnection issue. -- The example code in the README.md of `go.opentelemetry.io/otel/exporters/otlp` is moved to a compiled example test and used the new `WithAddress` instead of `WithEndpoint`. (#1513) -- The otel-collector example now uses the default OTLP receiver port of the collector. - -## [0.16.0] - 2020-01-13 - -### Added - -- Add the `ReadOnlySpan` and `ReadWriteSpan` interfaces to provide better control for accessing span data. (#1360) -- `NewGRPCDriver` function returns a `ProtocolDriver` that maintains a single gRPC connection to the collector. (#1369) -- Added documentation about the project's versioning policy. (#1388) -- Added `NewSplitDriver` for OTLP exporter that allows sending traces and metrics to different endpoints. (#1418) -- Added codeql worfklow to GitHub Actions (#1428) -- Added Gosec workflow to GitHub Actions (#1429) -- Add new HTTP driver for OTLP exporter in `exporters/otlp/otlphttp`. Currently it only supports the binary protobuf payloads. (#1420) -- Add an OpenCensus exporter bridge. (#1444) - -### Changed - -- Rename `internal/testing` to `internal/internaltest`. (#1449) -- Rename `export.SpanData` to `export.SpanSnapshot` and use it only for exporting spans. (#1360) -- Store the parent's full `SpanContext` rather than just its span ID in the `span` struct. (#1360) -- Improve span duration accuracy. (#1360) -- Migrated CI/CD from CircleCI to GitHub Actions (#1382) -- Remove duplicate checkout from GitHub Actions workflow (#1407) -- Metric `array` aggregator renamed `exact` to match its `aggregation.Kind` (#1412) -- Metric `exact` aggregator includes per-point timestamps (#1412) -- Metric stdout exporter uses MinMaxSumCount aggregator for ValueRecorder instruments (#1412) -- `NewExporter` from `exporters/otlp` now takes a `ProtocolDriver` as a parameter. (#1369) -- Many OTLP Exporter options became gRPC ProtocolDriver options. (#1369) -- Unify endpoint API that related to OTel exporter. (#1401) -- Optimize metric histogram aggregator to re-use its slice of buckets. (#1435) -- Metric aggregator Count() and histogram Bucket.Counts are consistently `uint64`. (1430) -- Histogram aggregator accepts functional options, uses default boundaries if none given. (#1434) -- `SamplingResult` now passed a `Tracestate` from the parent `SpanContext` (#1432) -- Moved gRPC driver for OTLP exporter to `exporters/otlp/otlpgrpc`. (#1420) -- The `TraceContext` propagator now correctly propagates `TraceState` through the `SpanContext`. (#1447) -- Metric Push and Pull Controller components are combined into a single "basic" Controller: - - `WithExporter()` and `Start()` to configure Push behavior - - `Start()` is optional; use `Collect()` and `ForEach()` for Pull behavior - - `Start()` and `Stop()` accept Context. (#1378) -- The `Event` type is moved from the `otel/sdk/export/trace` package to the `otel/trace` API package. (#1452) - -### Removed - -- Remove `errUninitializedSpan` as its only usage is now obsolete. (#1360) -- Remove Metric export functionality related to quantiles and summary data points: this is not specified (#1412) -- Remove DDSketch metric aggregator; our intention is to re-introduce this as an option of the histogram aggregator after [new OTLP histogram data types](https://github.com/open-telemetry/opentelemetry-proto/pull/226) are released (#1412) - -### Fixed - -- `BatchSpanProcessor.Shutdown()` will now shutdown underlying `export.SpanExporter`. (#1443) - -## [0.15.0] - 2020-12-10 - -### Added - -- The `WithIDGenerator` `TracerProviderOption` is added to the `go.opentelemetry.io/otel/trace` package to configure an `IDGenerator` for the `TracerProvider`. (#1363) - -### Changed - -- The Zipkin exporter now uses the Span status code to determine. (#1328) -- `NewExporter` and `Start` functions in `go.opentelemetry.io/otel/exporters/otlp` now receive `context.Context` as a first parameter. (#1357) -- Move the OpenCensus example into `example` directory. (#1359) -- Moved the SDK's `internal.IDGenerator` interface in to the `sdk/trace` package to enable support for externally-defined ID generators. (#1363) -- Bump `github.com/google/go-cmp` from 0.5.3 to 0.5.4 (#1374) -- Bump `github.com/golangci/golangci-lint` in `/internal/tools` (#1375) - -### Fixed - -- Metric SDK `SumObserver` and `UpDownSumObserver` instruments correctness fixes. (#1381) - -## [0.14.0] - 2020-11-19 - -### Added - -- An `EventOption` and the related `NewEventConfig` function are added to the `go.opentelemetry.io/otel` package to configure Span events. (#1254) -- A `TextMapPropagator` and associated `TextMapCarrier` are added to the `go.opentelemetry.io/otel/oteltest` package to test `TextMap` type propagators and their use. (#1259) -- `SpanContextFromContext` returns `SpanContext` from context. (#1255) -- `TraceState` has been added to `SpanContext`. (#1340) -- `DeploymentEnvironmentKey` added to `go.opentelemetry.io/otel/semconv` package. (#1323) -- Add an OpenCensus to OpenTelemetry tracing bridge. (#1305) -- Add a parent context argument to `SpanProcessor.OnStart` to follow the specification. (#1333) -- Add missing tests for `sdk/trace/attributes_map.go`. (#1337) - -### Changed - -- Move the `go.opentelemetry.io/otel/api/trace` package into `go.opentelemetry.io/otel/trace` with the following changes. (#1229) (#1307) - - `ID` has been renamed to `TraceID`. - - `IDFromHex` has been renamed to `TraceIDFromHex`. - - `EmptySpanContext` is removed. -- Move the `go.opentelemetry.io/otel/api/trace/tracetest` package into `go.opentelemetry.io/otel/oteltest`. (#1229) -- OTLP Exporter updates: - - supports OTLP v0.6.0 (#1230, #1354) - - supports configurable aggregation temporality (default: Cumulative, optional: Stateless). (#1296) -- The Sampler is now called on local child spans. (#1233) -- The `Kind` type from the `go.opentelemetry.io/otel/api/metric` package was renamed to `InstrumentKind` to more specifically describe what it is and avoid semantic ambiguity. (#1240) -- The `MetricKind` method of the `Descriptor` type in the `go.opentelemetry.io/otel/api/metric` package was renamed to `Descriptor.InstrumentKind`. - This matches the returned type and fixes misuse of the term metric. (#1240) -- Move test harness from the `go.opentelemetry.io/otel/api/apitest` package into `go.opentelemetry.io/otel/oteltest`. (#1241) -- Move the `go.opentelemetry.io/otel/api/metric/metrictest` package into `go.opentelemetry.io/oteltest` as part of #964. (#1252) -- Move the `go.opentelemetry.io/otel/api/metric` package into `go.opentelemetry.io/otel/metric` as part of #1303. (#1321) -- Move the `go.opentelemetry.io/otel/api/metric/registry` package into `go.opentelemetry.io/otel/metric/registry` as a part of #1303. (#1316) -- Move the `Number` type (together with related functions) from `go.opentelemetry.io/otel/api/metric` package into `go.opentelemetry.io/otel/metric/number` as a part of #1303. (#1316) -- The function signature of the Span `AddEvent` method in `go.opentelemetry.io/otel` is updated to no longer take an unused context and instead take a required name and a variable number of `EventOption`s. (#1254) -- The function signature of the Span `RecordError` method in `go.opentelemetry.io/otel` is updated to no longer take an unused context and instead take a required error value and a variable number of `EventOption`s. (#1254) -- Move the `go.opentelemetry.io/otel/api/global` package to `go.opentelemetry.io/otel`. (#1262) (#1330) -- Move the `Version` function from `go.opentelemetry.io/otel/sdk` to `go.opentelemetry.io/otel`. (#1330) -- Rename correlation context header from `"otcorrelations"` to `"baggage"` to match the OpenTelemetry specification. (#1267) -- Fix `Code.UnmarshalJSON` to work with valid JSON only. (#1276) -- The `resource.New()` method changes signature to support builtin attributes and functional options, including `telemetry.sdk.*` and - `host.name` semantic conventions; the former method is renamed `resource.NewWithAttributes`. (#1235) -- The Prometheus exporter now exports non-monotonic counters (i.e. `UpDownCounter`s) as gauges. (#1210) -- Correct the `Span.End` method documentation in the `otel` API to state updates are not allowed on a span after it has ended. (#1310) -- Updated span collection limits for attribute, event and link counts to 1000 (#1318) -- Renamed `semconv.HTTPUrlKey` to `semconv.HTTPURLKey`. (#1338) - -### Removed - -- The `ErrInvalidHexID`, `ErrInvalidTraceIDLength`, `ErrInvalidSpanIDLength`, `ErrInvalidSpanIDLength`, or `ErrNilSpanID` from the `go.opentelemetry.io/otel` package are unexported now. (#1243) -- The `AddEventWithTimestamp` method on the `Span` interface in `go.opentelemetry.io/otel` is removed due to its redundancy. - It is replaced by using the `AddEvent` method with a `WithTimestamp` option. (#1254) -- The `MockSpan` and `MockTracer` types are removed from `go.opentelemetry.io/otel/oteltest`. - `Tracer` and `Span` from the same module should be used in their place instead. (#1306) -- `WorkerCount` option is removed from `go.opentelemetry.io/otel/exporters/otlp`. (#1350) -- Remove the following labels types: INT32, UINT32, UINT64 and FLOAT32. (#1314) - -### Fixed - -- Rename `MergeItererator` to `MergeIterator` in the `go.opentelemetry.io/otel/label` package. (#1244) -- The `go.opentelemetry.io/otel/api/global` packages global TextMapPropagator now delegates functionality to a globally set delegate for all previously returned propagators. (#1258) -- Fix condition in `label.Any`. (#1299) -- Fix global `TracerProvider` to pass options to its configured provider. (#1329) -- Fix missing handler for `ExactKind` aggregator in OTLP metrics transformer (#1309) - -## [0.13.0] - 2020-10-08 - -### Added - -- OTLP Metric exporter supports Histogram aggregation. (#1209) -- The `Code` struct from the `go.opentelemetry.io/otel/codes` package now supports JSON marshaling and unmarshaling as well as implements the `Stringer` interface. (#1214) -- A Baggage API to implement the OpenTelemetry specification. (#1217) -- Add Shutdown method to sdk/trace/provider, shutdown processors in the order they were registered. (#1227) - -### Changed - -- Set default propagator to no-op propagator. (#1184) -- The `HTTPSupplier`, `HTTPExtractor`, `HTTPInjector`, and `HTTPPropagator` from the `go.opentelemetry.io/otel/api/propagation` package were replaced with unified `TextMapCarrier` and `TextMapPropagator` in the `go.opentelemetry.io/otel/propagation` package. (#1212) (#1325) -- The `New` function from the `go.opentelemetry.io/otel/api/propagation` package was replaced with `NewCompositeTextMapPropagator` in the `go.opentelemetry.io/otel` package. (#1212) -- The status codes of the `go.opentelemetry.io/otel/codes` package have been updated to match the latest OpenTelemetry specification. - They now are `Unset`, `Error`, and `Ok`. - They no longer track the gRPC codes. (#1214) -- The `StatusCode` field of the `SpanData` struct in the `go.opentelemetry.io/otel/sdk/export/trace` package now uses the codes package from this package instead of the gRPC project. (#1214) -- Move the `go.opentelemetry.io/otel/api/baggage` package into `go.opentelemetry.io/otel/baggage`. (#1217) (#1325) -- A `Shutdown` method of `SpanProcessor` and all its implementations receives a context and returns an error. (#1264) - -### Fixed - -- Copies of data from arrays and slices passed to `go.opentelemetry.io/otel/label.ArrayValue()` are now used in the returned `Value` instead of using the mutable data itself. (#1226) - -### Removed - -- The `ExtractHTTP` and `InjectHTTP` functions from the `go.opentelemetry.io/otel/api/propagation` package were removed. (#1212) -- The `Propagators` interface from the `go.opentelemetry.io/otel/api/propagation` package was removed to conform to the OpenTelemetry specification. - The explicit `TextMapPropagator` type can be used in its place as this is the `Propagator` type the specification defines. (#1212) -- The `SetAttribute` method of the `Span` from the `go.opentelemetry.io/otel/api/trace` package was removed given its redundancy with the `SetAttributes` method. (#1216) -- The internal implementation of Baggage storage is removed in favor of using the new Baggage API functionality. (#1217) -- Remove duplicate hostname key `HostHostNameKey` in Resource semantic conventions. (#1219) -- Nested array/slice support has been removed. (#1226) - -## [0.12.0] - 2020-09-24 - -### Added - -- A `SpanConfigure` function in `go.opentelemetry.io/otel/api/trace` to create a new `SpanConfig` from `SpanOption`s. (#1108) -- In the `go.opentelemetry.io/otel/api/trace` package, `NewTracerConfig` was added to construct new `TracerConfig`s. - This addition was made to conform with our project option conventions. (#1155) -- Instrumentation library information was added to the Zipkin exporter. (#1119) -- The `SpanProcessor` interface now has a `ForceFlush()` method. (#1166) -- More semantic conventions for k8s as resource attributes. (#1167) - -### Changed - -- Add reconnecting udp connection type to Jaeger exporter. - This change adds a new optional implementation of the udp conn interface used to detect changes to an agent's host dns record. - It then adopts the new destination address to ensure the exporter doesn't get stuck. This change was ported from jaegertracing/jaeger-client-go#520. (#1063) -- Replace `StartOption` and `EndOption` in `go.opentelemetry.io/otel/api/trace` with `SpanOption`. - This change is matched by replacing the `StartConfig` and `EndConfig` with a unified `SpanConfig`. (#1108) -- Replace the `LinkedTo` span option in `go.opentelemetry.io/otel/api/trace` with `WithLinks`. - This is be more consistent with our other option patterns, i.e. passing the item to be configured directly instead of its component parts, and provides a cleaner function signature. (#1108) -- The `go.opentelemetry.io/otel/api/trace` `TracerOption` was changed to an interface to conform to project option conventions. (#1109) -- Move the `B3` and `TraceContext` from within the `go.opentelemetry.io/otel/api/trace` package to their own `go.opentelemetry.io/otel/propagators` package. - This removal of the propagators is reflective of the OpenTelemetry specification for these propagators as well as cleans up the `go.opentelemetry.io/otel/api/trace` API. (#1118) -- Rename Jaeger tags used for instrumentation library information to reflect changes in OpenTelemetry specification. (#1119) -- Rename `ProbabilitySampler` to `TraceIDRatioBased` and change semantics to ignore parent span sampling status. (#1115) -- Move `tools` package under `internal`. (#1141) -- Move `go.opentelemetry.io/otel/api/correlation` package to `go.opentelemetry.io/otel/api/baggage`. (#1142) - The `correlation.CorrelationContext` propagator has been renamed `baggage.Baggage`. Other exported functions and types are unchanged. -- Rename `ParentOrElse` sampler to `ParentBased` and allow setting samplers depending on parent span. (#1153) -- In the `go.opentelemetry.io/otel/api/trace` package, `SpanConfigure` was renamed to `NewSpanConfig`. (#1155) -- Change `dependabot.yml` to add a `Skip Changelog` label to dependabot-sourced PRs. (#1161) -- The [configuration style guide](https://github.com/open-telemetry/opentelemetry-go/blob/master/CONTRIBUTING.md#config) has been updated to - recommend the use of `newConfig()` instead of `configure()`. (#1163) -- The `otlp.Config` type has been unexported and changed to `otlp.config`, along with its initializer. (#1163) -- Ensure exported interface types include parameter names and update the - Style Guide to reflect this styling rule. (#1172) -- Don't consider unset environment variable for resource detection to be an error. (#1170) -- Rename `go.opentelemetry.io/otel/api/metric.ConfigureInstrument` to `NewInstrumentConfig` and - `go.opentelemetry.io/otel/api/metric.ConfigureMeter` to `NewMeterConfig`. -- ValueObserver instruments use LastValue aggregator by default. (#1165) -- OTLP Metric exporter supports LastValue aggregation. (#1165) -- Move the `go.opentelemetry.io/otel/api/unit` package to `go.opentelemetry.io/otel/unit`. (#1185) -- Rename `Provider` to `MeterProvider` in the `go.opentelemetry.io/otel/api/metric` package. (#1190) -- Rename `NoopProvider` to `NoopMeterProvider` in the `go.opentelemetry.io/otel/api/metric` package. (#1190) -- Rename `NewProvider` to `NewMeterProvider` in the `go.opentelemetry.io/otel/api/metric/metrictest` package. (#1190) -- Rename `Provider` to `MeterProvider` in the `go.opentelemetry.io/otel/api/metric/registry` package. (#1190) -- Rename `NewProvider` to `NewMeterProvider` in the `go.opentelemetry.io/otel/api/metri/registryc` package. (#1190) -- Rename `Provider` to `TracerProvider` in the `go.opentelemetry.io/otel/api/trace` package. (#1190) -- Rename `NoopProvider` to `NoopTracerProvider` in the `go.opentelemetry.io/otel/api/trace` package. (#1190) -- Rename `Provider` to `TracerProvider` in the `go.opentelemetry.io/otel/api/trace/tracetest` package. (#1190) -- Rename `NewProvider` to `NewTracerProvider` in the `go.opentelemetry.io/otel/api/trace/tracetest` package. (#1190) -- Rename `WrapperProvider` to `WrapperTracerProvider` in the `go.opentelemetry.io/otel/bridge/opentracing` package. (#1190) -- Rename `NewWrapperProvider` to `NewWrapperTracerProvider` in the `go.opentelemetry.io/otel/bridge/opentracing` package. (#1190) -- Rename `Provider` method of the pull controller to `MeterProvider` in the `go.opentelemetry.io/otel/sdk/metric/controller/pull` package. (#1190) -- Rename `Provider` method of the push controller to `MeterProvider` in the `go.opentelemetry.io/otel/sdk/metric/controller/push` package. (#1190) -- Rename `ProviderOptions` to `TracerProviderConfig` in the `go.opentelemetry.io/otel/sdk/trace` package. (#1190) -- Rename `ProviderOption` to `TracerProviderOption` in the `go.opentelemetry.io/otel/sdk/trace` package. (#1190) -- Rename `Provider` to `TracerProvider` in the `go.opentelemetry.io/otel/sdk/trace` package. (#1190) -- Rename `NewProvider` to `NewTracerProvider` in the `go.opentelemetry.io/otel/sdk/trace` package. (#1190) -- Renamed `SamplingDecision` values to comply with OpenTelemetry specification change. (#1192) -- Renamed Zipkin attribute names from `ot.status_code & ot.status_description` to `otel.status_code & otel.status_description`. (#1201) -- The default SDK now invokes registered `SpanProcessor`s in the order they were registered with the `TracerProvider`. (#1195) -- Add test of spans being processed by the `SpanProcessor`s in the order they were registered. (#1203) - -### Removed - -- Remove the B3 propagator from `go.opentelemetry.io/otel/propagators`. It is now located in the - `go.opentelemetry.io/contrib/propagators/` module. (#1191) -- Remove the semantic convention for HTTP status text, `HTTPStatusTextKey` from package `go.opentelemetry.io/otel/semconv`. (#1194) - -### Fixed - -- Zipkin example no longer mentions `ParentSampler`, corrected to `ParentBased`. (#1171) -- Fix missing shutdown processor in otel-collector example. (#1186) -- Fix missing shutdown processor in basic and namedtracer examples. (#1197) - -## [0.11.0] - 2020-08-24 - -### Added - -- Support for exporting array-valued attributes via OTLP. (#992) -- `Noop` and `InMemory` `SpanBatcher` implementations to help with testing integrations. (#994) -- Support for filtering metric label sets. (#1047) -- A dimensionality-reducing metric Processor. (#1057) -- Integration tests for more OTel Collector Attribute types. (#1062) -- A new `WithSpanProcessor` `ProviderOption` is added to the `go.opentelemetry.io/otel/sdk/trace` package to create a `Provider` and automatically register the `SpanProcessor`. (#1078) - -### Changed - -- Rename `sdk/metric/processor/test` to `sdk/metric/processor/processortest`. (#1049) -- Rename `sdk/metric/controller/test` to `sdk/metric/controller/controllertest`. (#1049) -- Rename `api/testharness` to `api/apitest`. (#1049) -- Rename `api/trace/testtrace` to `api/trace/tracetest`. (#1049) -- Change Metric Processor to merge multiple observations. (#1024) -- The `go.opentelemetry.io/otel/bridge/opentracing` bridge package has been made into its own module. - This removes the package dependencies of this bridge from the rest of the OpenTelemetry based project. (#1038) -- Renamed `go.opentelemetry.io/otel/api/standard` package to `go.opentelemetry.io/otel/semconv` to avoid the ambiguous and generic name `standard` and better describe the package as containing OpenTelemetry semantic conventions. (#1016) -- The environment variable used for resource detection has been changed from `OTEL_RESOURCE_LABELS` to `OTEL_RESOURCE_ATTRIBUTES` (#1042) -- Replace `WithSyncer` with `WithBatcher` in examples. (#1044) -- Replace the `google.golang.org/grpc/codes` dependency in the API with an equivalent `go.opentelemetry.io/otel/codes` package. (#1046) -- Merge the `go.opentelemetry.io/otel/api/label` and `go.opentelemetry.io/otel/api/kv` into the new `go.opentelemetry.io/otel/label` package. (#1060) -- Unify Callback Function Naming. - Rename `*Callback` with `*Func`. (#1061) -- CI builds validate against last two versions of Go, dropping 1.13 and adding 1.15. (#1064) -- The `go.opentelemetry.io/otel/sdk/export/trace` interfaces `SpanSyncer` and `SpanBatcher` have been replaced with a specification compliant `Exporter` interface. - This interface still supports the export of `SpanData`, but only as a slice. - Implementation are also required now to return any error from `ExportSpans` if one occurs as well as implement a `Shutdown` method for exporter clean-up. (#1078) -- The `go.opentelemetry.io/otel/sdk/trace` `NewBatchSpanProcessor` function no longer returns an error. - If a `nil` exporter is passed as an argument to this function, instead of it returning an error, it now returns a `BatchSpanProcessor` that handles the export of `SpanData` by not taking any action. (#1078) -- The `go.opentelemetry.io/otel/sdk/trace` `NewProvider` function to create a `Provider` no longer returns an error, instead only a `*Provider`. - This change is related to `NewBatchSpanProcessor` not returning an error which was the only error this function would return. (#1078) - -### Removed - -- Duplicate, unused API sampler interface. (#999) - Use the [`Sampler` interface](https://github.com/open-telemetry/opentelemetry-go/blob/v0.11.0/sdk/trace/sampling.go) provided by the SDK instead. -- The `grpctrace` instrumentation was moved to the `go.opentelemetry.io/contrib` repository and out of this repository. - This move includes moving the `grpc` example to the `go.opentelemetry.io/contrib` as well. (#1027) -- The `WithSpan` method of the `Tracer` interface. - The functionality this method provided was limited compared to what a user can provide themselves. - It was removed with the understanding that if there is sufficient user need it can be added back based on actual user usage. (#1043) -- The `RegisterSpanProcessor` and `UnregisterSpanProcessor` functions. - These were holdovers from an approach prior to the TracerProvider design. They were not used anymore. (#1077) -- The `oterror` package. (#1026) -- The `othttp` and `httptrace` instrumentations were moved to `go.opentelemetry.io/contrib`. (#1032) - -### Fixed - -- The `semconv.HTTPServerMetricAttributesFromHTTPRequest()` function no longer generates the high-cardinality `http.request.content.length` label. (#1031) -- Correct instrumentation version tag in Jaeger exporter. (#1037) -- The SDK span will now set an error event if the `End` method is called during a panic (i.e. it was deferred). (#1043) -- Move internally generated protobuf code from the `go.opentelemetry.io/otel` to the OTLP exporter to reduce dependency overhead. (#1050) -- The `otel-collector` example referenced outdated collector processors. (#1006) - -## [0.10.0] - 2020-07-29 - -This release migrates the default OpenTelemetry SDK into its own Go module, decoupling the SDK from the API and reducing dependencies for instrumentation packages. - -### Added - -- The Zipkin exporter now has `NewExportPipeline` and `InstallNewPipeline` constructor functions to match the common pattern. - These function build a new exporter with default SDK options and register the exporter with the `global` package respectively. (#944) -- Add propagator option for gRPC instrumentation. (#986) -- The `testtrace` package now tracks the `trace.SpanKind` for each span. (#987) - -### Changed - -- Replace the `RegisterGlobal` `Option` in the Jaeger exporter with an `InstallNewPipeline` constructor function. - This matches the other exporter constructor patterns and will register a new exporter after building it with default configuration. (#944) -- The trace (`go.opentelemetry.io/otel/exporters/trace/stdout`) and metric (`go.opentelemetry.io/otel/exporters/metric/stdout`) `stdout` exporters are now merged into a single exporter at `go.opentelemetry.io/otel/exporters/stdout`. - This new exporter was made into its own Go module to follow the pattern of all exporters and decouple it from the `go.opentelemetry.io/otel` module. (#956, #963) -- Move the `go.opentelemetry.io/otel/exporters/test` test package to `go.opentelemetry.io/otel/sdk/export/metric/metrictest`. (#962) -- The `go.opentelemetry.io/otel/api/kv/value` package was merged into the parent `go.opentelemetry.io/otel/api/kv` package. (#968) - - `value.Bool` was replaced with `kv.BoolValue`. - - `value.Int64` was replaced with `kv.Int64Value`. - - `value.Uint64` was replaced with `kv.Uint64Value`. - - `value.Float64` was replaced with `kv.Float64Value`. - - `value.Int32` was replaced with `kv.Int32Value`. - - `value.Uint32` was replaced with `kv.Uint32Value`. - - `value.Float32` was replaced with `kv.Float32Value`. - - `value.String` was replaced with `kv.StringValue`. - - `value.Int` was replaced with `kv.IntValue`. - - `value.Uint` was replaced with `kv.UintValue`. - - `value.Array` was replaced with `kv.ArrayValue`. -- Rename `Infer` to `Any` in the `go.opentelemetry.io/otel/api/kv` package. (#972) -- Change `othttp` to use the `httpsnoop` package to wrap the `ResponseWriter` so that optional interfaces (`http.Hijacker`, `http.Flusher`, etc.) that are implemented by the original `ResponseWriter`are also implemented by the wrapped `ResponseWriter`. (#979) -- Rename `go.opentelemetry.io/otel/sdk/metric/aggregator/test` package to `go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest`. (#980) -- Make the SDK into its own Go module called `go.opentelemetry.io/otel/sdk`. (#985) -- Changed the default trace `Sampler` from `AlwaysOn` to `ParentOrElse(AlwaysOn)`. (#989) - -### Removed - -- The `IndexedAttribute` function from the `go.opentelemetry.io/otel/api/label` package was removed in favor of `IndexedLabel` which it was synonymous with. (#970) - -### Fixed - -- Bump github.com/golangci/golangci-lint from 1.28.3 to 1.29.0 in /tools. (#953) -- Bump github.com/google/go-cmp from 0.5.0 to 0.5.1. (#957) -- Use `global.Handle` for span export errors in the OTLP exporter. (#946) -- Correct Go language formatting in the README documentation. (#961) -- Remove default SDK dependencies from the `go.opentelemetry.io/otel/api` package. (#977) -- Remove default SDK dependencies from the `go.opentelemetry.io/otel/instrumentation` package. (#983) -- Move documented examples for `go.opentelemetry.io/otel/instrumentation/grpctrace` interceptors into Go example tests. (#984) - -## [0.9.0] - 2020-07-20 - -### Added - -- A new Resource Detector interface is included to allow resources to be automatically detected and included. (#939) -- A Detector to automatically detect resources from an environment variable. (#939) -- Github action to generate protobuf Go bindings locally in `internal/opentelemetry-proto-gen`. (#938) -- OTLP .proto files from `open-telemetry/opentelemetry-proto` imported as a git submodule under `internal/opentelemetry-proto`. - References to `github.com/open-telemetry/opentelemetry-proto` changed to `go.opentelemetry.io/otel/internal/opentelemetry-proto-gen`. (#942) - -### Changed - -- Non-nil value `struct`s for key-value pairs will be marshalled using JSON rather than `Sprintf`. (#948) - -### Removed - -- Removed dependency on `github.com/open-telemetry/opentelemetry-collector`. (#943) - -## [0.8.0] - 2020-07-09 - -### Added - -- The `B3Encoding` type to represent the B3 encoding(s) the B3 propagator can inject. - A value for HTTP supported encodings (Multiple Header: `MultipleHeader`, Single Header: `SingleHeader`) are included. (#882) -- The `FlagsDeferred` trace flag to indicate if the trace sampling decision has been deferred. (#882) -- The `FlagsDebug` trace flag to indicate if the trace is a debug trace. (#882) -- Add `peer.service` semantic attribute. (#898) -- Add database-specific semantic attributes. (#899) -- Add semantic convention for `faas.coldstart` and `container.id`. (#909) -- Add http content size semantic conventions. (#905) -- Include `http.request_content_length` in HTTP request basic attributes. (#905) -- Add semantic conventions for operating system process resource attribute keys. (#919) -- The Jaeger exporter now has a `WithBatchMaxCount` option to specify the maximum number of spans sent in a batch. (#931) - -### Changed - -- Update `CONTRIBUTING.md` to ask for updates to `CHANGELOG.md` with each pull request. (#879) -- Use lowercase header names for B3 Multiple Headers. (#881) -- The B3 propagator `SingleHeader` field has been replaced with `InjectEncoding`. - This new field can be set to combinations of the `B3Encoding` bitmasks and will inject trace information in these encodings. - If no encoding is set, the propagator will default to `MultipleHeader` encoding. (#882) -- The B3 propagator now extracts from either HTTP encoding of B3 (Single Header or Multiple Header) based on what is contained in the header. - Preference is given to Single Header encoding with Multiple Header being the fallback if Single Header is not found or is invalid. - This behavior change is made to dynamically support all correctly encoded traces received instead of having to guess the expected encoding prior to receiving. (#882) -- Extend semantic conventions for RPC. (#900) -- To match constant naming conventions in the `api/standard` package, the `FaaS*` key names are appended with a suffix of `Key`. (#920) - - `"api/standard".FaaSName` -> `FaaSNameKey` - - `"api/standard".FaaSID` -> `FaaSIDKey` - - `"api/standard".FaaSVersion` -> `FaaSVersionKey` - - `"api/standard".FaaSInstance` -> `FaaSInstanceKey` - -### Removed - -- The `FlagsUnused` trace flag is removed. - The purpose of this flag was to act as the inverse of `FlagsSampled`, the inverse of `FlagsSampled` is used instead. (#882) -- The B3 header constants (`B3SingleHeader`, `B3DebugFlagHeader`, `B3TraceIDHeader`, `B3SpanIDHeader`, `B3SampledHeader`, `B3ParentSpanIDHeader`) are removed. - If B3 header keys are needed [the authoritative OpenZipkin package constants](https://pkg.go.dev/github.com/openzipkin/zipkin-go@v0.2.2/propagation/b3?tab=doc#pkg-constants) should be used instead. (#882) - -### Fixed - -- The B3 Single Header name is now correctly `b3` instead of the previous `X-B3`. (#881) -- The B3 propagator now correctly supports sampling only values (`b3: 0`, `b3: 1`, or `b3: d`) for a Single B3 Header. (#882) -- The B3 propagator now propagates the debug flag. - This removes the behavior of changing the debug flag into a set sampling bit. - Instead, this now follow the B3 specification and omits the `X-B3-Sampling` header. (#882) -- The B3 propagator now tracks "unset" sampling state (meaning "defer the decision") and does not set the `X-B3-Sampling` header when injecting. (#882) -- Bump github.com/itchyny/gojq from 0.10.3 to 0.10.4 in /tools. (#883) -- Bump github.com/opentracing/opentracing-go from v1.1.1-0.20190913142402-a7454ce5950e to v1.2.0. (#885) -- The tracing time conversion for OTLP spans is now correctly set to `UnixNano`. (#896) -- Ensure span status is not set to `Unknown` when no HTTP status code is provided as it is assumed to be `200 OK`. (#908) -- Ensure `httptrace.clientTracer` closes `http.headers` span. (#912) -- Prometheus exporter will not apply stale updates or forget inactive metrics. (#903) -- Add test for api.standard `HTTPClientAttributesFromHTTPRequest`. (#905) -- Bump github.com/golangci/golangci-lint from 1.27.0 to 1.28.1 in /tools. (#901, #913) -- Update otel-colector example to use the v0.5.0 collector. (#915) -- The `grpctrace` instrumentation uses a span name conforming to the OpenTelemetry semantic conventions (does not contain a leading slash (`/`)). (#922) -- The `grpctrace` instrumentation includes an `rpc.method` attribute now set to the gRPC method name. (#900, #922) -- The `grpctrace` instrumentation `rpc.service` attribute now contains the package name if one exists. - This is in accordance with OpenTelemetry semantic conventions. (#922) -- Correlation Context extractor will no longer insert an empty map into the returned context when no valid values are extracted. (#923) -- Bump google.golang.org/api from 0.28.0 to 0.29.0 in /exporters/trace/jaeger. (#925) -- Bump github.com/itchyny/gojq from 0.10.4 to 0.11.0 in /tools. (#926) -- Bump github.com/golangci/golangci-lint from 1.28.1 to 1.28.2 in /tools. (#930) - -## [0.7.0] - 2020-06-26 - -This release implements the v0.5.0 version of the OpenTelemetry specification. - -### Added - -- The othttp instrumentation now includes default metrics. (#861) -- This CHANGELOG file to track all changes in the project going forward. -- Support for array type attributes. (#798) -- Apply transitive dependabot go.mod dependency updates as part of a new automatic Github workflow. (#844) -- Timestamps are now passed to exporters for each export. (#835) -- Add new `Accumulation` type to metric SDK to transport telemetry from `Accumulator`s to `Processor`s. - This replaces the prior `Record` `struct` use for this purpose. (#835) -- New dependabot integration to automate package upgrades. (#814) -- `Meter` and `Tracer` implementations accept instrumentation version version as an optional argument. - This instrumentation version is passed on to exporters. (#811) (#805) (#802) -- The OTLP exporter includes the instrumentation version in telemetry it exports. (#811) -- Environment variables for Jaeger exporter are supported. (#796) -- New `aggregation.Kind` in the export metric API. (#808) -- New example that uses OTLP and the collector. (#790) -- Handle errors in the span `SetName` during span initialization. (#791) -- Default service config to enable retries for retry-able failed requests in the OTLP exporter and an option to override this default. (#777) -- New `go.opentelemetry.io/otel/api/oterror` package to uniformly support error handling and definitions for the project. (#778) -- New `global` default implementation of the `go.opentelemetry.io/otel/api/oterror.Handler` interface to be used to handle errors prior to an user defined `Handler`. - There is also functionality for the user to register their `Handler` as well as a convenience function `Handle` to handle an error with this global `Handler`(#778) -- Options to specify propagators for httptrace and grpctrace instrumentation. (#784) -- The required `application/json` header for the Zipkin exporter is included in all exports. (#774) -- Integrate HTTP semantics helpers from the contrib repository into the `api/standard` package. #769 - -### Changed - -- Rename `Integrator` to `Processor` in the metric SDK. (#863) -- Rename `AggregationSelector` to `AggregatorSelector`. (#859) -- Rename `SynchronizedCopy` to `SynchronizedMove`. (#858) -- Rename `simple` integrator to `basic` integrator. (#857) -- Merge otlp collector examples. (#841) -- Change the metric SDK to support cumulative, delta, and pass-through exporters directly. - With these changes, cumulative and delta specific exporters are able to request the correct kind of aggregation from the SDK. (#840) -- The `Aggregator.Checkpoint` API is renamed to `SynchronizedCopy` and adds an argument, a different `Aggregator` into which the copy is stored. (#812) -- The `export.Aggregator` contract is that `Update()` and `SynchronizedCopy()` are synchronized with each other. - All the aggregation interfaces (`Sum`, `LastValue`, ...) are not meant to be synchronized, as the caller is expected to synchronize aggregators at a higher level after the `Accumulator`. - Some of the `Aggregators` used unnecessary locking and that has been cleaned up. (#812) -- Use of `metric.Number` was replaced by `int64` now that we use `sync.Mutex` in the `MinMaxSumCount` and `Histogram` `Aggregators`. (#812) -- Replace `AlwaysParentSample` with `ParentSample(fallback)` to match the OpenTelemetry v0.5.0 specification. (#810) -- Rename `sdk/export/metric/aggregator` to `sdk/export/metric/aggregation`. #808 -- Send configured headers with every request in the OTLP exporter, instead of just on connection creation. (#806) -- Update error handling for any one off error handlers, replacing, instead, with the `global.Handle` function. (#791) -- Rename `plugin` directory to `instrumentation` to match the OpenTelemetry specification. (#779) -- Makes the argument order to Histogram and DDSketch `New()` consistent. (#781) - -### Removed - -- `Uint64NumberKind` and related functions from the API. (#864) -- Context arguments from `Aggregator.Checkpoint` and `Integrator.Process` as they were unused. (#803) -- `SpanID` is no longer included in parameters for sampling decision to match the OpenTelemetry specification. (#775) - -### Fixed - -- Upgrade OTLP exporter to opentelemetry-proto matching the opentelemetry-collector v0.4.0 release. (#866) -- Allow changes to `go.sum` and `go.mod` when running dependabot tidy-up. (#871) -- Bump github.com/stretchr/testify from 1.4.0 to 1.6.1. (#824) -- Bump github.com/prometheus/client_golang from 1.7.0 to 1.7.1 in /exporters/metric/prometheus. (#867) -- Bump google.golang.org/grpc from 1.29.1 to 1.30.0 in /exporters/trace/jaeger. (#853) -- Bump google.golang.org/grpc from 1.29.1 to 1.30.0 in /exporters/trace/zipkin. (#854) -- Bumps github.com/golang/protobuf from 1.3.2 to 1.4.2 (#848) -- Bump github.com/stretchr/testify from 1.4.0 to 1.6.1 in /exporters/otlp (#817) -- Bump github.com/golangci/golangci-lint from 1.25.1 to 1.27.0 in /tools (#828) -- Bump github.com/prometheus/client_golang from 1.5.0 to 1.7.0 in /exporters/metric/prometheus (#838) -- Bump github.com/stretchr/testify from 1.4.0 to 1.6.1 in /exporters/trace/jaeger (#829) -- Bump github.com/benbjohnson/clock from 1.0.0 to 1.0.3 (#815) -- Bump github.com/stretchr/testify from 1.4.0 to 1.6.1 in /exporters/trace/zipkin (#823) -- Bump github.com/itchyny/gojq from 0.10.1 to 0.10.3 in /tools (#830) -- Bump github.com/stretchr/testify from 1.4.0 to 1.6.1 in /exporters/metric/prometheus (#822) -- Bump google.golang.org/grpc from 1.27.1 to 1.29.1 in /exporters/trace/zipkin (#820) -- Bump google.golang.org/grpc from 1.27.1 to 1.29.1 in /exporters/trace/jaeger (#831) -- Bump github.com/google/go-cmp from 0.4.0 to 0.5.0 (#836) -- Bump github.com/google/go-cmp from 0.4.0 to 0.5.0 in /exporters/trace/jaeger (#837) -- Bump github.com/google/go-cmp from 0.4.0 to 0.5.0 in /exporters/otlp (#839) -- Bump google.golang.org/api from 0.20.0 to 0.28.0 in /exporters/trace/jaeger (#843) -- Set span status from HTTP status code in the othttp instrumentation. (#832) -- Fixed typo in push controller comment. (#834) -- The `Aggregator` testing has been updated and cleaned. (#812) -- `metric.Number(0)` expressions are replaced by `0` where possible. (#812) -- Fixed `global` `handler_test.go` test failure. #804 -- Fixed `BatchSpanProcessor.Shutdown` to wait until all spans are processed. (#766) -- Fixed OTLP example's accidental early close of exporter. (#807) -- Ensure zipkin exporter reads and closes response body. (#788) -- Update instrumentation to use `api/standard` keys instead of custom keys. (#782) -- Clean up tools and RELEASING documentation. (#762) - -## [0.6.0] - 2020-05-21 - -### Added - -- Support for `Resource`s in the prometheus exporter. (#757) -- New pull controller. (#751) -- New `UpDownSumObserver` instrument. (#750) -- OpenTelemetry collector demo. (#711) -- New `SumObserver` instrument. (#747) -- New `UpDownCounter` instrument. (#745) -- New timeout `Option` and configuration function `WithTimeout` to the push controller. (#742) -- New `api/standards` package to implement semantic conventions and standard key-value generation. (#731) - -### Changed - -- Rename `Register*` functions in the metric API to `New*` for all `Observer` instruments. (#761) -- Use `[]float64` for histogram boundaries, not `[]metric.Number`. (#758) -- Change OTLP example to use exporter as a trace `Syncer` instead of as an unneeded `Batcher`. (#756) -- Replace `WithResourceAttributes()` with `WithResource()` in the trace SDK. (#754) -- The prometheus exporter now uses the new pull controller. (#751) -- Rename `ScheduleDelayMillis` to `BatchTimeout` in the trace `BatchSpanProcessor`.(#752) -- Support use of synchronous instruments in asynchronous callbacks (#725) -- Move `Resource` from the `Export` method parameter into the metric export `Record`. (#739) -- Rename `Observer` instrument to `ValueObserver`. (#734) -- The push controller now has a method (`Provider()`) to return a `metric.Provider` instead of the old `Meter` method that acted as a `metric.Provider`. (#738) -- Replace `Measure` instrument by `ValueRecorder` instrument. (#732) -- Rename correlation context header from `"Correlation-Context"` to `"otcorrelations"` to match the OpenTelemetry specification. 727) - -### Fixed - -- Ensure gRPC `ClientStream` override methods do not panic in grpctrace package. (#755) -- Disable parts of `BatchSpanProcessor` test until a fix is found. (#743) -- Fix `string` case in `kv` `Infer` function. (#746) -- Fix panic in grpctrace client interceptors. (#740) -- Refactor the `api/metrics` push controller and add `CheckpointSet` synchronization. (#737) -- Rewrite span batch process queue batching logic. (#719) -- Remove the push controller named Meter map. (#738) -- Fix Histogram aggregator initial state (fix #735). (#736) -- Ensure golang alpine image is running `golang-1.14` for examples. (#733) -- Added test for grpctrace `UnaryInterceptorClient`. (#695) -- Rearrange `api/metric` code layout. (#724) - -## [0.5.0] - 2020-05-13 - -### Added - -- Batch `Observer` callback support. (#717) -- Alias `api` types to root package of project. (#696) -- Create basic `othttp.Transport` for simple client instrumentation. (#678) -- `SetAttribute(string, interface{})` to the trace API. (#674) -- Jaeger exporter option that allows user to specify custom http client. (#671) -- `Stringer` and `Infer` methods to `key`s. (#662) - -### Changed - -- Rename `NewKey` in the `kv` package to just `Key`. (#721) -- Move `core` and `key` to `kv` package. (#720) -- Make the metric API `Meter` a `struct` so the abstract `MeterImpl` can be passed and simplify implementation. (#709) -- Rename SDK `Batcher` to `Integrator` to match draft OpenTelemetry SDK specification. (#710) -- Rename SDK `Ungrouped` integrator to `simple.Integrator` to match draft OpenTelemetry SDK specification. (#710) -- Rename SDK `SDK` `struct` to `Accumulator` to match draft OpenTelemetry SDK specification. (#710) -- Move `Number` from `core` to `api/metric` package. (#706) -- Move `SpanContext` from `core` to `trace` package. (#692) -- Change traceparent header from `Traceparent` to `traceparent` to implement the W3C specification. (#681) - -### Fixed - -- Update tooling to run generators in all submodules. (#705) -- gRPC interceptor regexp to match methods without a service name. (#683) -- Use a `const` for padding 64-bit B3 trace IDs. (#701) -- Update `mockZipkin` listen address from `:0` to `127.0.0.1:0`. (#700) -- Left-pad 64-bit B3 trace IDs with zero. (#698) -- Propagate at least the first W3C tracestate header. (#694) -- Remove internal `StateLocker` implementation. (#688) -- Increase instance size CI system uses. (#690) -- Add a `key` benchmark and use reflection in `key.Infer()`. (#679) -- Fix internal `global` test by using `global.Meter` with `RecordBatch()`. (#680) -- Reimplement histogram using mutex instead of `StateLocker`. (#669) -- Switch `MinMaxSumCount` to a mutex lock implementation instead of `StateLocker`. (#667) -- Update documentation to not include any references to `WithKeys`. (#672) -- Correct misspelling. (#668) -- Fix clobbering of the span context if extraction fails. (#656) -- Bump `golangci-lint` and work around the corrupting bug. (#666) (#670) - -## [0.4.3] - 2020-04-24 - -### Added - -- `Dockerfile` and `docker-compose.yml` to run example code. (#635) -- New `grpctrace` package that provides gRPC client and server interceptors for both unary and stream connections. (#621) -- New `api/label` package, providing common label set implementation. (#651) -- Support for JSON marshaling of `Resources`. (#654) -- `TraceID` and `SpanID` implementations for `Stringer` interface. (#642) -- `RemoteAddrKey` in the othttp plugin to include the HTTP client address in top-level spans. (#627) -- `WithSpanFormatter` option to the othttp plugin. (#617) -- Updated README to include section for compatible libraries and include reference to the contrib repository. (#612) -- The prometheus exporter now supports exporting histograms. (#601) -- A `String` method to the `Resource` to return a hashable identifier for a now unique resource. (#613) -- An `Iter` method to the `Resource` to return an array `AttributeIterator`. (#613) -- An `Equal` method to the `Resource` test the equivalence of resources. (#613) -- An iterable structure (`AttributeIterator`) for `Resource` attributes. - -### Changed - -- zipkin export's `NewExporter` now requires a `serviceName` argument to ensure this needed values is provided. (#644) -- Pass `Resources` through the metrics export pipeline. (#659) - -### Removed - -- `WithKeys` option from the metric API. (#639) - -### Fixed - -- Use the `label.Set.Equivalent` value instead of an encoding in the batcher. (#658) -- Correct typo `trace.Exporter` to `trace.SpanSyncer` in comments. (#653) -- Use type names for return values in jaeger exporter. (#648) -- Increase the visibility of the `api/key` package by updating comments and fixing usages locally. (#650) -- `Checkpoint` only after `Update`; Keep records in the `sync.Map` longer. (#647) -- Do not cache `reflect.ValueOf()` in metric Labels. (#649) -- Batch metrics exported from the OTLP exporter based on `Resource` and labels. (#626) -- Add error wrapping to the prometheus exporter. (#631) -- Update the OTLP exporter batching of traces to use a unique `string` representation of an associated `Resource` as the batching key. (#623) -- Update OTLP `SpanData` transform to only include the `ParentSpanID` if one exists. (#614) -- Update `Resource` internal representation to uniquely and reliably identify resources. (#613) -- Check return value from `CheckpointSet.ForEach` in prometheus exporter. (#622) -- Ensure spans created by httptrace client tracer reflect operation structure. (#618) -- Create a new recorder rather than reuse when multiple observations in same epoch for asynchronous instruments. #610 -- The default port the OTLP exporter uses to connect to the OpenTelemetry collector is updated to match the one the collector listens on by default. (#611) - - -## [0.4.2] - 2020-03-31 - -### Fixed - -- Fix `pre_release.sh` to update version in `sdk/opentelemetry.go`. (#607) -- Fix time conversion from internal to OTLP in OTLP exporter. (#606) - -## [0.4.1] - 2020-03-31 - -### Fixed - -- Update `tag.sh` to create signed tags. (#604) - -## [0.4.0] - 2020-03-30 - -### Added - -- New API package `api/metric/registry` that exposes a `MeterImpl` wrapper for use by SDKs to generate unique instruments. (#580) -- Script to verify examples after a new release. (#579) - -### Removed - -- The dogstatsd exporter due to lack of support. - This additionally removes support for statsd. (#591) -- `LabelSet` from the metric API. - This is replaced by a `[]core.KeyValue` slice. (#595) -- `Labels` from the metric API's `Meter` interface. (#595) - -### Changed - -- The metric `export.Labels` became an interface which the SDK implements and the `export` package provides a simple, immutable implementation of this interface intended for testing purposes. (#574) -- Renamed `internal/metric.Meter` to `MeterImpl`. (#580) -- Renamed `api/global/internal.obsImpl` to `asyncImpl`. (#580) - -### Fixed - -- Corrected missing return in mock span. (#582) -- Update License header for all source files to match CNCF guidelines and include a test to ensure it is present. (#586) (#596) -- Update to v0.3.0 of the OTLP in the OTLP exporter. (#588) -- Update pre-release script to be compatible between GNU and BSD based systems. (#592) -- Add a `RecordBatch` benchmark. (#594) -- Moved span transforms of the OTLP exporter to the internal package. (#593) -- Build both go-1.13 and go-1.14 in circleci to test for all supported versions of Go. (#569) -- Removed unneeded allocation on empty labels in OLTP exporter. (#597) -- Update `BatchedSpanProcessor` to process the queue until no data but respect max batch size. (#599) -- Update project documentation godoc.org links to pkg.go.dev. (#602) - -## [0.3.0] - 2020-03-21 - -This is a first official beta release, which provides almost fully complete metrics, tracing, and context propagation functionality. -There is still a possibility of breaking changes. - -### Added - -- Add `Observer` metric instrument. (#474) -- Add global `Propagators` functionality to enable deferred initialization for propagators registered before the first Meter SDK is installed. (#494) -- Simplified export setup pipeline for the jaeger exporter to match other exporters. (#459) -- The zipkin trace exporter. (#495) -- The OTLP exporter to export metric and trace telemetry to the OpenTelemetry collector. (#497) (#544) (#545) -- The `StatusMessage` field was add to the trace `Span`. (#524) -- Context propagation in OpenTracing bridge in terms of OpenTelemetry context propagation. (#525) -- The `Resource` type was added to the SDK. (#528) -- The global API now supports a `Tracer` and `Meter` function as shortcuts to getting a global `*Provider` and calling these methods directly. (#538) -- The metric API now defines a generic `MeterImpl` interface to support general purpose `Meter` construction. - Additionally, `SyncImpl` and `AsyncImpl` are added to support general purpose instrument construction. (#560) -- A metric `Kind` is added to represent the `MeasureKind`, `ObserverKind`, and `CounterKind`. (#560) -- Scripts to better automate the release process. (#576) - -### Changed - -- Default to to use `AlwaysSampler` instead of `ProbabilitySampler` to match OpenTelemetry specification. (#506) -- Renamed `AlwaysSampleSampler` to `AlwaysOnSampler` in the trace API. (#511) -- Renamed `NeverSampleSampler` to `AlwaysOffSampler` in the trace API. (#511) -- The `Status` field of the `Span` was changed to `StatusCode` to disambiguate with the added `StatusMessage`. (#524) -- Updated the trace `Sampler` interface conform to the OpenTelemetry specification. (#531) -- Rename metric API `Options` to `Config`. (#541) -- Rename metric `Counter` aggregator to be `Sum`. (#541) -- Unify metric options into `Option` from instrument specific options. (#541) -- The trace API's `TraceProvider` now support `Resource`s. (#545) -- Correct error in zipkin module name. (#548) -- The jaeger trace exporter now supports `Resource`s. (#551) -- Metric SDK now supports `Resource`s. - The `WithResource` option was added to configure a `Resource` on creation and the `Resource` method was added to the metric `Descriptor` to return the associated `Resource`. (#552) -- Replace `ErrNoLastValue` and `ErrEmptyDataSet` by `ErrNoData` in the metric SDK. (#557) -- The stdout trace exporter now supports `Resource`s. (#558) -- The metric `Descriptor` is now included at the API instead of the SDK. (#560) -- Replace `Ordered` with an iterator in `export.Labels`. (#567) - -### Removed - -- The vendor specific Stackdriver. It is now hosted on 3rd party vendor infrastructure. (#452) -- The `Unregister` method for metric observers as it is not in the OpenTelemetry specification. (#560) -- `GetDescriptor` from the metric SDK. (#575) -- The `Gauge` instrument from the metric API. (#537) - -### Fixed - -- Make histogram aggregator checkpoint consistent. (#438) -- Update README with import instructions and how to build and test. (#505) -- The default label encoding was updated to be unique. (#508) -- Use `NewRoot` in the othttp plugin for public endpoints. (#513) -- Fix data race in `BatchedSpanProcessor`. (#518) -- Skip test-386 for Mac OS 10.15.x (Catalina and upwards). #521 -- Use a variable-size array to represent ordered labels in maps. (#523) -- Update the OTLP protobuf and update changed import path. (#532) -- Use `StateLocker` implementation in `MinMaxSumCount`. (#546) -- Eliminate goroutine leak in histogram stress test. (#547) -- Update OTLP exporter with latest protobuf. (#550) -- Add filters to the othttp plugin. (#556) -- Provide an implementation of the `Header*` filters that do not depend on Go 1.14. (#565) -- Encode labels once during checkpoint. - The checkpoint function is executed in a single thread so we can do the encoding lazily before passing the encoded version of labels to the exporter. - This is a cheap and quick way to avoid encoding the labels on every collection interval. (#572) -- Run coverage over all packages in `COVERAGE_MOD_DIR`. (#573) - -## [0.2.3] - 2020-03-04 - -### Added - -- `RecordError` method on `Span`s in the trace API to Simplify adding error events to spans. (#473) -- Configurable push frequency for exporters setup pipeline. (#504) - -### Changed - -- Rename the `exporter` directory to `exporters`. - The `go.opentelemetry.io/otel/exporter/trace/jaeger` package was mistakenly released with a `v1.0.0` tag instead of `v0.1.0`. - This resulted in all subsequent releases not becoming the default latest. - A consequence of this was that all `go get`s pulled in the incompatible `v0.1.0` release of that package when pulling in more recent packages from other otel packages. - Renaming the `exporter` directory to `exporters` fixes this issue by renaming the package and therefore clearing any existing dependency tags. - Consequentially, this action also renames *all* exporter packages. (#502) - -### Removed - -- The `CorrelationContextHeader` constant in the `correlation` package is no longer exported. (#503) - -## [0.2.2] - 2020-02-27 - -### Added - -- `HTTPSupplier` interface in the propagation API to specify methods to retrieve and store a single value for a key to be associated with a carrier. (#467) -- `HTTPExtractor` interface in the propagation API to extract information from an `HTTPSupplier` into a context. (#467) -- `HTTPInjector` interface in the propagation API to inject information into an `HTTPSupplier.` (#467) -- `Config` and configuring `Option` to the propagator API. (#467) -- `Propagators` interface in the propagation API to contain the set of injectors and extractors for all supported carrier formats. (#467) -- `HTTPPropagator` interface in the propagation API to inject and extract from an `HTTPSupplier.` (#467) -- `WithInjectors` and `WithExtractors` functions to the propagator API to configure injectors and extractors to use. (#467) -- `ExtractHTTP` and `InjectHTTP` functions to apply configured HTTP extractors and injectors to a passed context. (#467) -- Histogram aggregator. (#433) -- `DefaultPropagator` function and have it return `trace.TraceContext` as the default context propagator. (#456) -- `AlwaysParentSample` sampler to the trace API. (#455) -- `WithNewRoot` option function to the trace API to specify the created span should be considered a root span. (#451) - - -### Changed - -- Renamed `WithMap` to `ContextWithMap` in the correlation package. (#481) -- Renamed `FromContext` to `MapFromContext` in the correlation package. (#481) -- Move correlation context propagation to correlation package. (#479) -- Do not default to putting remote span context into links. (#480) -- Propagators extrac -- `Tracer.WithSpan` updated to accept `StartOptions`. (#472) -- Renamed `MetricKind` to `Kind` to not stutter in the type usage. (#432) -- Renamed the `export` package to `metric` to match directory structure. (#432) -- Rename the `api/distributedcontext` package to `api/correlation`. (#444) -- Rename the `api/propagators` package to `api/propagation`. (#444) -- Move the propagators from the `propagators` package into the `trace` API package. (#444) -- Update `Float64Gauge`, `Int64Gauge`, `Float64Counter`, `Int64Counter`, `Float64Measure`, and `Int64Measure` metric methods to use value receivers instead of pointers. (#462) -- Moved all dependencies of tools package to a tools directory. (#466) - -### Removed - -- Binary propagators. (#467) -- NOOP propagator. (#467) - -### Fixed - -- Upgraded `github.com/golangci/golangci-lint` from `v1.21.0` to `v1.23.6` in `tools/`. (#492) -- Fix a possible nil-dereference crash (#478) -- Correct comments for `InstallNewPipeline` in the stdout exporter. (#483) -- Correct comments for `InstallNewPipeline` in the dogstatsd exporter. (#484) -- Correct comments for `InstallNewPipeline` in the prometheus exporter. (#482) -- Initialize `onError` based on `Config` in prometheus exporter. (#486) -- Correct module name in prometheus exporter README. (#475) -- Removed tracer name prefix from span names. (#430) -- Fix `aggregator_test.go` import package comment. (#431) -- Improved detail in stdout exporter. (#436) -- Fix a dependency issue (generate target should depend on stringer, not lint target) in Makefile. (#442) -- Reorders the Makefile targets within `precommit` target so we generate files and build the code before doing linting, so we can get much nicer errors about syntax errors from the compiler. (#442) -- Reword function documentation in gRPC plugin. (#446) -- Send the `span.kind` tag to Jaeger from the jaeger exporter. (#441) -- Fix `metadataSupplier` in the jaeger exporter to overwrite the header if existing instead of appending to it. (#441) -- Upgraded to Go 1.13 in CI. (#465) -- Correct opentelemetry.io URL in trace SDK documentation. (#464) -- Refactored reference counting logic in SDK determination of stale records. (#468) -- Add call to `runtime.Gosched` in instrument `acquireHandle` logic to not block the collector. (#469) - -## [0.2.1.1] - 2020-01-13 - -### Fixed - -- Use stateful batcher on Prometheus exporter fixing regresion introduced in #395. (#428) - -## [0.2.1] - 2020-01-08 - -### Added - -- Global meter forwarding implementation. - This enables deferred initialization for metric instruments registered before the first Meter SDK is installed. (#392) -- Global trace forwarding implementation. - This enables deferred initialization for tracers registered before the first Trace SDK is installed. (#406) -- Standardize export pipeline creation in all exporters. (#395) -- A testing, organization, and comments for 64-bit field alignment. (#418) -- Script to tag all modules in the project. (#414) - -### Changed - -- Renamed `propagation` package to `propagators`. (#362) -- Renamed `B3Propagator` propagator to `B3`. (#362) -- Renamed `TextFormatPropagator` propagator to `TextFormat`. (#362) -- Renamed `BinaryPropagator` propagator to `Binary`. (#362) -- Renamed `BinaryFormatPropagator` propagator to `BinaryFormat`. (#362) -- Renamed `NoopTextFormatPropagator` propagator to `NoopTextFormat`. (#362) -- Renamed `TraceContextPropagator` propagator to `TraceContext`. (#362) -- Renamed `SpanOption` to `StartOption` in the trace API. (#369) -- Renamed `StartOptions` to `StartConfig` in the trace API. (#369) -- Renamed `EndOptions` to `EndConfig` in the trace API. (#369) -- `Number` now has a pointer receiver for its methods. (#375) -- Renamed `CurrentSpan` to `SpanFromContext` in the trace API. (#379) -- Renamed `SetCurrentSpan` to `ContextWithSpan` in the trace API. (#379) -- Renamed `Message` in Event to `Name` in the trace API. (#389) -- Prometheus exporter no longer aggregates metrics, instead it only exports them. (#385) -- Renamed `HandleImpl` to `BoundInstrumentImpl` in the metric API. (#400) -- Renamed `Float64CounterHandle` to `Float64CounterBoundInstrument` in the metric API. (#400) -- Renamed `Int64CounterHandle` to `Int64CounterBoundInstrument` in the metric API. (#400) -- Renamed `Float64GaugeHandle` to `Float64GaugeBoundInstrument` in the metric API. (#400) -- Renamed `Int64GaugeHandle` to `Int64GaugeBoundInstrument` in the metric API. (#400) -- Renamed `Float64MeasureHandle` to `Float64MeasureBoundInstrument` in the metric API. (#400) -- Renamed `Int64MeasureHandle` to `Int64MeasureBoundInstrument` in the metric API. (#400) -- Renamed `Release` method for bound instruments in the metric API to `Unbind`. (#400) -- Renamed `AcquireHandle` method for bound instruments in the metric API to `Bind`. (#400) -- Renamed the `File` option in the stdout exporter to `Writer`. (#404) -- Renamed all `Options` to `Config` for all metric exports where this wasn't already the case. - -### Fixed - -- Aggregator import path corrected. (#421) -- Correct links in README. (#368) -- The README was updated to match latest code changes in its examples. (#374) -- Don't capitalize error statements. (#375) -- Fix ignored errors. (#375) -- Fix ambiguous variable naming. (#375) -- Removed unnecessary type casting. (#375) -- Use named parameters. (#375) -- Updated release schedule. (#378) -- Correct http-stackdriver example module name. (#394) -- Removed the `http.request` span in `httptrace` package. (#397) -- Add comments in the metrics SDK (#399) -- Initialize checkpoint when creating ddsketch aggregator to prevent panic when merging into a empty one. (#402) (#403) -- Add documentation of compatible exporters in the README. (#405) -- Typo fix. (#408) -- Simplify span check logic in SDK tracer implementation. (#419) - -## [0.2.0] - 2019-12-03 - -### Added - -- Unary gRPC tracing example. (#351) -- Prometheus exporter. (#334) -- Dogstatsd metrics exporter. (#326) - -### Changed - -- Rename `MaxSumCount` aggregation to `MinMaxSumCount` and add the `Min` interface for this aggregation. (#352) -- Rename `GetMeter` to `Meter`. (#357) -- Rename `HTTPTraceContextPropagator` to `TraceContextPropagator`. (#355) -- Rename `HTTPB3Propagator` to `B3Propagator`. (#355) -- Rename `HTTPTraceContextPropagator` to `TraceContextPropagator`. (#355) -- Move `/global` package to `/api/global`. (#356) -- Rename `GetTracer` to `Tracer`. (#347) - -### Removed - -- `SetAttribute` from the `Span` interface in the trace API. (#361) -- `AddLink` from the `Span` interface in the trace API. (#349) -- `Link` from the `Span` interface in the trace API. (#349) - -### Fixed - -- Exclude example directories from coverage report. (#365) -- Lint make target now implements automatic fixes with `golangci-lint` before a second run to report the remaining issues. (#360) -- Drop `GO111MODULE` environment variable in Makefile as Go 1.13 is the project specified minimum version and this is environment variable is not needed for that version of Go. (#359) -- Run the race checker for all test. (#354) -- Redundant commands in the Makefile are removed. (#354) -- Split the `generate` and `lint` targets of the Makefile. (#354) -- Renames `circle-ci` target to more generic `ci` in Makefile. (#354) -- Add example Prometheus binary to gitignore. (#358) -- Support negative numbers with the `MaxSumCount`. (#335) -- Resolve race conditions in `push_test.go` identified in #339. (#340) -- Use `/usr/bin/env bash` as a shebang in scripts rather than `/bin/bash`. (#336) -- Trace benchmark now tests both `AlwaysSample` and `NeverSample`. - Previously it was testing `AlwaysSample` twice. (#325) -- Trace benchmark now uses a `[]byte` for `TraceID` to fix failing test. (#325) -- Added a trace benchmark to test variadic functions in `setAttribute` vs `setAttributes` (#325) -- The `defaultkeys` batcher was only using the encoded label set as its map key while building a checkpoint. - This allowed distinct label sets through, but any metrics sharing a label set could be overwritten or merged incorrectly. - This was corrected. (#333) - - -## [0.1.2] - 2019-11-18 - -### Fixed - -- Optimized the `simplelru` map for attributes to reduce the number of allocations. (#328) -- Removed unnecessary unslicing of parameters that are already a slice. (#324) - -## [0.1.1] - 2019-11-18 - -This release contains a Metrics SDK with stdout exporter and supports basic aggregations such as counter, gauges, array, maxsumcount, and ddsketch. - -### Added - -- Metrics stdout export pipeline. (#265) -- Array aggregation for raw measure metrics. (#282) -- The core.Value now have a `MarshalJSON` method. (#281) - -### Removed - -- `WithService`, `WithResources`, and `WithComponent` methods of tracers. (#314) -- Prefix slash in `Tracer.Start()` for the Jaeger example. (#292) - -### Changed - -- Allocation in LabelSet construction to reduce GC overhead. (#318) -- `trace.WithAttributes` to append values instead of replacing (#315) -- Use a formula for tolerance in sampling tests. (#298) -- Move export types into trace and metric-specific sub-directories. (#289) -- `SpanKind` back to being based on an `int` type. (#288) - -### Fixed - -- URL to OpenTelemetry website in README. (#323) -- Name of othttp default tracer. (#321) -- `ExportSpans` for the stackdriver exporter now handles `nil` context. (#294) -- CI modules cache to correctly restore/save from/to the cache. (#316) -- Fix metric SDK race condition between `LoadOrStore` and the assignment `rec.recorder = i.meter.exporter.AggregatorFor(rec)`. (#293) -- README now reflects the new code structure introduced with these changes. (#291) -- Make the basic example work. (#279) - -## [0.1.0] - 2019-11-04 - -This is the first release of open-telemetry go library. -It contains api and sdk for trace and meter. - -### Added - -- Initial OpenTelemetry trace and metric API prototypes. -- Initial OpenTelemetry trace, metric, and export SDK packages. -- A wireframe bridge to support compatibility with OpenTracing. -- Example code for a basic, http-stackdriver, http, jaeger, and named tracer setup. -- Exporters for Jaeger, Stackdriver, and stdout. -- Propagators for binary, B3, and trace-context protocols. -- Project information and guidelines in the form of a README and CONTRIBUTING. -- Tools to build the project and a Makefile to automate the process. -- Apache-2.0 license. -- CircleCI build CI manifest files. -- CODEOWNERS file to track owners of this project. - - -[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v0.19.0...HEAD -[0.19.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.19.0 -[0.18.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.18.0 -[0.17.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.17.0 -[0.16.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.16.0 -[0.15.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.15.0 -[0.14.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.14.0 -[0.13.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.13.0 -[0.12.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.12.0 -[0.11.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.11.0 -[0.10.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.10.0 -[0.9.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.9.0 -[0.8.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.8.0 -[0.7.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.7.0 -[0.6.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.6.0 -[0.5.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.5.0 -[0.4.3]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.4.3 -[0.4.2]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.4.2 -[0.4.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.4.1 -[0.4.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.4.0 -[0.3.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.3.0 -[0.2.3]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.2.3 -[0.2.2]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.2.2 -[0.2.1.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.2.1.1 -[0.2.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.2.1 -[0.2.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.2.0 -[0.1.2]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.1.2 -[0.1.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.1.1 -[0.1.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.1.0 diff --git a/vendor/go.opentelemetry.io/otel/CODEOWNERS b/vendor/go.opentelemetry.io/otel/CODEOWNERS deleted file mode 100644 index 6c50189a46dc2..0000000000000 --- a/vendor/go.opentelemetry.io/otel/CODEOWNERS +++ /dev/null @@ -1,17 +0,0 @@ -##################################################### -# -# List of approvers for this repository -# -##################################################### -# -# Learn about membership in OpenTelemetry community: -# https://github.com/open-telemetry/community/blob/main/community-membership.md -# -# -# Learn about CODEOWNERS file format: -# https://help.github.com/en/articles/about-code-owners -# - -* @jmacd @lizthegrey @MrAlias @Aneurysm9 @evantorrie @XSAM @dashpole - -CODEOWNERS @MrAlias @Aneurysm9 diff --git a/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md b/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md deleted file mode 100644 index c604065a85849..0000000000000 --- a/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md +++ /dev/null @@ -1,380 +0,0 @@ -# Contributing to opentelemetry-go - -The Go special interest group (SIG) meets regularly. See the -OpenTelemetry -[community](https://github.com/open-telemetry/community#golang-sdk) -repo for information on this and other language SIGs. - -See the [public meeting -notes](https://docs.google.com/document/d/1A63zSWX0x2CyCK_LoNhmQC4rqhLpYXJzXbEPDUQ2n6w/edit#heading=h.9tngw7jdwd6b) -for a summary description of past meetings. To request edit access, -join the meeting or get in touch on -[Slack](https://cloud-native.slack.com/archives/C01NPAXACKT). - -## Development - -You can view and edit the source code by cloning this repository: - -```bash -git clone https://github.com/open-telemetry/opentelemetry-go.git -``` - -Run `make test` to run the tests instead of `go test`. - -There are some generated files checked into the repo. To make sure -that the generated files are up-to-date, run `make` (or `make -precommit` - the `precommit` target is the default). - -The `precommit` target also fixes the formatting of the code and -checks the status of the go module files. - -If after running `make precommit` the output of `git status` contains -`nothing to commit, working tree clean` then it means that everything -is up-to-date and properly formatted. - -## Pull Requests - -### How to Send Pull Requests - -Everyone is welcome to contribute code to `opentelemetry-go` via -GitHub pull requests (PRs). - -To create a new PR, fork the project in GitHub and clone the upstream -repo: - -```sh -$ go get -d go.opentelemetry.io/otel -``` - -(This may print some warning about "build constraints exclude all Go -files", just ignore it.) - -This will put the project in `${GOPATH}/src/go.opentelemetry.io/otel`. You -can alternatively use `git` directly with: - -```sh -$ git clone https://github.com/open-telemetry/opentelemetry-go -``` - -(Note that `git clone` is *not* using the `go.opentelemetry.io/otel` name - -that name is a kind of a redirector to GitHub that `go get` can -understand, but `git` does not.) - -This would put the project in the `opentelemetry-go` directory in -current working directory. - -Enter the newly created directory and add your fork as a new remote: - -```sh -$ git remote add git@github.com:/opentelemetry-go -``` - -Check out a new branch, make modifications, run linters and tests, update -`CHANGELOG.md`, and push the branch to your fork: - -```sh -$ git checkout -b -# edit files -# update changelog -$ make precommit -$ git add -p -$ git commit -$ git push -``` - -Open a pull request against the main `opentelemetry-go` repo. Be sure to add the pull -request ID to the entry you added to `CHANGELOG.md`. - -### How to Receive Comments - -* If the PR is not ready for review, please put `[WIP]` in the title, - tag it as `work-in-progress`, or mark it as - [`draft`](https://github.blog/2019-02-14-introducing-draft-pull-requests/). -* Make sure CLA is signed and CI is clear. - -### How to Get PRs Merged - -A PR is considered to be **ready to merge** when: - -* It has received two approvals from Collaborators/Maintainers (at - different companies). This is not enforced through technical means - and a PR may be **ready to merge** with a single approval if the change - and its approach have been discussed and consensus reached. -* Feedback has been addressed. -* Any substantive changes to your PR will require that you clear any prior - Approval reviews, this includes changes resulting from other feedback. Unless - the approver explicitly stated that their approval will persist across - changes it should be assumed that the PR needs their review again. Other - project members (e.g. approvers, maintainers) can help with this if there are - any questions or if you forget to clear reviews. -* It has been open for review for at least one working day. This gives - people reasonable time to review. -* Trivial changes (typo, cosmetic, doc, etc.) do not have to wait for - one day and may be merged with a single Maintainer's approval. -* `CHANGELOG.md` has been updated to reflect what has been - added, changed, removed, or fixed. -* Urgent fix can take exception as long as it has been actively - communicated. - -Any Maintainer can merge the PR once it is **ready to merge**. - -## Design Choices - -As with other OpenTelemetry clients, opentelemetry-go follows the -[opentelemetry-specification](https://github.com/open-telemetry/opentelemetry-specification). - -It's especially valuable to read through the [library -guidelines](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/library-guidelines.md). - -### Focus on Capabilities, Not Structure Compliance - -OpenTelemetry is an evolving specification, one where the desires and -use cases are clear, but the method to satisfy those uses cases are -not. - -As such, Contributions should provide functionality and behavior that -conforms to the specification, but the interface and structure is -flexible. - -It is preferable to have contributions follow the idioms of the -language rather than conform to specific API names or argument -patterns in the spec. - -For a deeper discussion, see: -https://github.com/open-telemetry/opentelemetry-specification/issues/165 - -## Style Guide - -One of the primary goals of this project is that it is actually used by -developers. With this goal in mind the project strives to build -user-friendly and idiomatic Go code adhering to the Go community's best -practices. - -For a non-comprehensive but foundational overview of these best practices -the [Effective Go](https://golang.org/doc/effective_go.html) documentation -is an excellent starting place. - -As a convenience for developers building this project the `make precommit` -will format, lint, validate, and in some cases fix the changes you plan to -submit. This check will need to pass for your changes to be able to be -merged. - -In addition to idiomatic Go, the project has adopted certain standards for -implementations of common patterns. These standards should be followed as a -default, and if they are not followed documentation needs to be included as -to the reasons why. - -### Configuration - -When creating an instantiation function for a complex `struct` it is useful -to allow variable number of options to be applied. However, the strong type -system of Go restricts the function design options. There are a few ways to -solve this problem, but we have landed on the following design. - -#### `config` - -Configuration should be held in a `struct` named `config`, or prefixed with -specific type name this Configuration applies to if there are multiple -`config` in the package. This `struct` must contain configuration options. - -```go -// config contains configuration options for a thing. -type config struct { - // options ... -} -``` - -In general the `config` `struct` will not need to be used externally to the -package and should be unexported. If, however, it is expected that the user -will likely want to build custom options for the configuration, the `config` -should be exported. Please, include in the documentation for the `config` -how the user can extend the configuration. - -It is important that `config` are not shared across package boundaries. -Meaning a `config` from one package should not be directly used by another. - -Optionally, it is common to include a `newConfig` function (with the same -naming scheme). This function wraps any defaults setting and looping over -all options to create a configured `config`. - -```go -// newConfig returns an appropriately configured config. -func newConfig([]Option) config { - // Set default values for config. - config := config{/* […] */} - for _, option := range options { - option.Apply(&config) - } - // Preform any validation here. - return config -} -``` - -If validation of the `config` options is also preformed this can return an -error as well that is expected to be handled by the instantiation function -or propagated to the user. - -Given the design goal of not having the user need to work with the `config`, -the `newConfig` function should also be unexported. - -#### `Option` - -To set the value of the options a `config` contains, a corresponding -`Option` interface type should be used. - -```go -type Option interface { - Apply(*config) -} -``` - -The name of the interface should be prefixed in the same way the -corresponding `config` is (if at all). - -#### Options - -All user configurable options for a `config` must have a related unexported -implementation of the `Option` interface and an exported configuration -function that wraps this implementation. - -The wrapping function name should be prefixed with `With*` (or in the -special case of a boolean options `Without*`) and should have the following -function signature. - -```go -func With*(…) Option { … } -``` - -##### `bool` Options - -```go -type defaultFalseOption bool - -func (o defaultFalseOption) Apply(c *config) { - c.Bool = bool(o) -} - -// WithOption sets a T* to have an option included. -func WithOption() Option { - return defaultFalseOption(true) -} -``` - -```go -type defaultTrueOption bool - -func (o defaultTrueOption) Apply(c *config) { - c.Bool = bool(o) -} - -// WithoutOption sets a T* to have Bool option excluded. -func WithoutOption() Option { - return defaultTrueOption(false) -} -```` - -##### Declared Type Options - -```go -type myTypeOption struct { - MyType MyType -} - -func (o myTypeOption) Apply(c *config) { - c.MyType = o.MyType -} - -// WithMyType sets T* to have include MyType. -func WithMyType(t MyType) Option { - return myTypeOption{t} -} -``` - -#### Instantiation - -Using this configuration pattern to configure instantiation with a `New*` -function. - -```go -func NewT*(options ...Option) T* {…} -``` - -Any required parameters can be declared before the variadic `options`. - -#### Dealing with Overlap - -Sometimes there are multiple complex `struct` that share common -configuration and also have distinct configuration. To avoid repeated -portions of `config`s, a common `config` can be used with the union of -options being handled with the `Option` interface. - -For example. - -```go -// config holds options for all animals. -type config struct { - Weight float64 - Color string - MaxAltitude float64 -} - -// DogOption apply Dog specific options. -type DogOption interface { - ApplyDog(*config) -} - -// BirdOption apply Bird specific options. -type BirdOption interface { - ApplyBird(*config) -} - -// Option apply options for all animals. -type Option interface { - BirdOption - DogOption -} - -type weightOption float64 -func (o weightOption) ApplyDog(c *config) { c.Weight = float64(o) } -func (o weightOption) ApplyBird(c *config) { c.Weight = float64(o) } -func WithWeight(w float64) Option { return weightOption(w) } - -type furColorOption string -func (o furColorOption) ApplyDog(c *config) { c.Color = string(o) } -func WithFurColor(c string) DogOption { return furColorOption(c) } - -type maxAltitudeOption float64 -func (o maxAltitudeOption) ApplyBird(c *config) { c.MaxAltitude = float64(o) } -func WithMaxAltitude(a float64) BirdOption { return maxAltitudeOption(a) } - -func NewDog(name string, o ...DogOption) Dog {…} -func NewBird(name string, o ...BirdOption) Bird {…} -``` - -### Interface Type - -To allow other developers to better comprehend the code, it is important -to ensure it is sufficiently documented. One simple measure that contributes -to this aim is self-documenting by naming method parameters. Therefore, -where appropriate, methods of every exported interface type should have -their parameters appropriately named. - -## Approvers and Maintainers - -Approvers: - -- [Liz Fong-Jones](https://github.com/lizthegrey), Honeycomb -- [Evan Torrie](https://github.com/evantorrie), Verizon Media -- [Josh MacDonald](https://github.com/jmacd), LightStep -- [Sam Xie](https://github.com/XSAM) -- [David Ashpole](https://github.com/dashpole), Google - -Maintainers: - -- [Anthony Mirabella](https://github.com/Aneurysm9), AWS -- [Tyler Yahn](https://github.com/MrAlias), Splunk - -### Become an Approver or a Maintainer - -See the [community membership document in OpenTelemetry community -repo](https://github.com/open-telemetry/community/blob/main/community-membership.md). diff --git a/vendor/go.opentelemetry.io/otel/Makefile b/vendor/go.opentelemetry.io/otel/Makefile deleted file mode 100644 index b290b667101aa..0000000000000 --- a/vendor/go.opentelemetry.io/otel/Makefile +++ /dev/null @@ -1,179 +0,0 @@ -# Copyright The OpenTelemetry 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. - -EXAMPLES := $(shell ./get_main_pkgs.sh ./example) -TOOLS_MOD_DIR := ./internal/tools - -# All source code and documents. Used in spell check. -ALL_DOCS := $(shell find . -name '*.md' -type f | sort) -# All directories with go.mod files related to opentelemetry library. Used for building, testing and linting. -ALL_GO_MOD_DIRS := $(filter-out $(TOOLS_MOD_DIR), $(shell find . -type f -name 'go.mod' -exec dirname {} \; | egrep -v '^./example' | sort)) $(shell find ./example -type f -name 'go.mod' -exec dirname {} \; | sort) -ALL_COVERAGE_MOD_DIRS := $(shell find . -type f -name 'go.mod' -exec dirname {} \; | egrep -v '^./example|^$(TOOLS_MOD_DIR)' | sort) - -GO = go -TIMEOUT = 60 - -.DEFAULT_GOAL := precommit - -.PHONY: precommit ci -precommit: dependabot-check license-check lint build examples test-default -ci: precommit check-clean-work-tree test-coverage - -# Tools - -TOOLS = $(CURDIR)/.tools - -$(TOOLS): - @mkdir -p $@ -$(TOOLS)/%: | $(TOOLS) - cd $(TOOLS_MOD_DIR) && \ - $(GO) build -o $@ $(PACKAGE) - -CROSSLINK = $(TOOLS)/crosslink -$(TOOLS)/crosslink: PACKAGE=go.opentelemetry.io/otel/$(TOOLS_MOD_DIR)/crosslink - -GOLANGCI_LINT = $(TOOLS)/golangci-lint -$(TOOLS)/golangci-lint: PACKAGE=github.com/golangci/golangci-lint/cmd/golangci-lint - -MISSPELL = $(TOOLS)/misspell -$(TOOLS)/misspell: PACKAGE= github.com/client9/misspell/cmd/misspell - -STRINGER = $(TOOLS)/stringer -$(TOOLS)/stringer: PACKAGE=golang.org/x/tools/cmd/stringer - -$(TOOLS)/gojq: PACKAGE=github.com/itchyny/gojq/cmd/gojq - -.PHONY: tools -tools: $(CROSSLINK) $(GOLANGCI_LINT) $(MISSPELL) $(STRINGER) $(TOOLS)/gojq - - -# Build - -.PHONY: examples generate build -examples: - @set -e; for dir in $(EXAMPLES); do \ - echo "$(GO) build $${dir}/..."; \ - (cd "$${dir}" && \ - $(GO) build .); \ - done - -generate: $(STRINGER) - set -e; for dir in $(ALL_GO_MOD_DIRS); do \ - echo "$(GO) generate $${dir}/..."; \ - (cd "$${dir}" && \ - PATH="$(TOOLS):$${PATH}" $(GO) generate ./...); \ - done - -build: generate - # Build all package code including testing code. - set -e; for dir in $(ALL_GO_MOD_DIRS); do \ - echo "$(GO) build $${dir}/..."; \ - (cd "$${dir}" && \ - $(GO) build ./... && \ - $(GO) list ./... \ - | grep -v third_party \ - | xargs $(GO) test -vet=off -run xxxxxMatchNothingxxxxx >/dev/null); \ - done - -# Tests - -TEST_TARGETS := test-default test-bench test-short test-verbose test-race -.PHONY: $(TEST_TARGETS) test -test-default: ARGS=-v -race -test-bench: ARGS=-run=xxxxxMatchNothingxxxxx -test.benchtime=1ms -bench=. -test-short: ARGS=-short -test-verbose: ARGS=-v -test-race: ARGS=-race -$(TEST_TARGETS): test -test: - @set -e; for dir in $(ALL_GO_MOD_DIRS); do \ - echo "$(GO) test -timeout $(TIMEOUT)s $(ARGS) $${dir}/..."; \ - (cd "$${dir}" && \ - $(GO) list ./... \ - | grep -v third_party \ - | xargs $(GO) test -timeout $(TIMEOUT)s $(ARGS)); \ - done - -COVERAGE_MODE = atomic -COVERAGE_PROFILE = coverage.out -.PHONY: test-coverage -test-coverage: - @set -e; \ - printf "" > coverage.txt; \ - for dir in $(ALL_COVERAGE_MOD_DIRS); do \ - echo "$(GO) test -coverpkg=./... -covermode=$(COVERAGE_MODE) -coverprofile="$(COVERAGE_PROFILE)" $${dir}/..."; \ - (cd "$${dir}" && \ - $(GO) list ./... \ - | grep -v third_party \ - | xargs $(GO) test -coverpkg=./... -covermode=$(COVERAGE_MODE) -coverprofile="$(COVERAGE_PROFILE)" && \ - $(GO) tool cover -html=coverage.out -o coverage.html); \ - [ -f "$${dir}/coverage.out" ] && cat "$${dir}/coverage.out" >> coverage.txt; \ - done; \ - sed -i.bak -e '2,$$ { /^mode: /d; }' coverage.txt - -.PHONY: lint -lint: misspell lint-modules | $(GOLANGCI_LINT) - set -e; for dir in $(ALL_GO_MOD_DIRS); do \ - echo "golangci-lint in $${dir}"; \ - (cd "$${dir}" && \ - $(GOLANGCI_LINT) run --fix && \ - $(GOLANGCI_LINT) run); \ - done - -.PHONY: misspell -misspell: | $(MISSPELL) - $(MISSPELL) -w $(ALL_DOCS) - -.PHONY: lint-modules -lint-modules: | $(CROSSLINK) - set -e; for dir in $(ALL_GO_MOD_DIRS) $(TOOLS_MOD_DIR); do \ - echo "$(GO) mod tidy in $${dir}"; \ - (cd "$${dir}" && \ - $(GO) mod tidy); \ - done - echo "cross-linking all go modules" - $(CROSSLINK) - -.PHONY: license-check -license-check: - @licRes=$$(for f in $$(find . -type f \( -iname '*.go' -o -iname '*.sh' \) ! -path '**/third_party/*' ! -path './exporters/otlp/internal/opentelemetry-proto/*') ; do \ - awk '/Copyright The OpenTelemetry Authors|generated|GENERATED/ && NR<=3 { found=1; next } END { if (!found) print FILENAME }' $$f; \ - done); \ - if [ -n "$${licRes}" ]; then \ - echo "license header checking failed:"; echo "$${licRes}"; \ - exit 1; \ - fi - -.PHONY: dependabot-check -dependabot-check: - @result=$$( \ - for f in $$( find . -type f -name go.mod -exec dirname {} \; | sed 's/^.\/\?/\//' ); \ - do grep -q "$$f" .github/dependabot.yml \ - || echo "$$f"; \ - done; \ - ); \ - if [ -n "$$result" ]; then \ - echo "missing go.mod dependabot check:"; echo "$$result"; \ - exit 1; \ - fi - -.PHONY: check-clean-work-tree -check-clean-work-tree: - @if ! git diff --quiet; then \ - echo; \ - echo 'Working tree is not clean, did you forget to run "make precommit"?'; \ - echo; \ - git status; \ - exit 1; \ - fi diff --git a/vendor/go.opentelemetry.io/otel/Makefile.protos b/vendor/go.opentelemetry.io/otel/Makefile.protos deleted file mode 100644 index 16edc57ee823c..0000000000000 --- a/vendor/go.opentelemetry.io/otel/Makefile.protos +++ /dev/null @@ -1,129 +0,0 @@ -# -*- mode: makefile; -*- -# Copyright The OpenTelemetry 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. -# -# This Makefile.protos has rules to generate go code for otlp -# exporter. It does it by copying the proto files from -# `exporters/otlp/internal/opentelemetry-proto` (which is a -# submodule that needs to be checked out) into `gen/proto`, changing -# the go_package option to a valid string, generating the go files and -# finally copying the files into the module. The files are not -# generated in place, because protoc generates a too-deep directory -# structure. -# -# Currently, all the generated code is in -# `exporters/otlp/internal/opentelemetry-proto-gen`. -# -# Prereqs: wget (for downloading the zip file with protoc binary), -# unzip (for unpacking the archive), rsync (for copying back the -# generated files). - -PROTOC_VERSION := 3.14.0 - -TOOLS_DIR := $(abspath ./.tools) -TOOLS_MOD_DIR := ./internal/tools -PROTOBUF_VERSION := v1 -OTEL_PROTO_SUBMODULE := exporters/otlp/internal/opentelemetry-proto -GEN_TEMP_DIR := gen -SUBMODULE_PROTO_FILES := $(wildcard $(OTEL_PROTO_SUBMODULE)/opentelemetry/proto/*/$(PROTOBUF_VERSION)/*.proto) $(wildcard $(OTEL_PROTO_SUBMODULE)/opentelemetry/proto/collector/*/$(PROTOBUF_VERSION)/*.proto) - -ifeq ($(strip $(SUBMODULE_PROTO_FILES)),) -$(error Submodule at $(OTEL_PROTO_SUBMODULE) is not checked out, use "git submodule update --init") -endif - -PROTOBUF_GEN_DIR := exporters/otlp/internal/opentelemetry-proto-gen -PROTOBUF_TEMP_DIR := $(GEN_TEMP_DIR)/pb-go -PROTO_SOURCE_DIR := $(GEN_TEMP_DIR)/proto -SOURCE_PROTO_FILES := $(subst $(OTEL_PROTO_SUBMODULE),$(PROTO_SOURCE_DIR),$(SUBMODULE_PROTO_FILES)) - -.DEFAULT_GOAL := protobuf - -UNAME_S := $(shell uname -s) -UNAME_M := $(shell uname -m) - -ifeq ($(UNAME_S),Linux) - -PROTOC_OS := linux -PROTOC_ARCH := $(UNAME_M) - -else ifeq ($(UNAME_S),Darwin) - -PROTOC_OS := osx -PROTOC_ARCH := x86_64 - -endif - -PROTOC_ZIP_URL := https://github.com/protocolbuffers/protobuf/releases/download/v$(PROTOC_VERSION)/protoc-$(PROTOC_VERSION)-$(PROTOC_OS)-$(PROTOC_ARCH).zip - -$(TOOLS_DIR)/PROTOC_$(PROTOC_VERSION): - @rm -f "$(TOOLS_DIR)"/PROTOC_* && \ - touch "$@" - -# Depend on a versioned file (like PROTOC_3.14.0), so when version -# gets bumped, we will depend on a nonexistent file and thus download -# a newer version. -$(TOOLS_DIR)/protoc/bin/protoc: $(TOOLS_DIR)/PROTOC_$(PROTOC_VERSION) - echo "Fetching protoc $(PROTOC_VERSION)" && \ - rm -rf $(TOOLS_DIR)/protoc && \ - wget -O $(TOOLS_DIR)/protoc.zip $(PROTOC_ZIP_URL) && \ - unzip $(TOOLS_DIR)/protoc.zip -d $(TOOLS_DIR)/protoc-tmp && \ - rm $(TOOLS_DIR)/protoc.zip && \ - touch $(TOOLS_DIR)/protoc-tmp/bin/protoc && \ - mv $(TOOLS_DIR)/protoc-tmp $(TOOLS_DIR)/protoc - -$(TOOLS_DIR)/protoc-gen-gogofast: $(TOOLS_MOD_DIR)/go.mod $(TOOLS_MOD_DIR)/go.sum $(TOOLS_MOD_DIR)/tools.go - cd $(TOOLS_MOD_DIR) && \ - go build -o $(TOOLS_DIR)/protoc-gen-gogofast github.com/gogo/protobuf/protoc-gen-gogofast && \ - go mod tidy - -# Return a sed expression for replacing the go_package option in proto -# file with a one that's valid for us. -# -# Example: $(call get-sed-expr,$(PROTOBUF_GEN_DIR)) -define get-sed-expr -'s,go_package = "github.com/open-telemetry/opentelemetry-proto/gen/go,go_package = "go.opentelemetry.io/otel/$(1),' -endef - -.PHONY: protobuf -protobuf: protobuf-source gen-protobuf copy-protobufs - -.PHONY: protobuf-source -protobuf-source: $(SOURCE_PROTO_FILES) - -# This copies proto files from submodule into $(PROTO_SOURCE_DIR), -# thus satisfying the $(SOURCE_PROTO_FILES) prerequisite. The copies -# have their package name replaced by go.opentelemetry.io/otel. -$(PROTO_SOURCE_DIR)/%.proto: $(OTEL_PROTO_SUBMODULE)/%.proto - @ \ - mkdir -p $(@D); \ - sed -e $(call get-sed-expr,$(PROTOBUF_GEN_DIR)) "$<" >"$@.tmp"; \ - mv "$@.tmp" "$@" - -.PHONY: gen-protobuf -gen-protobuf: $(SOURCE_PROTO_FILES) $(TOOLS_DIR)/protoc-gen-gogofast $(TOOLS_DIR)/protoc/bin/protoc - @ \ - mkdir -p "$(PROTOBUF_TEMP_DIR)"; \ - set -e; for f in $^; do \ - if [[ "$${f}" == $(TOOLS_DIR)/* ]]; then continue; fi; \ - echo "protoc $${f#"$(PROTO_SOURCE_DIR)/"}"; \ - PATH="$(TOOLS_DIR):$${PATH}" $(TOOLS_DIR)/protoc/bin/protoc --proto_path="$(PROTO_SOURCE_DIR)" --gogofast_out="plugins=grpc:$(PROTOBUF_TEMP_DIR)" "$${f}"; \ - done - -.PHONY: copy-protobufs -copy-protobufs: - @rsync -a $(PROTOBUF_TEMP_DIR)/go.opentelemetry.io/otel/exporters . - -.PHONY: clean -clean: - rm -rf $(GEN_TEMP_DIR) diff --git a/vendor/go.opentelemetry.io/otel/README.md b/vendor/go.opentelemetry.io/otel/README.md deleted file mode 100644 index c841ba896e560..0000000000000 --- a/vendor/go.opentelemetry.io/otel/README.md +++ /dev/null @@ -1,92 +0,0 @@ -# OpenTelemetry-Go - -[![CI](https://github.com/open-telemetry/opentelemetry-go/workflows/ci/badge.svg)](https://github.com/open-telemetry/opentelemetry-go/actions?query=workflow%3Aci+branch%3Amain) -[![PkgGoDev](https://pkg.go.dev/badge/go.opentelemetry.io/otel)](https://pkg.go.dev/go.opentelemetry.io/otel) -[![Go Report Card](https://goreportcard.com/badge/go.opentelemetry.io/otel)](https://goreportcard.com/report/go.opentelemetry.io/otel) -[![Slack](https://img.shields.io/badge/slack-@cncf/otel--go-brightgreen.svg?logo=slack)](https://cloud-native.slack.com/archives/C01NPAXACKT) - - -The Go [OpenTelemetry](https://opentelemetry.io/) implementation. - -## Project Status - -**Warning**: this project is currently in a pre-GA phase. Backwards -incompatible changes may be introduced in subsequent minor version releases as -we work to track the evolving OpenTelemetry specification and user feedback. - -Our progress towards a GA release candidate is tracked in [this project -board](https://github.com/orgs/open-telemetry/projects/5). This release -candidate will follow semantic versioning and will be released with a major -version greater than zero. - -Progress and status specific to this repository is tracked in our local -[project boards](https://github.com/open-telemetry/opentelemetry-go/projects) -and -[milestones](https://github.com/open-telemetry/opentelemetry-go/milestones). - -Project versioning information and stability guarantees can be found in the -[versioning documentation](./VERSIONING.md). - -### Compatibility - -This project is tested on the following systems. - -| OS | Go Version | Architecture | -| ------- | ---------- | ------------ | -| Ubuntu | 1.15 | amd64 | -| Ubuntu | 1.14 | amd64 | -| Ubuntu | 1.15 | 386 | -| Ubuntu | 1.14 | 386 | -| MacOS | 1.15 | amd64 | -| MacOS | 1.14 | amd64 | -| Windows | 1.15 | amd64 | -| Windows | 1.14 | amd64 | -| Windows | 1.15 | 386 | -| Windows | 1.14 | 386 | - -While this project should work for other systems, no compatibility guarantees -are made for those systems currently. - -## Getting Started - -You can find a getting started guide on [opentelemetry.io](https://opentelemetry.io/docs/go/getting-started/). - -OpenTelemetry's goal is to provide a single set of APIs to capture distributed -traces and metrics from your application and send them to an observability -platform. This project allows you to do just that for applications written in -Go. There are two steps to this process: instrument your application, and -configure an exporter. - -### Instrumentation - -To start capturing distributed traces and metric events from your application -it first needs to be instrumented. The easiest way to do this is by using an -instrumentation library for your code. Be sure to check out [the officially -supported instrumentation -libraries](https://github.com/open-telemetry/opentelemetry-go-contrib/tree/main/instrumentation). - -If you need to extend the telemetry an instrumentation library provides or want -to build your own instrumentation for your application directly you will need -to use the -[go.opentelemetry.io/otel/api](https://pkg.go.dev/go.opentelemetry.io/otel/api) -package. The included [examples](./example/) are a good way to see some -practical uses of this process. - -### Export - -Now that your application is instrumented to collect telemetry, it needs an -export pipeline to send that telemetry to an observability platform. - -You can find officially supported exporters [here](./exporters/) and in the -companion [contrib -repository](https://github.com/open-telemetry/opentelemetry-go-contrib/tree/main/exporters/metric). -Additionally, there are many vendor specific or 3rd party exporters for -OpenTelemetry. These exporters are broken down by -[trace](https://pkg.go.dev/go.opentelemetry.io/otel/sdk/export/trace?tab=importedby) -and -[metric](https://pkg.go.dev/go.opentelemetry.io/otel/sdk/export/metric?tab=importedby) -support. - -## Contributing - -See the [contributing documentation](CONTRIBUTING.md). diff --git a/vendor/go.opentelemetry.io/otel/RELEASING.md b/vendor/go.opentelemetry.io/otel/RELEASING.md deleted file mode 100644 index 71d23b47a5461..0000000000000 --- a/vendor/go.opentelemetry.io/otel/RELEASING.md +++ /dev/null @@ -1,81 +0,0 @@ -# Release Process - -## Pre-Release - -Update go.mod for submodules to depend on the new release which will happen in the next step. - -1. Run the pre-release script. It creates a branch `pre_release_` that will contain all release changes. - - ``` - ./pre_release.sh -t - ``` - -2. Verify the changes. - - ``` - git diff main - ``` - - This should have changed the version for all modules to be ``. - -3. Update the [Changelog](./CHANGELOG.md). - - Make sure all relevant changes for this release are included and are in language that non-contributors to the project can understand. - To verify this, you can look directly at the commits since the ``. - - ``` - git --no-pager log --pretty=oneline "..HEAD" - ``` - - - Move all the `Unreleased` changes into a new section following the title scheme (`[] - `). - - Update all the appropriate links at the bottom. - -4. Push the changes to upstream and create a Pull Request on GitHub. - Be sure to include the curated changes from the [Changelog](./CHANGELOG.md) in the description. - - -## Tag - -Once the Pull Request with all the version changes has been approved and merged it is time to tag the merged commit. - -***IMPORTANT***: It is critical you use the same tag that you used in the Pre-Release step! -Failure to do so will leave things in a broken state. - -***IMPORTANT***: [There is currently no way to remove an incorrectly tagged version of a Go module](https://github.com/golang/go/issues/34189). -It is critical you make sure the version you push upstream is correct. -[Failure to do so will lead to minor emergencies and tough to work around](https://github.com/open-telemetry/opentelemetry-go/issues/331). - -1. Run the tag.sh script using the `` of the commit on the main branch for the merged Pull Request. - - ``` - ./tag.sh - ``` - -2. Push tags to the upstream remote (not your fork: `github.com/open-telemetry/opentelemetry-go.git`). - Make sure you push all sub-modules as well. - - ``` - git push upstream - git push upstream - ... - ``` - -## Release - -Finally create a Release for the new `` on GitHub. -The release body should include all the release notes from the Changelog for this release. -Additionally, the `tag.sh` script generates commit logs since last release which can be used to supplement the release notes. - -## Verify Examples - -After releasing verify that examples build outside of the repository. - -``` -./verify_examples.sh -``` - -The script copies examples into a different directory removes any `replace` declarations in `go.mod` and builds them. -This ensures they build with the published release, not the local copy. - -## Contrib Repository - -Once verified be sure to [make a release for the `contrib` repository](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/RELEASING.md) that uses this release. diff --git a/vendor/go.opentelemetry.io/otel/VERSIONING.md b/vendor/go.opentelemetry.io/otel/VERSIONING.md deleted file mode 100644 index 3579b794ee972..0000000000000 --- a/vendor/go.opentelemetry.io/otel/VERSIONING.md +++ /dev/null @@ -1,217 +0,0 @@ -# Versioning - -This document describes the versioning policy for this repository. This policy -is designed so the following goals can be achieved. - -**Users are provided a codebase of value that is stable and secure.** - -## Policy - -* Versioning of this project will be idiomatic of a Go project using [Go - modules](https://github.com/golang/go/wiki/Modules). - * [Semantic import - versioning](https://github.com/golang/go/wiki/Modules#semantic-import-versioning) - will be used. - * Versions will comply with [semver 2.0](https://semver.org/spec/v2.0.0.html). - * If a module is version `v2` or higher, the major version of the module - must be included as a `/vN` at the end of the module paths used in - `go.mod` files (e.g., `module go.opentelemetry.io/otel/v2`, `require - go.opentelemetry.io/otel/v2 v2.0.1`) and in the package import path - (e.g., `import "go.opentelemetry.io/otel/v2/trace"`). This includes the - paths used in `go get` commands (e.g., `go get - go.opentelemetry.io/otel/v2@v2.0.1`. Note there is both a `/v2` and a - `@v2.0.1` in that example. One way to think about it is that the module - name now includes the `/v2`, so include `/v2` whenever you are using the - module name). - * If a module is version `v0` or `v1`, do not include the major version in - either the module path or the import path. - * Modules will be used to encapsulate signals and components. - * Experimental modules still under active development will be versioned at - `v0` to imply the stability guarantee defined by - [semver](https://semver.org/spec/v2.0.0.html#spec-item-4). - - > Major version zero (0.y.z) is for initial development. Anything MAY - > change at any time. The public API SHOULD NOT be considered stable. - - * Mature modules for which we guarantee a stable public API will be versioned - with a major version greater than `v0`. - * The decision to make a module stable will be made on a case-by-case - basis by the maintainers of this project. - * Experimental modules will start their versioning at `v0.0.0` and will - increment their minor version when backwards incompatible changes are - released and increment their patch version when backwards compatible - changes are released. - * All stable modules that use the same major version number will use the - same entire version number. - * Stable modules may be released with an incremented minor or patch - version even though that module has not been changed, but rather so - that it will remain at the same version as other stable modules that - did undergo change. - * When an experimental module becomes stable a new stable module version - will be released and will include this now stable module. The new - stable module version will be an increment of the minor version number - and will be applied to all existing stable modules as well as the newly - stable module being released. -* Versioning of the associated [contrib - repository](https://github.com/open-telemetry/opentelemetry-go-contrib) of - this project will be idiomatic of a Go project using [Go - modules](https://github.com/golang/go/wiki/Modules). - * [Semantic import - versioning](https://github.com/golang/go/wiki/Modules#semantic-import-versioning) - will be used. - * Versions will comply with [semver 2.0](https://semver.org/spec/v2.0.0.html). - * If a module is version `v2` or higher, the - major version of the module must be included as a `/vN` at the end of the - module paths used in `go.mod` files (e.g., `module - go.opentelemetry.io/contrib/instrumentation/host/v2`, `require - go.opentelemetry.io/contrib/instrumentation/host/v2 v2.0.1`) and in the - package import path (e.g., `import - "go.opentelemetry.io/contrib/instrumentation/host/v2"`). This includes - the paths used in `go get` commands (e.g., `go get - go.opentelemetry.io/contrib/instrumentation/host/v2@v2.0.1`. Note there - is both a `/v2` and a `@v2.0.1` in that example. One way to think about - it is that the module name now includes the `/v2`, so include `/v2` - whenever you are using the module name). - * If a module is version `v0` or `v1`, do not include the major version - in either the module path or the import path. - * In addition to public APIs, telemetry produced by stable instrumentation - will remain stable and backwards compatible. This is to avoid breaking - alerts and dashboard. - * Modules will be used to encapsulate instrumentation, detectors, exporters, - propagators, and any other independent sets of related components. - * Experimental modules still under active development will be versioned at - `v0` to imply the stability guarantee defined by - [semver](https://semver.org/spec/v2.0.0.html#spec-item-4). - - > Major version zero (0.y.z) is for initial development. Anything MAY - > change at any time. The public API SHOULD NOT be considered stable. - - * Mature modules for which we guarantee a stable public API and telemetry will - be versioned with a major version greater than `v0`. - * Experimental modules will start their versioning at `v0.0.0` and will - increment their minor version when backwards incompatible changes are - released and increment their patch version when backwards compatible - changes are released. - * Stable contrib modules cannot depend on experimental modules from this - project. - * All stable contrib modules of the same major version with this project - will use the same entire version as this project. - * Stable modules may be released with an incremented minor or patch - version even though that module's code has not been changed. Instead - the only change that will have been included is to have updated that - modules dependency on this project's stable APIs. - * When an experimental module in contrib becomes stable a new stable - module version will be released and will include this now stable - module. The new stable module version will be an increment of the minor - version number and will be applied to all existing stable contrib - modules, this project's modules, and the newly stable module being - released. - * Contrib modules will be kept up to date with this project's releases. - * Due to the dependency contrib modules will implicitly have on this - project's modules the release of stable contrib modules to match the - released version number will be staggered after this project's release. - There is no explicit time guarantee for how long after this projects - release the contrib release will be. Effort should be made to keep them - as close in time as possible. - * No additional stable release in this project can be made until the - contrib repository has a matching stable release. - * No release can be made in the contrib repository after this project's - stable release except for a stable release of the contrib repository. -* GitHub releases will be made for all releases. -* Go modules will be made available at Go package mirrors. - -## Example Versioning Lifecycle - -To better understand the implementation of the above policy the following -example is provided. This project is simplified to include only the following -modules and their versions: - -* `otel`: `v0.14.0` -* `otel/trace`: `v0.14.0` -* `otel/metric`: `v0.14.0` -* `otel/baggage`: `v0.14.0` -* `otel/sdk/trace`: `v0.14.0` -* `otel/sdk/metric`: `v0.14.0` - -These modules have been developed to a point where the `otel/trace`, -`otel/baggage`, and `otel/sdk/trace` modules have reached a point that they -should be considered for a stable release. The `otel/metric` and -`otel/sdk/metric` are still under active development and the `otel` module -depends on both `otel/trace` and `otel/metric`. - -The `otel` package is refactored to remove its dependencies on `otel/metric` so -it can be released as stable as well. With that done the following release -candidates are made: - -* `otel`: `v1.0.0-rc.1` -* `otel/trace`: `v1.0.0-rc.1` -* `otel/baggage`: `v1.0.0-rc.1` -* `otel/sdk/trace`: `v1.0.0-rc.1` - -The `otel/metric` and `otel/sdk/metric` modules remain at `v0.14.0`. - -A few minor issues are discovered in the `otel/trace` package. These issues are -resolved with some minor, but backwards incompatible, changes and are released -as a second release candidate: - -* `otel`: `v1.0.0-rc.2` -* `otel/trace`: `v1.0.0-rc.2` -* `otel/baggage`: `v1.0.0-rc.2` -* `otel/sdk/trace`: `v1.0.0-rc.2` - -Notice that all module version numbers are incremented to adhere to our -versioning policy. - -After these release candidates have been evaluated to satisfaction, they are -released as version `v1.0.0`. - -* `otel`: `v1.0.0` -* `otel/trace`: `v1.0.0` -* `otel/baggage`: `v1.0.0` -* `otel/sdk/trace`: `v1.0.0` - -Since both the `go` utility and the Go module system support [the semantic -versioning definition of -precedence](https://semver.org/spec/v2.0.0.html#spec-item-11), this release -will correctly be interpreted as the successor to the previous release -candidates. - -Active development of this project continues. The `otel/metric` module now has -backwards incompatible changes to its API that need to be released and the -`otel/baggage` module has a minor bug fix that needs to be released. The -following release is made: - -* `otel`: `v1.0.1` -* `otel/trace`: `v1.0.1` -* `otel/metric`: `v0.15.0` -* `otel/baggage`: `v1.0.1` -* `otel/sdk/trace`: `v1.0.1` -* `otel/sdk/metric`: `v0.15.0` - -Notice that, again, all stable module versions are incremented in unison and -the `otel/sdk/metric` package, which depends on the `otel/metric` package, also -bumped its version. This bump of the `otel/sdk/metric` package makes sense -given their coupling, though it is not explicitly required by our versioning -policy. - -As we progress, the `otel/metric` and `otel/sdk/metric` packages have reached a -point where they should be evaluated for stability. The `otel` module is -reintegrated with the `otel/metric` package and the following release is made: - -* `otel`: `v1.1.0-rc.1` -* `otel/trace`: `v1.1.0-rc.1` -* `otel/metric`: `v1.1.0-rc.1` -* `otel/baggage`: `v1.1.0-rc.1` -* `otel/sdk/trace`: `v1.1.0-rc.1` -* `otel/sdk/metric`: `v1.1.0-rc.1` - -All the modules are evaluated and determined to a viable stable release. They -are then released as version `v1.1.0` (the minor version is incremented to -indicate the addition of new signal). - -* `otel`: `v1.1.0` -* `otel/trace`: `v1.1.0` -* `otel/metric`: `v1.1.0` -* `otel/baggage`: `v1.1.0` -* `otel/sdk/trace`: `v1.1.0` -* `otel/sdk/metric`: `v1.1.0` diff --git a/vendor/go.opentelemetry.io/otel/attribute/value.go b/vendor/go.opentelemetry.io/otel/attribute/value.go index 44ef795cb7898..7b979409e3001 100644 --- a/vendor/go.opentelemetry.io/otel/attribute/value.go +++ b/vendor/go.opentelemetry.io/otel/attribute/value.go @@ -45,7 +45,7 @@ const ( BOOL // INT64 is a 64-bit signed integral Type Value. INT64 - // UINT32 is a 32-bit unsigned integral Type Value. + // FLOAT64 is a 64-bit floating point Type Value. FLOAT64 // STRING is a string Type Value. STRING diff --git a/vendor/go.opentelemetry.io/otel/doc.go b/vendor/go.opentelemetry.io/otel/doc.go deleted file mode 100644 index 771ce81cc2f0c..0000000000000 --- a/vendor/go.opentelemetry.io/otel/doc.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright The OpenTelemetry 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 otel provides global access to the OpenTelemetry API. The subpackages of -the otel package provide an implementation of the OpenTelemetry API. - -This package is currently in a pre-GA phase. Backwards incompatible changes -may be introduced in subsequent minor version releases as we work to track the -evolving OpenTelemetry specification and user feedback. - -The provided API is used to instrument code and measure data about that code's -performance and operation. The measured data, by default, is not processed or -transmitted anywhere. An implementation of the OpenTelemetry SDK, like the -default SDK implementation (go.opentelemetry.io/otel/sdk), and associated -exporters are used to process and transport this data. - -To read the getting started guide, see https://opentelemetry.io/docs/go/getting-started/. - -To read more about tracing, see go.opentelemetry.io/otel/trace. - -To read more about metrics, see go.opentelemetry.io/otel/metric. - -To read more about propagation, see go.opentelemetry.io/otel/propagation and -go.opentelemetry.io/otel/baggage. -*/ -package otel // import "go.opentelemetry.io/otel" diff --git a/vendor/go.opentelemetry.io/otel/error_handler.go b/vendor/go.opentelemetry.io/otel/error_handler.go deleted file mode 100644 index ac42f8be0723a..0000000000000 --- a/vendor/go.opentelemetry.io/otel/error_handler.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright The OpenTelemetry 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 otel // import "go.opentelemetry.io/otel" - -// ErrorHandler handles irremediable events. -type ErrorHandler interface { - // Handle handles any error deemed irremediable by an OpenTelemetry - // component. - Handle(error) -} diff --git a/vendor/go.opentelemetry.io/otel/get_main_pkgs.sh b/vendor/go.opentelemetry.io/otel/get_main_pkgs.sh deleted file mode 100644 index 9a58fb1d372ea..0000000000000 --- a/vendor/go.opentelemetry.io/otel/get_main_pkgs.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash - -# Copyright The OpenTelemetry 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. - -set -euo pipefail - -top_dir='.' -if [[ $# -gt 0 ]]; then - top_dir="${1}" -fi - -p=$(pwd) -mod_dirs=() - -# Note `mapfile` does not exist in older bash versions: -# https://stackoverflow.com/questions/41475261/need-alternative-to-readarray-mapfile-for-script-on-older-version-of-bash - -while IFS= read -r line; do - mod_dirs+=("$line") -done < <(find "${top_dir}" -type f -name 'go.mod' -exec dirname {} \; | sort) - -for mod_dir in "${mod_dirs[@]}"; do - cd "${mod_dir}" - - while IFS= read -r line; do - echo ".${line#${p}}" - done < <(go list --find -f '{{.Name}}|{{.Dir}}' ./... | grep '^main|' | cut -f 2- -d '|') - cd "${p}" -done diff --git a/vendor/go.opentelemetry.io/otel/go.mod b/vendor/go.opentelemetry.io/otel/go.mod deleted file mode 100644 index a6a36d88b24b8..0000000000000 --- a/vendor/go.opentelemetry.io/otel/go.mod +++ /dev/null @@ -1,55 +0,0 @@ -module go.opentelemetry.io/otel - -go 1.14 - -require ( - github.com/google/go-cmp v0.5.5 - github.com/stretchr/testify v1.7.0 - go.opentelemetry.io/otel/metric v0.19.0 - go.opentelemetry.io/otel/oteltest v0.19.0 - go.opentelemetry.io/otel/trace v0.19.0 -) - -replace go.opentelemetry.io/otel => ./ - -replace go.opentelemetry.io/otel/bridge/opencensus => ./bridge/opencensus - -replace go.opentelemetry.io/otel/bridge/opentracing => ./bridge/opentracing - -replace go.opentelemetry.io/otel/example/jaeger => ./example/jaeger - -replace go.opentelemetry.io/otel/example/namedtracer => ./example/namedtracer - -replace go.opentelemetry.io/otel/example/opencensus => ./example/opencensus - -replace go.opentelemetry.io/otel/example/otel-collector => ./example/otel-collector - -replace go.opentelemetry.io/otel/example/prom-collector => ./example/prom-collector - -replace go.opentelemetry.io/otel/example/prometheus => ./example/prometheus - -replace go.opentelemetry.io/otel/example/zipkin => ./example/zipkin - -replace go.opentelemetry.io/otel/exporters/metric/prometheus => ./exporters/metric/prometheus - -replace go.opentelemetry.io/otel/exporters/otlp => ./exporters/otlp - -replace go.opentelemetry.io/otel/exporters/stdout => ./exporters/stdout - -replace go.opentelemetry.io/otel/exporters/trace/jaeger => ./exporters/trace/jaeger - -replace go.opentelemetry.io/otel/exporters/trace/zipkin => ./exporters/trace/zipkin - -replace go.opentelemetry.io/otel/internal/tools => ./internal/tools - -replace go.opentelemetry.io/otel/sdk => ./sdk - -replace go.opentelemetry.io/otel/metric => ./metric - -replace go.opentelemetry.io/otel/oteltest => ./oteltest - -replace go.opentelemetry.io/otel/sdk/export/metric => ./sdk/export/metric - -replace go.opentelemetry.io/otel/sdk/metric => ./sdk/metric - -replace go.opentelemetry.io/otel/trace => ./trace diff --git a/vendor/go.opentelemetry.io/otel/go.sum b/vendor/go.opentelemetry.io/otel/go.sum deleted file mode 100644 index b69f2e56da0f5..0000000000000 --- a/vendor/go.opentelemetry.io/otel/go.sum +++ /dev/null @@ -1,15 +0,0 @@ -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/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -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.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -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/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/vendor/go.opentelemetry.io/otel/handler.go b/vendor/go.opentelemetry.io/otel/handler.go deleted file mode 100644 index 27e1caa30d9f7..0000000000000 --- a/vendor/go.opentelemetry.io/otel/handler.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright The OpenTelemetry 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 otel // import "go.opentelemetry.io/otel" - -import ( - "log" - "os" - "sync" - "sync/atomic" -) - -var ( - // globalErrorHandler provides an ErrorHandler that can be used - // throughout an OpenTelemetry instrumented project. When a user - // specified ErrorHandler is registered (`SetErrorHandler`) all calls to - // `Handle` and will be delegated to the registered ErrorHandler. - globalErrorHandler = &loggingErrorHandler{ - l: log.New(os.Stderr, "", log.LstdFlags), - } - - // delegateErrorHandlerOnce ensures that a user provided ErrorHandler is - // only ever registered once. - delegateErrorHandlerOnce sync.Once - - // Comiple time check that loggingErrorHandler implements ErrorHandler. - _ ErrorHandler = (*loggingErrorHandler)(nil) -) - -// loggingErrorHandler logs all errors to STDERR. -type loggingErrorHandler struct { - delegate atomic.Value - - l *log.Logger -} - -// setDelegate sets the ErrorHandler delegate if one is not already set. -func (h *loggingErrorHandler) setDelegate(d ErrorHandler) { - if h.delegate.Load() != nil { - // Delegate already registered - return - } - h.delegate.Store(d) -} - -// Handle implements ErrorHandler. -func (h *loggingErrorHandler) Handle(err error) { - if d := h.delegate.Load(); d != nil { - d.(ErrorHandler).Handle(err) - return - } - h.l.Print(err) -} - -// GetErrorHandler returns the global ErrorHandler instance. If no ErrorHandler -// instance has been set (`SetErrorHandler`), the default ErrorHandler which -// logs errors to STDERR is returned. -func GetErrorHandler() ErrorHandler { - return globalErrorHandler -} - -// SetErrorHandler sets the global ErrorHandler to be h. -func SetErrorHandler(h ErrorHandler) { - delegateErrorHandlerOnce.Do(func() { - current := GetErrorHandler() - if current == h { - return - } - if internalHandler, ok := current.(*loggingErrorHandler); ok { - internalHandler.setDelegate(h) - } - }) -} - -// Handle is a convience function for ErrorHandler().Handle(err) -func Handle(err error) { - GetErrorHandler().Handle(err) -} diff --git a/vendor/go.opentelemetry.io/otel/metric/go.mod b/vendor/go.opentelemetry.io/otel/metric/go.mod index 687e9a168d2f2..47bc47badde64 100644 --- a/vendor/go.opentelemetry.io/otel/metric/go.mod +++ b/vendor/go.opentelemetry.io/otel/metric/go.mod @@ -49,6 +49,6 @@ replace go.opentelemetry.io/otel/trace => ../trace require ( github.com/google/go-cmp v0.5.5 github.com/stretchr/testify v1.7.0 - go.opentelemetry.io/otel v0.19.0 - go.opentelemetry.io/otel/oteltest v0.19.0 + go.opentelemetry.io/otel v0.20.0 + go.opentelemetry.io/otel/oteltest v0.20.0 ) diff --git a/vendor/go.opentelemetry.io/otel/pre_release.sh b/vendor/go.opentelemetry.io/otel/pre_release.sh deleted file mode 100644 index 0de22169cfcb2..0000000000000 --- a/vendor/go.opentelemetry.io/otel/pre_release.sh +++ /dev/null @@ -1,95 +0,0 @@ -#!/bin/bash - -# Copyright The OpenTelemetry 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. - -set -e - -help() -{ - printf "\n" - printf "Usage: $0 -t tag\n" - printf "\t-t Unreleased tag. Update all go.mod with this tag.\n" - exit 1 # Exit script after printing help -} - -while getopts "t:" opt -do - case "$opt" in - t ) TAG="$OPTARG" ;; - ? ) help ;; # Print help - esac -done - -# Print help in case parameters are empty -if [ -z "$TAG" ] -then - printf "Tag is missing\n"; - help -fi - -# Validate semver -SEMVER_REGEX="^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\-[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?(\\+[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?$" -if [[ "${TAG}" =~ ${SEMVER_REGEX} ]]; then - printf "${TAG} is valid semver tag.\n" -else - printf "${TAG} is not a valid semver tag.\n" - exit -1 -fi - -TAG_FOUND=`git tag --list ${TAG}` -if [[ ${TAG_FOUND} = ${TAG} ]] ; then - printf "Tag ${TAG} already exists\n" - exit -1 -fi - -# Get version for version.go -OTEL_VERSION=$(echo "${TAG}" | grep -o '^v[0-9]\+\.[0-9]\+\.[0-9]\+') -# Strip leading v -OTEL_VERSION="${OTEL_VERSION#v}" - -cd $(dirname $0) - -if ! git diff --quiet; then \ - printf "Working tree is not clean, can't proceed with the release process\n" - git status - git diff - exit 1 -fi - -# Update version.go -cp ./version.go ./version.go.bak -sed "s/\(return \"\)[0-9]*\.[0-9]*\.[0-9]*\"/\1${OTEL_VERSION}\"/" ./version.go.bak >./version.go -rm -f ./version.go.bak - -# Update go.mod -git checkout -b pre_release_${TAG} main -PACKAGE_DIRS=$(find . -mindepth 2 -type f -name 'go.mod' -exec dirname {} \; | egrep -v 'tools' | sed 's/^\.\///' | sort) - -for dir in $PACKAGE_DIRS; do - cp "${dir}/go.mod" "${dir}/go.mod.bak" - sed "s/opentelemetry.io\/otel\([^ ]*\) v[0-9]*\.[0-9]*\.[0-9]/opentelemetry.io\/otel\1 ${TAG}/" "${dir}/go.mod.bak" >"${dir}/go.mod" - rm -f "${dir}/go.mod.bak" -done - -# Run lint to update go.sum -make lint - -# Add changes and commit. -git add . -make ci -git commit -m "Prepare for releasing $TAG" - -printf "Now run following to verify the changes.\ngit diff main\n" -printf "\nThen push the changes to upstream\n" diff --git a/vendor/go.opentelemetry.io/otel/propagation.go b/vendor/go.opentelemetry.io/otel/propagation.go deleted file mode 100644 index d29aaa32c0b5b..0000000000000 --- a/vendor/go.opentelemetry.io/otel/propagation.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright The OpenTelemetry 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 otel // import "go.opentelemetry.io/otel" - -import ( - "go.opentelemetry.io/otel/internal/global" - "go.opentelemetry.io/otel/propagation" -) - -// GetTextMapPropagator returns the global TextMapPropagator. If none has been -// set, a No-Op TextMapPropagator is returned. -func GetTextMapPropagator() propagation.TextMapPropagator { - return global.TextMapPropagator() -} - -// SetTextMapPropagator sets propagator as the global TextMapPropagator. -func SetTextMapPropagator(propagator propagation.TextMapPropagator) { - global.SetTextMapPropagator(propagator) -} diff --git a/vendor/go.opentelemetry.io/otel/propagation/trace_context.go b/vendor/go.opentelemetry.io/otel/propagation/trace_context.go index cb38cf416c2ec..82de416bea64b 100644 --- a/vendor/go.opentelemetry.io/otel/propagation/trace_context.go +++ b/vendor/go.opentelemetry.io/otel/propagation/trace_context.go @@ -54,15 +54,22 @@ func (tc TraceContext) Inject(ctx context.Context, carrier TextMapCarrier) { carrier.Set(tracestateHeader, sc.TraceState().String()) - h := fmt.Sprintf("%.2x-%s-%s-%.2x", + // Clear all flags other than the trace-context supported sampling bit. + flags := sc.TraceFlags() & trace.FlagsSampled + + h := fmt.Sprintf("%.2x-%s-%s-%s", supportedVersion, sc.TraceID(), sc.SpanID(), - sc.TraceFlags()&trace.FlagsSampled) + flags) carrier.Set(traceparentHeader, h) } // Extract reads tracecontext from the carrier into a returned Context. +// +// The returned Context will be a copy of ctx and contain the extracted +// tracecontext as the remote SpanContext. If the extracted tracecontext is +// invalid, the passed ctx will be returned directly instead. func (tc TraceContext) Extract(ctx context.Context, carrier TextMapCarrier) context.Context { sc := tc.extract(carrier) if !sc.IsValid() { @@ -130,7 +137,7 @@ func (tc TraceContext) extract(carrier TextMapCarrier) trace.SpanContext { return trace.SpanContext{} } // Clear all flags other than the trace-context supported sampling bit. - scc.TraceFlags = opts[0] & trace.FlagsSampled + scc.TraceFlags = trace.TraceFlags(opts[0]) & trace.FlagsSampled scc.TraceState = parseTraceState(carrier.Get(tracestateHeader)) scc.Remote = true diff --git a/vendor/go.opentelemetry.io/otel/tag.sh b/vendor/go.opentelemetry.io/otel/tag.sh deleted file mode 100644 index 70767c70377ea..0000000000000 --- a/vendor/go.opentelemetry.io/otel/tag.sh +++ /dev/null @@ -1,178 +0,0 @@ -#!/usr/bin/env bash - -# Copyright The OpenTelemetry 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. - -readonly PROGNAME=$(basename "$0") -readonly PROGDIR=$(readlink -m "$(dirname "$0")") - -readonly EXCLUDE_PACKAGES="internal/tools" -readonly SEMVER_REGEX="v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\-[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?(\\+[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?" - -usage() { - cat <<- EOF -Usage: $PROGNAME [OPTIONS] SEMVER_TAG COMMIT_HASH - -Creates git tag for all Go packages in project. - -OPTIONS: - -h --help Show this help. - -ARGUMENTS: - SEMVER_TAG Semantic version to tag with. - COMMIT_HASH Git commit hash to tag. -EOF -} - -cmdline() { - local arg commit - - for arg - do - local delim="" - case "$arg" in - # Translate long form options to short form. - --help) args="${args}-h ";; - # Pass through for everything else. - *) [[ "${arg:0:1}" == "-" ]] || delim="\"" - args="${args}${delim}${arg}${delim} ";; - esac - done - - # Reset and process short form options. - eval set -- "$args" - - while getopts "h" OPTION - do - case $OPTION in - h) - usage - exit 0 - ;; - *) - echo "unknown option: $OPTION" - usage - exit 1 - ;; - esac - done - - # Positional arguments. - shift $((OPTIND-1)) - readonly TAG="$1" - if [ -z "$TAG" ] - then - echo "missing SEMVER_TAG" - usage - exit 1 - fi - if [[ ! "$TAG" =~ $SEMVER_REGEX ]] - then - printf "invalid semantic version: %s\n" "$TAG" - exit 2 - fi - if [[ "$( git tag --list "$TAG" )" ]] - then - printf "tag already exists: %s\n" "$TAG" - exit 2 - fi - - shift - commit="$1" - if [ -z "$commit" ] - then - echo "missing COMMIT_HASH" - usage - exit 1 - fi - # Verify rev is for a commit and unify hashes into a complete SHA1. - readonly SHA="$( git rev-parse --quiet --verify "${commit}^{commit}" )" - if [ -z "$SHA" ] - then - printf "invalid commit hash: %s\n" "$commit" - exit 2 - fi - if [ "$( git merge-base "$SHA" HEAD )" != "$SHA" ] - then - printf "commit '%s' not found on this branch\n" "$commit" - exit 2 - fi -} - -package_dirs() { - # Return a list of package directories in the form: - # - # package/directory/a - # package/directory/b - # deeper/package/directory/a - # ... - # - # Making sure to exclude any packages in the EXCLUDE_PACKAGES regexp. - find . -mindepth 2 -type f -name 'go.mod' -exec dirname {} \; \ - | grep -E -v "$EXCLUDE_PACKAGES" \ - | sed 's/^\.\///' \ - | sort -} - -git_tag() { - local tag="$1" - local commit="$2" - - git tag -a "$tag" -s -m "Version $tag" "$commit" -} - -previous_version() { - local current="$1" - - # Requires git > 2.0 - git tag -l --sort=v:refname \ - | grep -E "^${SEMVER_REGEX}$" \ - | grep -v "$current" \ - | tail -1 -} - -print_changes() { - local tag="$1" - local previous - - previous="$( previous_version "$tag" )" - if [ -n "$previous" ] - then - printf "\nRaw changes made between %s and %s\n" "$previous" "$tag" - printf "======================================\n" - git --no-pager log --pretty=oneline "${previous}..$tag" - fi -} - -main() { - local dir - - cmdline "$@" - - cd "$PROGDIR" || exit 3 - - # Create tag for root package. - git_tag "$TAG" "$SHA" - printf "created tag: %s\n" "$TAG" - - # Create tag for all sub-packages. - for dir in $( package_dirs ) - do - git_tag "${dir}/$TAG" "$SHA" - printf "created tag: %s\n" "${dir}/$TAG" - done - - print_changes "$TAG" -} -main "$@" diff --git a/vendor/go.opentelemetry.io/otel/trace.go b/vendor/go.opentelemetry.io/otel/trace.go deleted file mode 100644 index 1d5ffb8ea5745..0000000000000 --- a/vendor/go.opentelemetry.io/otel/trace.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright The OpenTelemetry 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 otel // import "go.opentelemetry.io/otel" - -import ( - "go.opentelemetry.io/otel/internal/global" - "go.opentelemetry.io/otel/trace" -) - -// Tracer creates a named tracer that implements Tracer interface. -// If the name is an empty string then provider uses default name. -// -// This is short for GetTracerProvider().Tracer(name) -func Tracer(name string) trace.Tracer { - return GetTracerProvider().Tracer(name) -} - -// GetTracerProvider returns the registered global trace provider. -// If none is registered then an instance of NoopTracerProvider is returned. -// -// Use the trace provider to create a named tracer. E.g. -// tracer := global.GetTracerProvider().Tracer("example.com/foo") -// or -// tracer := global.Tracer("example.com/foo") -func GetTracerProvider() trace.TracerProvider { - return global.TracerProvider() -} - -// SetTracerProvider registers `tp` as the global trace provider. -func SetTracerProvider(tp trace.TracerProvider) { - global.SetTracerProvider(tp) -} diff --git a/vendor/go.opentelemetry.io/otel/trace/context.go b/vendor/go.opentelemetry.io/otel/trace/context.go new file mode 100644 index 0000000000000..76f9a083c4096 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/trace/context.go @@ -0,0 +1,61 @@ +// Copyright The OpenTelemetry 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 trace // import "go.opentelemetry.io/otel/trace" + +import "context" + +type traceContextKeyType int + +const currentSpanKey traceContextKeyType = iota + +// ContextWithSpan returns a copy of parent with span set as the current Span. +func ContextWithSpan(parent context.Context, span Span) context.Context { + return context.WithValue(parent, currentSpanKey, span) +} + +// ContextWithSpanContext returns a copy of parent with sc as the current +// Span. The Span implementation that wraps sc is non-recording and performs +// no operations other than to return sc as the SpanContext from the +// SpanContext method. +func ContextWithSpanContext(parent context.Context, sc SpanContext) context.Context { + return ContextWithSpan(parent, nonRecordingSpan{sc: sc}) +} + +// ContextWithRemoteSpanContext returns a copy of parent with rsc set explicly +// as a remote SpanContext and as the current Span. The Span implementation +// that wraps rsc is non-recording and performs no operations other than to +// return rsc as the SpanContext from the SpanContext method. +func ContextWithRemoteSpanContext(parent context.Context, rsc SpanContext) context.Context { + return ContextWithSpanContext(parent, rsc.WithRemote(true)) +} + +// SpanFromContext returns the current Span from ctx. +// +// If no Span is currently set in ctx an implementation of a Span that +// performs no operations is returned. +func SpanFromContext(ctx context.Context) Span { + if ctx == nil { + return noopSpan{} + } + if span, ok := ctx.Value(currentSpanKey).(Span); ok { + return span + } + return noopSpan{} +} + +// SpanContextFromContext returns the current Span's SpanContext. +func SpanContextFromContext(ctx context.Context) SpanContext { + return SpanFromContext(ctx).SpanContext() +} diff --git a/vendor/go.opentelemetry.io/otel/trace/go.mod b/vendor/go.opentelemetry.io/otel/trace/go.mod index 508fa95968e70..914e4f4384a33 100644 --- a/vendor/go.opentelemetry.io/otel/trace/go.mod +++ b/vendor/go.opentelemetry.io/otel/trace/go.mod @@ -49,5 +49,5 @@ replace go.opentelemetry.io/otel/trace => ./ require ( github.com/google/go-cmp v0.5.5 github.com/stretchr/testify v1.7.0 - go.opentelemetry.io/otel v0.19.0 + go.opentelemetry.io/otel v0.20.0 ) diff --git a/vendor/go.opentelemetry.io/otel/version.go b/vendor/go.opentelemetry.io/otel/trace/nonrecording.go similarity index 60% rename from vendor/go.opentelemetry.io/otel/version.go rename to vendor/go.opentelemetry.io/otel/trace/nonrecording.go index f15d8d1753efb..88fcb81611f96 100644 --- a/vendor/go.opentelemetry.io/otel/version.go +++ b/vendor/go.opentelemetry.io/otel/trace/nonrecording.go @@ -12,9 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -package otel // import "go.opentelemetry.io/otel" +package trace // import "go.opentelemetry.io/otel/trace" -// Version is the current release version of OpenTelemetry in use. -func Version() string { - return "0.19.0" +// nonRecordingSpan is a minimal implementation of a Span that wraps a +// SpanContext. It performs no operations other than to return the wrapped +// SpanContext. +type nonRecordingSpan struct { + noopSpan + + sc SpanContext } + +// SpanContext returns the wrapped SpanContext. +func (s nonRecordingSpan) SpanContext() SpanContext { return s.sc } diff --git a/vendor/go.opentelemetry.io/otel/trace/noop.go b/vendor/go.opentelemetry.io/otel/trace/noop.go index e47d506979a05..4a20f20cb41d7 100644 --- a/vendor/go.opentelemetry.io/otel/trace/noop.go +++ b/vendor/go.opentelemetry.io/otel/trace/noop.go @@ -51,7 +51,7 @@ func (t noopTracer) Start(ctx context.Context, name string, _ ...SpanOption) (co // noopSpan is an implementation of Span that preforms no operations. type noopSpan struct{} -var _ noopSpan = noopSpan{} +var _ Span = noopSpan{} // SpanContext returns an empty span context. func (noopSpan) SpanContext() SpanContext { return SpanContext{} } diff --git a/vendor/go.opentelemetry.io/otel/trace/trace.go b/vendor/go.opentelemetry.io/otel/trace/trace.go index fc45a86388fa5..d372e7d9d7223 100644 --- a/vendor/go.opentelemetry.io/otel/trace/trace.go +++ b/vendor/go.opentelemetry.io/otel/trace/trace.go @@ -30,13 +30,7 @@ import ( const ( // FlagsSampled is a bitmask with the sampled bit set. A SpanContext // with the sampling bit set means the span is sampled. - FlagsSampled = byte(0x01) - // FlagsDeferred is a bitmask with the deferred bit set. A SpanContext - // with the deferred bit set means the sampling decision has been - // defered to the receiver. - FlagsDeferred = byte(0x02) - // FlagsDebug is a bitmask with the debug bit set. - FlagsDebug = byte(0x04) + FlagsSampled = TraceFlags(0x01) errInvalidHexID errorConst = "trace-id and span-id can only contain [0-9a-f] characters, all lowercase" @@ -319,12 +313,40 @@ func isTraceStateKeyValueValid(kv attribute.KeyValue) bool { valueFormatRegExp.MatchString(kv.Value.Emit()) } +// TraceFlags contains flags that can be set on a SpanContext +type TraceFlags byte //nolint:golint + +// IsSampled returns if the sampling bit is set in the TraceFlags. +func (tf TraceFlags) IsSampled() bool { + return tf&FlagsSampled == FlagsSampled +} + +// WithSampled sets the sampling bit in a new copy of the TraceFlags. +func (tf TraceFlags) WithSampled(sampled bool) TraceFlags { + if sampled { + return tf | FlagsSampled + } + + return tf &^ FlagsSampled +} + +// MarshalJSON implements a custom marshal function to encode TraceFlags +// as a hex string. +func (tf TraceFlags) MarshalJSON() ([]byte, error) { + return json.Marshal(tf.String()) +} + +// String returns the hex string representation form of TraceFlags +func (tf TraceFlags) String() string { + return hex.EncodeToString([]byte{byte(tf)}[:]) +} + // SpanContextConfig contains mutable fields usable for constructing // an immutable SpanContext. type SpanContextConfig struct { TraceID TraceID SpanID SpanID - TraceFlags byte + TraceFlags TraceFlags TraceState TraceState Remote bool } @@ -345,7 +367,7 @@ func NewSpanContext(config SpanContextConfig) SpanContext { type SpanContext struct { traceID TraceID spanID SpanID - traceFlags byte + traceFlags TraceFlags traceState TraceState remote bool } @@ -415,12 +437,17 @@ func (sc SpanContext) WithSpanID(spanID SpanID) SpanContext { } // TraceFlags returns the flags from the SpanContext. -func (sc SpanContext) TraceFlags() byte { +func (sc SpanContext) TraceFlags() TraceFlags { return sc.traceFlags } +// IsSampled returns if the sampling bit is set in the SpanContext's TraceFlags. +func (sc SpanContext) IsSampled() bool { + return sc.traceFlags.IsSampled() +} + // WithTraceFlags returns a new SpanContext with the TraceFlags replaced. -func (sc SpanContext) WithTraceFlags(flags byte) SpanContext { +func (sc SpanContext) WithTraceFlags(flags TraceFlags) SpanContext { return SpanContext{ traceID: sc.traceID, spanID: sc.spanID, @@ -430,21 +457,6 @@ func (sc SpanContext) WithTraceFlags(flags byte) SpanContext { } } -// IsDeferred returns if the deferred bit is set in the trace flags. -func (sc SpanContext) IsDeferred() bool { - return sc.traceFlags&FlagsDeferred == FlagsDeferred -} - -// IsDebug returns if the debug bit is set in the trace flags. -func (sc SpanContext) IsDebug() bool { - return sc.traceFlags&FlagsDebug == FlagsDebug -} - -// IsSampled returns if the sampling bit is set in the trace flags. -func (sc SpanContext) IsSampled() bool { - return sc.traceFlags&FlagsSampled == FlagsSampled -} - // TraceState returns the TraceState from the SpanContext. func (sc SpanContext) TraceState() TraceState { return sc.traceState @@ -481,48 +493,6 @@ func (sc SpanContext) MarshalJSON() ([]byte, error) { }) } -type traceContextKeyType int - -const ( - currentSpanKey traceContextKeyType = iota - remoteContextKey -) - -// ContextWithSpan returns a copy of parent with span set to current. -func ContextWithSpan(parent context.Context, span Span) context.Context { - return context.WithValue(parent, currentSpanKey, span) -} - -// SpanFromContext returns the current span from ctx, or noop span if none set. -func SpanFromContext(ctx context.Context) Span { - if span, ok := ctx.Value(currentSpanKey).(Span); ok { - return span - } - return noopSpan{} -} - -// SpanContextFromContext returns the current SpanContext from ctx, or an empty SpanContext if none set. -func SpanContextFromContext(ctx context.Context) SpanContext { - if span := SpanFromContext(ctx); span != nil { - return span.SpanContext() - } - return SpanContext{} -} - -// ContextWithRemoteSpanContext returns a copy of parent with a remote set as -// the remote span context. -func ContextWithRemoteSpanContext(parent context.Context, remote SpanContext) context.Context { - return context.WithValue(parent, remoteContextKey, remote.WithRemote(true)) -} - -// RemoteSpanContextFromContext returns the remote span context from ctx. -func RemoteSpanContextFromContext(ctx context.Context) SpanContext { - if sc, ok := ctx.Value(remoteContextKey).(SpanContext); ok { - return sc - } - return SpanContext{} -} - // Span is the individual component of a trace. It represents a single named // and timed operation of a workflow that is traced. A Tracer is used to // create a Span and it is then up to the operation the Span represents to @@ -545,7 +515,10 @@ type Span interface { // true if the Span is active and events can be recorded. IsRecording() bool - // RecordError records an error as a Span event. + // RecordError will record err as an exception span event for this span. An + // additional call toSetStatus is required if the Status of the Span should + // be set to Error, this method does not change the Span status. If this + // span is not being recorded or err is nil than this method does nothing. RecordError(err error, options ...EventOption) // SpanContext returns the SpanContext of the Span. The returned @@ -574,6 +547,10 @@ type Event struct { // Attributes describe the aspects of the event. Attributes []attribute.KeyValue + // DroppedAttributeCount is the number of attributes that were not + // recorded due to configured limits being reached. + DroppedAttributeCount int + // Time at which this event was recorded. Time time.Time } @@ -594,8 +571,15 @@ type Event struct { // form. A Link is used to keep reference to the original SpanContext and // track the relationship. type Link struct { + // SpanContext of the linked Span. SpanContext + + // Attributes describe the aspects of the link. Attributes []attribute.KeyValue + + // DroppedAttributeCount is the number of attributes that were not + // recorded due to configured limits being reached. + DroppedAttributeCount int } // SpanKind is the role a Span plays in a Trace. diff --git a/vendor/go.opentelemetry.io/otel/verify_examples.sh b/vendor/go.opentelemetry.io/otel/verify_examples.sh deleted file mode 100644 index dbb61a4227959..0000000000000 --- a/vendor/go.opentelemetry.io/otel/verify_examples.sh +++ /dev/null @@ -1,85 +0,0 @@ -#!/bin/bash - -# Copyright The OpenTelemetry 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. - -set -euo pipefail - -cd $(dirname $0) -TOOLS_DIR=$(pwd)/.tools - -if [ -z "${GOPATH}" ] ; then - printf "GOPATH is not defined.\n" - exit -1 -fi - -if [ ! -d "${GOPATH}" ] ; then - printf "GOPATH ${GOPATH} is invalid \n" - exit -1 -fi - -# Pre-requisites -if ! git diff --quiet; then \ - git status - printf "\n\nError: working tree is not clean\n" - exit -1 -fi - -if [ "$(git tag --contains $(git log -1 --pretty=format:"%H"))" = "" ] ; then - printf "$(git log -1)" - printf "\n\nError: HEAD is not pointing to a tagged version" -fi - -make ${TOOLS_DIR}/gojq - -DIR_TMP="${GOPATH}/src/oteltmp/" -rm -rf $DIR_TMP -mkdir -p $DIR_TMP - -printf "Copy examples to ${DIR_TMP}\n" -cp -a ./example ${DIR_TMP} - -# Update go.mod files -printf "Update go.mod: rename module and remove replace\n" - -PACKAGE_DIRS=$(find . -mindepth 2 -type f -name 'go.mod' -exec dirname {} \; | egrep 'example' | sed 's/^\.\///' | sort) - -for dir in $PACKAGE_DIRS; do - printf " Update go.mod for $dir\n" - (cd "${DIR_TMP}/${dir}" && \ - # replaces is ("mod1" "mod2" …) - replaces=($(go mod edit -json | ${TOOLS_DIR}/gojq '.Replace[].Old.Path')) && \ - # strip double quotes - replaces=("${replaces[@]%\"}") && \ - replaces=("${replaces[@]#\"}") && \ - # make an array (-dropreplace=mod1 -dropreplace=mod2 …) - dropreplaces=("${replaces[@]/#/-dropreplace=}") && \ - go mod edit -module "oteltmp/${dir}" "${dropreplaces[@]}" && \ - go mod tidy) -done -printf "Update done:\n\n" - -# Build directories that contain main package. These directories are different than -# directories that contain go.mod files. -printf "Build examples:\n" -EXAMPLES=$(./get_main_pkgs.sh ./example) -for ex in $EXAMPLES; do - printf " Build $ex in ${DIR_TMP}/${ex}\n" - (cd "${DIR_TMP}/${ex}" && \ - go build .) -done - -# Cleanup -printf "Remove copied files.\n" -rm -rf $DIR_TMP diff --git a/vendor/go.uber.org/multierr/.travis.yml b/vendor/go.uber.org/multierr/.travis.yml deleted file mode 100644 index 8636ab42ad141..0000000000000 --- a/vendor/go.uber.org/multierr/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -sudo: false -language: go -go_import_path: go.uber.org/multierr - -env: - global: - - GO111MODULE=on - -go: - - oldstable - - stable - -before_install: -- go version - -script: -- | - set -e - make lint - make cover - -after_success: -- bash <(curl -s https://codecov.io/bash) diff --git a/vendor/go.uber.org/multierr/CHANGELOG.md b/vendor/go.uber.org/multierr/CHANGELOG.md index 6f1db9ef4a0a4..b0814e7c9bd59 100644 --- a/vendor/go.uber.org/multierr/CHANGELOG.md +++ b/vendor/go.uber.org/multierr/CHANGELOG.md @@ -1,6 +1,12 @@ Releases ======== +v1.7.0 (2021-05-06) +=================== + +- Add `AppendInvoke` to append into errors from `defer` blocks. + + v1.6.0 (2020-09-14) =================== diff --git a/vendor/go.uber.org/multierr/LICENSE.txt b/vendor/go.uber.org/multierr/LICENSE.txt index 858e02475f163..413e30f7ce210 100644 --- a/vendor/go.uber.org/multierr/LICENSE.txt +++ b/vendor/go.uber.org/multierr/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2017 Uber Technologies, Inc. +Copyright (c) 2017-2021 Uber Technologies, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/go.uber.org/multierr/Makefile b/vendor/go.uber.org/multierr/Makefile index 316004400b898..dcb6fe723c058 100644 --- a/vendor/go.uber.org/multierr/Makefile +++ b/vendor/go.uber.org/multierr/Makefile @@ -34,9 +34,5 @@ lint: gofmt golint staticcheck .PHONY: cover cover: - go test -coverprofile=cover.out -coverpkg=./... -v ./... + go test -race -coverprofile=cover.out -coverpkg=./... -v ./... go tool cover -html=cover.out -o cover.html - -update-license: - @cd tools && go install go.uber.org/tools/update-license - @$(GOBIN)/update-license $(GO_FILES) diff --git a/vendor/go.uber.org/multierr/README.md b/vendor/go.uber.org/multierr/README.md index 751bd65e58115..70aacecd71583 100644 --- a/vendor/go.uber.org/multierr/README.md +++ b/vendor/go.uber.org/multierr/README.md @@ -15,9 +15,9 @@ Stable: No breaking changes will be made before 2.0. Released under the [MIT License]. [MIT License]: LICENSE.txt -[doc-img]: https://godoc.org/go.uber.org/multierr?status.svg -[doc]: https://godoc.org/go.uber.org/multierr -[ci-img]: https://travis-ci.com/uber-go/multierr.svg?branch=master +[doc-img]: https://pkg.go.dev/badge/go.uber.org/multierr +[doc]: https://pkg.go.dev/go.uber.org/multierr +[ci-img]: https://github.com/uber-go/multierr/actions/workflows/go.yml/badge.svg [cov-img]: https://codecov.io/gh/uber-go/multierr/branch/master/graph/badge.svg -[ci]: https://travis-ci.com/uber-go/multierr +[ci]: https://github.com/uber-go/multierr/actions/workflows/go.yml [cov]: https://codecov.io/gh/uber-go/multierr diff --git a/vendor/go.uber.org/multierr/error.go b/vendor/go.uber.org/multierr/error.go index 5c9b67d5379ef..faa0a059464ed 100644 --- a/vendor/go.uber.org/multierr/error.go +++ b/vendor/go.uber.org/multierr/error.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Uber Technologies, Inc. +// Copyright (c) 2017-2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -35,8 +35,53 @@ // // err = multierr.Append(reader.Close(), writer.Close()) // -// This makes it possible to record resource cleanup failures from deferred -// blocks with the help of named return values. +// The underlying list of errors for a returned error object may be retrieved +// with the Errors function. +// +// errors := multierr.Errors(err) +// if len(errors) > 0 { +// fmt.Println("The following errors occurred:", errors) +// } +// +// Appending from a loop +// +// You sometimes need to append into an error from a loop. +// +// var err error +// for _, item := range items { +// err = multierr.Append(err, process(item)) +// } +// +// Cases like this may require knowledge of whether an individual instance +// failed. This usually requires introduction of a new variable. +// +// var err error +// for _, item := range items { +// if perr := process(item); perr != nil { +// log.Warn("skipping item", item) +// err = multierr.Append(err, perr) +// } +// } +// +// multierr includes AppendInto to simplify cases like this. +// +// var err error +// for _, item := range items { +// if multierr.AppendInto(&err, process(item)) { +// log.Warn("skipping item", item) +// } +// } +// +// This will append the error into the err variable, and return true if that +// individual error was non-nil. +// +// See AppendInto for more information. +// +// Deferred Functions +// +// Go makes it possible to modify the return value of a function in a defer +// block if the function was using named returns. This makes it possible to +// record resource cleanup failures from deferred blocks. // // func sendRequest(req Request) (err error) { // conn, err := openConnection() @@ -49,14 +94,21 @@ // // ... // } // -// The underlying list of errors for a returned error object may be retrieved -// with the Errors function. +// multierr provides the Invoker type and AppendInvoke function to make cases +// like the above simpler and obviate the need for a closure. The following is +// roughly equivalent to the example above. // -// errors := multierr.Errors(err) -// if len(errors) > 0 { -// fmt.Println("The following errors occurred:", errors) +// func sendRequest(req Request) (err error) { +// conn, err := openConnection() +// if err != nil { +// return err +// } +// defer multierr.AppendInvoke(err, multierr.Close(conn)) +// // ... // } // +// See AppendInvoke and Invoker for more information. +// // Advanced Usage // // Errors returned by Combine and Append MAY implement the following @@ -87,6 +139,7 @@ package multierr // import "go.uber.org/multierr" import ( "bytes" + "errors" "fmt" "io" "strings" @@ -186,6 +239,33 @@ func (merr *multiError) Errors() []error { return merr.errors } +// As attempts to find the first error in the error list that matches the type +// of the value that target points to. +// +// This function allows errors.As to traverse the values stored on the +// multierr error. +func (merr *multiError) As(target interface{}) bool { + for _, err := range merr.Errors() { + if errors.As(err, target) { + return true + } + } + return false +} + +// Is attempts to match the provided error against errors in the error list. +// +// This function allows errors.Is to traverse the values stored on the +// multierr error. +func (merr *multiError) Is(target error) bool { + for _, err := range merr.Errors() { + if errors.Is(err, target) { + return true + } + } + return false +} + func (merr *multiError) Error() string { if merr == nil { return "" @@ -421,7 +501,7 @@ func Append(left error, right error) error { // items = append(items, item) // } // -// Compare this with a verison that relies solely on Append: +// Compare this with a version that relies solely on Append: // // var err error // for line := range lines { @@ -447,3 +527,113 @@ func AppendInto(into *error, err error) (errored bool) { *into = Append(*into, err) return true } + +// Invoker is an operation that may fail with an error. Use it with +// AppendInvoke to append the result of calling the function into an error. +// This allows you to conveniently defer capture of failing operations. +// +// See also, Close and Invoke. +type Invoker interface { + Invoke() error +} + +// Invoke wraps a function which may fail with an error to match the Invoker +// interface. Use it to supply functions matching this signature to +// AppendInvoke. +// +// For example, +// +// func processReader(r io.Reader) (err error) { +// scanner := bufio.NewScanner(r) +// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err)) +// for scanner.Scan() { +// // ... +// } +// // ... +// } +// +// In this example, the following line will construct the Invoker right away, +// but defer the invocation of scanner.Err() until the function returns. +// +// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err)) +type Invoke func() error + +// Invoke calls the supplied function and returns its result. +func (i Invoke) Invoke() error { return i() } + +// Close builds an Invoker that closes the provided io.Closer. Use it with +// AppendInvoke to close io.Closers and append their results into an error. +// +// For example, +// +// func processFile(path string) (err error) { +// f, err := os.Open(path) +// if err != nil { +// return err +// } +// defer multierr.AppendInvoke(&err, multierr.Close(f)) +// return processReader(f) +// } +// +// In this example, multierr.Close will construct the Invoker right away, but +// defer the invocation of f.Close until the function returns. +// +// defer multierr.AppendInvoke(&err, multierr.Close(f)) +func Close(closer io.Closer) Invoker { + return Invoke(closer.Close) +} + +// AppendInvoke appends the result of calling the given Invoker into the +// provided error pointer. Use it with named returns to safely defer +// invocation of fallible operations until a function returns, and capture the +// resulting errors. +// +// func doSomething(...) (err error) { +// // ... +// f, err := openFile(..) +// if err != nil { +// return err +// } +// +// // multierr will call f.Close() when this function returns and +// // if the operation fails, its append its error into the +// // returned error. +// defer multierr.AppendInvoke(&err, multierr.Close(f)) +// +// scanner := bufio.NewScanner(f) +// // Similarly, this scheduled scanner.Err to be called and +// // inspected when the function returns and append its error +// // into the returned error. +// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err)) +// +// // ... +// } +// +// Without defer, AppendInvoke behaves exactly like AppendInto. +// +// err := // ... +// multierr.AppendInvoke(&err, mutltierr.Invoke(foo)) +// +// // ...is roughly equivalent to... +// +// err := // ... +// multierr.AppendInto(&err, foo()) +// +// The advantage of the indirection introduced by Invoker is to make it easy +// to defer the invocation of a function. Without this indirection, the +// invoked function will be evaluated at the time of the defer block rather +// than when the function returns. +// +// // BAD: This is likely not what the caller intended. This will evaluate +// // foo() right away and append its result into the error when the +// // function returns. +// defer multierr.AppendInto(&err, foo()) +// +// // GOOD: This will defer invocation of foo unutil the function returns. +// defer multierr.AppendInvoke(&err, multierr.Invoke(foo)) +// +// multierr provides a few Invoker implementations out of the box for +// convenience. See Invoker for more information. +func AppendInvoke(into *error, invoker Invoker) { + AppendInto(into, invoker.Invoke()) +} diff --git a/vendor/go.uber.org/multierr/go.mod b/vendor/go.uber.org/multierr/go.mod index ff8bdf95fcf99..398d6c99e7f79 100644 --- a/vendor/go.uber.org/multierr/go.mod +++ b/vendor/go.uber.org/multierr/go.mod @@ -1,8 +1,9 @@ module go.uber.org/multierr -go 1.12 +go 1.14 require ( - github.com/stretchr/testify v1.3.0 + github.com/stretchr/testify v1.7.0 go.uber.org/atomic v1.7.0 + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) diff --git a/vendor/go.uber.org/multierr/go.sum b/vendor/go.uber.org/multierr/go.sum index ecfc28657825b..75edd735e0b14 100644 --- a/vendor/go.uber.org/multierr/go.sum +++ b/vendor/go.uber.org/multierr/go.sum @@ -1,11 +1,16 @@ -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/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/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= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +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/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/vendor/go.uber.org/multierr/go113.go b/vendor/go.uber.org/multierr/go113.go deleted file mode 100644 index 264b0eac0ddcf..0000000000000 --- a/vendor/go.uber.org/multierr/go113.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2019 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -// +build go1.13 - -package multierr - -import "errors" - -// As attempts to find the first error in the error list that matches the type -// of the value that target points to. -// -// This function allows errors.As to traverse the values stored on the -// multierr error. -func (merr *multiError) As(target interface{}) bool { - for _, err := range merr.Errors() { - if errors.As(err, target) { - return true - } - } - return false -} - -// Is attempts to match the provided error against errors in the error list. -// -// This function allows errors.Is to traverse the values stored on the -// multierr error. -func (merr *multiError) Is(target error) bool { - for _, err := range merr.Errors() { - if errors.Is(err, target) { - return true - } - } - return false -} diff --git a/vendor/go.uber.org/zap/.travis.yml b/vendor/go.uber.org/zap/.travis.yml deleted file mode 100644 index cfdc69f413e74..0000000000000 --- a/vendor/go.uber.org/zap/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -language: go -sudo: false - -go_import_path: go.uber.org/zap -env: - global: - - TEST_TIMEOUT_SCALE=10 - - GO111MODULE=on - -matrix: - include: - - go: 1.13.x - - go: 1.14.x - env: LINT=1 - -script: - - test -z "$LINT" || make lint - - make test - - make bench - -after_success: - - make cover - - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/go.uber.org/zap/CHANGELOG.md b/vendor/go.uber.org/zap/CHANGELOG.md index fa817e6a10368..3b99bf0ac84f4 100644 --- a/vendor/go.uber.org/zap/CHANGELOG.md +++ b/vendor/go.uber.org/zap/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## 1.17.0 (25 May 2021) + +Bugfixes: +* [#867][]: Encode `` for nil `error` instead of a panic. +* [#931][], [#936][]: Update minimum version constraints to address + vulnerabilities in dependencies. + +Enhancements: +* [#865][]: Improve alignment of fields of the Logger struct, reducing its + size from 96 to 80 bytes. +* [#881][]: Support `grpclog.LoggerV2` in zapgrpc. +* [#903][]: Support URL-encoded POST requests to the AtomicLevel HTTP handler + with the `application/x-www-form-urlencoded` content type. +* [#912][]: Support multi-field encoding with `zap.Inline`. +* [#913][]: Speed up SugaredLogger for calls with a single string. +* [#928][]: Add support for filtering by field name to `zaptest/observer`. + +Thanks to @ash2k, @FMLS, @jimmystewpot, @Oncilla, @tsoslow, @tylitianrui, @withshubh, and @wziww for their contributions to this release. + ## 1.16.0 (1 Sep 2020) Bugfixes: @@ -430,3 +449,12 @@ upgrade to the upcoming stable release. [#854]: https://github.com/uber-go/zap/pull/854 [#861]: https://github.com/uber-go/zap/pull/861 [#862]: https://github.com/uber-go/zap/pull/862 +[#865]: https://github.com/uber-go/zap/pull/865 +[#867]: https://github.com/uber-go/zap/pull/867 +[#881]: https://github.com/uber-go/zap/pull/881 +[#903]: https://github.com/uber-go/zap/pull/903 +[#912]: https://github.com/uber-go/zap/pull/912 +[#913]: https://github.com/uber-go/zap/pull/913 +[#928]: https://github.com/uber-go/zap/pull/928 +[#931]: https://github.com/uber-go/zap/pull/931 +[#936]: https://github.com/uber-go/zap/pull/936 diff --git a/vendor/go.uber.org/zap/CONTRIBUTING.md b/vendor/go.uber.org/zap/CONTRIBUTING.md index 9454bbaf026a8..5cd965687138c 100644 --- a/vendor/go.uber.org/zap/CONTRIBUTING.md +++ b/vendor/go.uber.org/zap/CONTRIBUTING.md @@ -25,12 +25,6 @@ git remote add upstream https://github.com/uber-go/zap.git git fetch upstream ``` -Install zap's dependencies: - -``` -make dependencies -``` - Make sure that the tests and the linters pass: ``` diff --git a/vendor/go.uber.org/zap/FAQ.md b/vendor/go.uber.org/zap/FAQ.md index 5ec7288750762..b183b20bc1392 100644 --- a/vendor/go.uber.org/zap/FAQ.md +++ b/vendor/go.uber.org/zap/FAQ.md @@ -27,6 +27,13 @@ abstraction, and it lets us add methods without introducing breaking changes. Your applications should define and depend upon an interface that includes just the methods you use. +### Why are some of my logs missing? + +Logs are dropped intentionally by zap when sampling is enabled. The production +configuration (as returned by `NewProductionConfig()` enables sampling which will +cause repeated logs within a second to be sampled. See more details on why sampling +is enabled in [Why sample application logs](https://github.com/uber-go/zap/blob/master/FAQ.md#why-sample-application-logs). + ### Why sample application logs? Applications often experience runs of errors, either because of a bug or @@ -150,6 +157,7 @@ We're aware of the following extensions, but haven't used them ourselves: | `github.com/fgrosse/zaptest` | Ginkgo | | `github.com/blendle/zapdriver` | Stackdriver | | `github.com/moul/zapgorm` | Gorm | +| `github.com/moul/zapfilter` | Advanced filtering rules | [go-proverbs]: https://go-proverbs.github.io/ [import-path]: https://golang.org/cmd/go/#hdr-Remote_import_paths diff --git a/vendor/go.uber.org/zap/Makefile b/vendor/go.uber.org/zap/Makefile index dfaf6406e9746..9b1bc3b0e1d89 100644 --- a/vendor/go.uber.org/zap/Makefile +++ b/vendor/go.uber.org/zap/Makefile @@ -7,7 +7,7 @@ BENCH_FLAGS ?= -cpuprofile=cpu.pprof -memprofile=mem.pprof -benchmem # Directories containing independent Go modules. # # We track coverage only for the main module. -MODULE_DIRS = . ./benchmarks +MODULE_DIRS = . ./benchmarks ./zapgrpc/internal/test # Many Go tools take file globs or directories as arguments instead of packages. GO_FILES := $(shell \ @@ -33,12 +33,18 @@ lint: $(GOLINT) $(STATICCHECK) @echo "Checking for license headers..." @./checklicense.sh | tee -a lint.log @[ ! -s lint.log ] + @echo "Checking 'go mod tidy'..." + @make tidy + @if ! git diff --quiet; then \ + echo "'go mod tidy' resulted in changes or working tree is dirty:"; \ + git --no-pager diff; \ + fi $(GOLINT): - go install golang.org/x/lint/golint + cd tools && go install golang.org/x/lint/golint $(STATICCHECK): - go install honnef.co/go/tools/cmd/staticcheck + cd tools && go install honnef.co/go/tools/cmd/staticcheck .PHONY: test test: @@ -61,3 +67,7 @@ bench: updatereadme: rm -f README.md cat .readme.tmpl | go run internal/readme/readme.go > README.md + +.PHONY: tidy +tidy: + @$(foreach dir,$(MODULE_DIRS),(cd $(dir) && go mod tidy) &&) true diff --git a/vendor/go.uber.org/zap/README.md b/vendor/go.uber.org/zap/README.md index bcea28a196f01..1e64d6cffc13d 100644 --- a/vendor/go.uber.org/zap/README.md +++ b/vendor/go.uber.org/zap/README.md @@ -123,10 +123,10 @@ Released under the [MIT License](LICENSE.txt). benchmarking against slightly older versions of other packages. Versions are pinned in the [benchmarks/go.mod][] file. [↩](#anchor-versions) -[doc-img]: https://godoc.org/go.uber.org/zap?status.svg -[doc]: https://godoc.org/go.uber.org/zap -[ci-img]: https://travis-ci.com/uber-go/zap.svg?branch=master -[ci]: https://travis-ci.com/uber-go/zap +[doc-img]: https://pkg.go.dev/badge/go.uber.org/zap +[doc]: https://pkg.go.dev/go.uber.org/zap +[ci-img]: https://github.com/uber-go/zap/actions/workflows/go.yml/badge.svg +[ci]: https://github.com/uber-go/zap/actions/workflows/go.yml [cov-img]: https://codecov.io/gh/uber-go/zap/branch/master/graph/badge.svg [cov]: https://codecov.io/gh/uber-go/zap [benchmarking suite]: https://github.com/uber-go/zap/tree/master/benchmarks diff --git a/vendor/go.uber.org/zap/field.go b/vendor/go.uber.org/zap/field.go index 3c0d7d9578704..bbb745db5bdc3 100644 --- a/vendor/go.uber.org/zap/field.go +++ b/vendor/go.uber.org/zap/field.go @@ -400,6 +400,16 @@ func Object(key string, val zapcore.ObjectMarshaler) Field { return Field{Key: key, Type: zapcore.ObjectMarshalerType, Interface: val} } +// Inline constructs a Field that is similar to Object, but it +// will add the elements of the provided ObjectMarshaler to the +// current namespace. +func Inline(val zapcore.ObjectMarshaler) Field { + return zapcore.Field{ + Type: zapcore.InlineMarshalerType, + Interface: val, + } +} + // Any takes a key and an arbitrary value and chooses the best way to represent // them as a field, falling back to a reflection-based approach only if // necessary. diff --git a/vendor/go.uber.org/zap/go.mod b/vendor/go.uber.org/zap/go.mod index 6ef4db70edd53..6578a354546cf 100644 --- a/vendor/go.uber.org/zap/go.mod +++ b/vendor/go.uber.org/zap/go.mod @@ -4,10 +4,9 @@ go 1.13 require ( github.com/pkg/errors v0.8.1 - github.com/stretchr/testify v1.4.0 - go.uber.org/atomic v1.6.0 - go.uber.org/multierr v1.5.0 - golang.org/x/lint v0.0.0-20190930215403-16217165b5de - gopkg.in/yaml.v2 v2.2.2 - honnef.co/go/tools v0.0.1-2019.2.3 + github.com/stretchr/testify v1.7.0 + go.uber.org/atomic v1.7.0 + go.uber.org/multierr v1.6.0 + gopkg.in/yaml.v2 v2.2.8 + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) diff --git a/vendor/go.uber.org/zap/go.sum b/vendor/go.uber.org/zap/go.sum index 99cdb93ea0a2e..911a87ae1c4fc 100644 --- a/vendor/go.uber.org/zap/go.sum +++ b/vendor/go.uber.org/zap/go.sum @@ -1,56 +1,22 @@ -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -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/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/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 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/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 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/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -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-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c h1:IGkKhmfzcztjm6gYkykvu/NiS8kaqbCWAEWWAyf8J5U= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 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/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/vendor/go.uber.org/zap/http_handler.go b/vendor/go.uber.org/zap/http_handler.go index 1b0ecaca9c15c..1297c33b32851 100644 --- a/vendor/go.uber.org/zap/http_handler.go +++ b/vendor/go.uber.org/zap/http_handler.go @@ -23,6 +23,7 @@ package zap import ( "encoding/json" "fmt" + "io" "net/http" "go.uber.org/zap/zapcore" @@ -31,47 +32,63 @@ import ( // ServeHTTP is a simple JSON endpoint that can report on or change the current // logging level. // -// GET requests return a JSON description of the current logging level. PUT -// requests change the logging level and expect a payload like: +// GET +// +// The GET request returns a JSON description of the current logging level like: // {"level":"info"} // -// It's perfectly safe to change the logging level while a program is running. +// PUT +// +// The PUT request changes the logging level. It is perfectly safe to change the +// logging level while a program is running. Two content types are supported: +// +// Content-Type: application/x-www-form-urlencoded +// +// With this content type, the level can be provided through the request body or +// a query parameter. The log level is URL encoded like: +// +// level=debug +// +// The request body takes precedence over the query parameter, if both are +// specified. +// +// This content type is the default for a curl PUT request. Following are two +// example curl requests that both set the logging level to debug. +// +// curl -X PUT localhost:8080/log/level?level=debug +// curl -X PUT localhost:8080/log/level -d level=debug +// +// For any other content type, the payload is expected to be JSON encoded and +// look like: +// +// {"level":"info"} +// +// An example curl request could look like this: +// +// curl -X PUT localhost:8080/log/level -H "Content-Type: application/json" -d '{"level":"debug"}' +// func (lvl AtomicLevel) ServeHTTP(w http.ResponseWriter, r *http.Request) { type errorResponse struct { Error string `json:"error"` } type payload struct { - Level *zapcore.Level `json:"level"` + Level zapcore.Level `json:"level"` } enc := json.NewEncoder(w) switch r.Method { - case http.MethodGet: - current := lvl.Level() - enc.Encode(payload{Level: ¤t}) - + enc.Encode(payload{Level: lvl.Level()}) case http.MethodPut: - var req payload - - if errmess := func() string { - if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - return fmt.Sprintf("Request body must be well-formed JSON: %v", err) - } - if req.Level == nil { - return "Must specify a logging level." - } - return "" - }(); errmess != "" { + requestedLvl, err := decodePutRequest(r.Header.Get("Content-Type"), r) + if err != nil { w.WriteHeader(http.StatusBadRequest) - enc.Encode(errorResponse{Error: errmess}) + enc.Encode(errorResponse{Error: err.Error()}) return } - - lvl.SetLevel(*req.Level) - enc.Encode(req) - + lvl.SetLevel(requestedLvl) + enc.Encode(payload{Level: lvl.Level()}) default: w.WriteHeader(http.StatusMethodNotAllowed) enc.Encode(errorResponse{ @@ -79,3 +96,37 @@ func (lvl AtomicLevel) ServeHTTP(w http.ResponseWriter, r *http.Request) { }) } } + +// Decodes incoming PUT requests and returns the requested logging level. +func decodePutRequest(contentType string, r *http.Request) (zapcore.Level, error) { + if contentType == "application/x-www-form-urlencoded" { + return decodePutURL(r) + } + return decodePutJSON(r.Body) +} + +func decodePutURL(r *http.Request) (zapcore.Level, error) { + lvl := r.FormValue("level") + if lvl == "" { + return 0, fmt.Errorf("must specify logging level") + } + var l zapcore.Level + if err := l.UnmarshalText([]byte(lvl)); err != nil { + return 0, err + } + return l, nil +} + +func decodePutJSON(body io.Reader) (zapcore.Level, error) { + var pld struct { + Level *zapcore.Level `json:"level"` + } + if err := json.NewDecoder(body).Decode(&pld); err != nil { + return 0, fmt.Errorf("malformed request body: %v", err) + } + if pld.Level == nil { + return 0, fmt.Errorf("must specify logging level") + } + return *pld.Level, nil + +} diff --git a/vendor/go.uber.org/zap/logger.go b/vendor/go.uber.org/zap/logger.go index ea484aed10210..553f258e74a42 100644 --- a/vendor/go.uber.org/zap/logger.go +++ b/vendor/go.uber.org/zap/logger.go @@ -42,14 +42,15 @@ type Logger struct { core zapcore.Core development bool + addCaller bool + onFatal zapcore.CheckWriteAction // default is WriteThenFatal + name string errorOutput zapcore.WriteSyncer - addCaller bool - addStack zapcore.LevelEnabler + addStack zapcore.LevelEnabler callerSkip int - onFatal zapcore.CheckWriteAction // default is WriteThenFatal } // New constructs a new Logger from the provided zapcore.Core and Options. If @@ -334,7 +335,7 @@ func getCallerFrame(skip int) (frame runtime.Frame, ok bool) { const skipOffset = 2 // skip getCallerFrame and Callers pc := make([]uintptr, 1) - numFrames := runtime.Callers(skip+skipOffset, pc[:]) + numFrames := runtime.Callers(skip+skipOffset, pc) if numFrames < 1 { return } diff --git a/vendor/go.uber.org/zap/sugar.go b/vendor/go.uber.org/zap/sugar.go index 77ca227f47f96..4084dada79c5b 100644 --- a/vendor/go.uber.org/zap/sugar.go +++ b/vendor/go.uber.org/zap/sugar.go @@ -222,19 +222,30 @@ func (s *SugaredLogger) log(lvl zapcore.Level, template string, fmtArgs []interf return } - // Format with Sprint, Sprintf, or neither. - msg := template - if msg == "" && len(fmtArgs) > 0 { - msg = fmt.Sprint(fmtArgs...) - } else if msg != "" && len(fmtArgs) > 0 { - msg = fmt.Sprintf(template, fmtArgs...) - } - + msg := getMessage(template, fmtArgs) if ce := s.base.Check(lvl, msg); ce != nil { ce.Write(s.sweetenFields(context)...) } } +// getMessage format with Sprint, Sprintf, or neither. +func getMessage(template string, fmtArgs []interface{}) string { + if len(fmtArgs) == 0 { + return template + } + + if template != "" { + return fmt.Sprintf(template, fmtArgs...) + } + + if len(fmtArgs) == 1 { + if str, ok := fmtArgs[0].(string); ok { + return str + } + } + return fmt.Sprint(fmtArgs...) +} + func (s *SugaredLogger) sweetenFields(args []interface{}) []Field { if len(args) == 0 { return nil diff --git a/vendor/go.uber.org/zap/zapcore/console_encoder.go b/vendor/go.uber.org/zap/zapcore/console_encoder.go index 3b68f8c0c51c5..2307af404c5ef 100644 --- a/vendor/go.uber.org/zap/zapcore/console_encoder.go +++ b/vendor/go.uber.org/zap/zapcore/console_encoder.go @@ -56,7 +56,7 @@ type consoleEncoder struct { // encoder configuration, it will omit any element whose key is set to the empty // string. func NewConsoleEncoder(cfg EncoderConfig) Encoder { - if len(cfg.ConsoleSeparator) == 0 { + if cfg.ConsoleSeparator == "" { // Use a default delimiter of '\t' for backwards compatibility cfg.ConsoleSeparator = "\t" } diff --git a/vendor/go.uber.org/zap/zapcore/error.go b/vendor/go.uber.org/zap/zapcore/error.go index 9ba2272c3f760..f2a07d7864128 100644 --- a/vendor/go.uber.org/zap/zapcore/error.go +++ b/vendor/go.uber.org/zap/zapcore/error.go @@ -22,6 +22,7 @@ package zapcore import ( "fmt" + "reflect" "sync" ) @@ -42,7 +43,23 @@ import ( // ... // ], // } -func encodeError(key string, err error, enc ObjectEncoder) error { +func encodeError(key string, err error, enc ObjectEncoder) (retErr error) { + // Try to capture panics (from nil references or otherwise) when calling + // the Error() method + defer func() { + if rerr := recover(); rerr != nil { + // If it's a nil pointer, just say "". The likeliest causes are a + // error that fails to guard against nil or a nil pointer for a + // value receiver, and in either case, "" is a nice result. + if v := reflect.ValueOf(err); v.Kind() == reflect.Ptr && v.IsNil() { + enc.AddString(key, "") + return + } + + retErr = fmt.Errorf("PANIC=%v", rerr) + } + }() + basic := err.Error() enc.AddString(key, basic) diff --git a/vendor/go.uber.org/zap/zapcore/field.go b/vendor/go.uber.org/zap/zapcore/field.go index 7e255d63e0ddb..95bdb0a126f49 100644 --- a/vendor/go.uber.org/zap/zapcore/field.go +++ b/vendor/go.uber.org/zap/zapcore/field.go @@ -92,6 +92,10 @@ const ( ErrorType // SkipType indicates that the field is a no-op. SkipType + + // InlineMarshalerType indicates that the field carries an ObjectMarshaler + // that should be inlined. + InlineMarshalerType ) // A Field is a marshaling operation used to add a key-value pair to a logger's @@ -115,6 +119,8 @@ func (f Field) AddTo(enc ObjectEncoder) { err = enc.AddArray(f.Key, f.Interface.(ArrayMarshaler)) case ObjectMarshalerType: err = enc.AddObject(f.Key, f.Interface.(ObjectMarshaler)) + case InlineMarshalerType: + err = f.Interface.(ObjectMarshaler).MarshalLogObject(enc) case BinaryType: enc.AddBinary(f.Key, f.Interface.([]byte)) case BoolType: @@ -167,7 +173,7 @@ func (f Field) AddTo(enc ObjectEncoder) { case StringerType: err = encodeStringer(f.Key, f.Interface, enc) case ErrorType: - encodeError(f.Key, f.Interface.(error), enc) + err = encodeError(f.Key, f.Interface.(error), enc) case SkipType: break default: diff --git a/vendor/go.uber.org/zap/zapcore/write_syncer.go b/vendor/go.uber.org/zap/zapcore/write_syncer.go index 209e25fe22417..d4a1af3d07865 100644 --- a/vendor/go.uber.org/zap/zapcore/write_syncer.go +++ b/vendor/go.uber.org/zap/zapcore/write_syncer.go @@ -91,8 +91,7 @@ func NewMultiWriteSyncer(ws ...WriteSyncer) WriteSyncer { if len(ws) == 1 { return ws[0] } - // Copy to protect against https://github.com/golang/go/issues/7809 - return multiWriteSyncer(append([]WriteSyncer(nil), ws...)) + return multiWriteSyncer(ws) } // See https://golang.org/src/io/multi.go diff --git a/vendor/golang.org/x/crypto/argon2/blamka_amd64.s b/vendor/golang.org/x/crypto/argon2/blamka_amd64.s index c4c84f07a01e9..b2cc0515049a7 100644 --- a/vendor/golang.org/x/crypto/argon2/blamka_amd64.s +++ b/vendor/golang.org/x/crypto/argon2/blamka_amd64.s @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build amd64 && gc && !purego // +build amd64,gc,!purego #include "textflag.h" diff --git a/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.s b/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.s index a78ab3b3d9e4d..4b9daa18d9d9c 100644 --- a/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.s +++ b/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.s @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.7 && amd64 && gc && !purego // +build go1.7,amd64,gc,!purego #include "textflag.h" diff --git a/vendor/golang.org/x/crypto/blake2b/blake2b_amd64.s b/vendor/golang.org/x/crypto/blake2b/blake2b_amd64.s index bb72a03913f63..ae75eb9afcd7a 100644 --- a/vendor/golang.org/x/crypto/blake2b/blake2b_amd64.s +++ b/vendor/golang.org/x/crypto/blake2b/blake2b_amd64.s @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build amd64 && gc && !purego // +build amd64,gc,!purego #include "textflag.h" diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_arm64.s b/vendor/golang.org/x/crypto/chacha20/chacha_arm64.s index 8fb49a13e3bf2..63cae9e6f0b1b 100644 --- a/vendor/golang.org/x/crypto/chacha20/chacha_arm64.s +++ b/vendor/golang.org/x/crypto/chacha20/chacha_arm64.s @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.11 && gc && !purego // +build go1.11,gc,!purego #include "textflag.h" diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.s b/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.s index 3dad4b2fa27b6..5c0fed26f8502 100644 --- a/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.s +++ b/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.s @@ -19,6 +19,7 @@ // The differences in this and the original implementation are // due to the calling conventions and initialization of constants. +//go:build gc && !purego // +build gc,!purego #include "textflag.h" diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_s390x.s b/vendor/golang.org/x/crypto/chacha20/chacha_s390x.s index 818161189bc4b..f3ef5a019d959 100644 --- a/vendor/golang.org/x/crypto/chacha20/chacha_s390x.s +++ b/vendor/golang.org/x/crypto/chacha20/chacha_s390x.s @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build gc && !purego // +build gc,!purego #include "go_asm.h" diff --git a/vendor/golang.org/x/crypto/curve25519/curve25519.go b/vendor/golang.org/x/crypto/curve25519/curve25519.go index 4b9a655d1b562..cda3fdd3540dd 100644 --- a/vendor/golang.org/x/crypto/curve25519/curve25519.go +++ b/vendor/golang.org/x/crypto/curve25519/curve25519.go @@ -10,6 +10,8 @@ package curve25519 // import "golang.org/x/crypto/curve25519" import ( "crypto/subtle" "fmt" + + "golang.org/x/crypto/curve25519/internal/field" ) // ScalarMult sets dst to the product scalar * point. @@ -18,7 +20,55 @@ import ( // zeroes, irrespective of the scalar. Instead, use the X25519 function, which // will return an error. func ScalarMult(dst, scalar, point *[32]byte) { - scalarMult(dst, scalar, point) + var e [32]byte + + copy(e[:], scalar[:]) + e[0] &= 248 + e[31] &= 127 + e[31] |= 64 + + var x1, x2, z2, x3, z3, tmp0, tmp1 field.Element + x1.SetBytes(point[:]) + x2.One() + x3.Set(&x1) + z3.One() + + swap := 0 + for pos := 254; pos >= 0; pos-- { + b := e[pos/8] >> uint(pos&7) + b &= 1 + swap ^= int(b) + x2.Swap(&x3, swap) + z2.Swap(&z3, swap) + swap = int(b) + + tmp0.Subtract(&x3, &z3) + tmp1.Subtract(&x2, &z2) + x2.Add(&x2, &z2) + z2.Add(&x3, &z3) + z3.Multiply(&tmp0, &x2) + z2.Multiply(&z2, &tmp1) + tmp0.Square(&tmp1) + tmp1.Square(&x2) + x3.Add(&z3, &z2) + z2.Subtract(&z3, &z2) + x2.Multiply(&tmp1, &tmp0) + tmp1.Subtract(&tmp1, &tmp0) + z2.Square(&z2) + + z3.Mult32(&tmp1, 121666) + x3.Square(&x3) + tmp0.Add(&tmp0, &z3) + z3.Multiply(&x1, &z2) + z2.Multiply(&tmp1, &tmp0) + } + + x2.Swap(&x3, swap) + z2.Swap(&z3, swap) + + z2.Invert(&z2) + x2.Multiply(&x2, &z2) + copy(dst[:], x2.Bytes()) } // ScalarBaseMult sets dst to the product scalar * base where base is the diff --git a/vendor/golang.org/x/crypto/curve25519/curve25519_amd64.go b/vendor/golang.org/x/crypto/curve25519/curve25519_amd64.go deleted file mode 100644 index 84858480dff5f..0000000000000 --- a/vendor/golang.org/x/crypto/curve25519/curve25519_amd64.go +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright 2012 The 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. - -//go:build amd64 && gc && !purego -// +build amd64,gc,!purego - -package curve25519 - -// These functions are implemented in the .s files. The names of the functions -// in the rest of the file are also taken from the SUPERCOP sources to help -// people following along. - -//go:noescape - -func cswap(inout *[5]uint64, v uint64) - -//go:noescape - -func ladderstep(inout *[5][5]uint64) - -//go:noescape - -func freeze(inout *[5]uint64) - -//go:noescape - -func mul(dest, a, b *[5]uint64) - -//go:noescape - -func square(out, in *[5]uint64) - -// mladder uses a Montgomery ladder to calculate (xr/zr) *= s. -func mladder(xr, zr *[5]uint64, s *[32]byte) { - var work [5][5]uint64 - - work[0] = *xr - setint(&work[1], 1) - setint(&work[2], 0) - work[3] = *xr - setint(&work[4], 1) - - j := uint(6) - var prevbit byte - - for i := 31; i >= 0; i-- { - for j < 8 { - bit := ((*s)[i] >> j) & 1 - swap := bit ^ prevbit - prevbit = bit - cswap(&work[1], uint64(swap)) - ladderstep(&work) - j-- - } - j = 7 - } - - *xr = work[1] - *zr = work[2] -} - -func scalarMult(out, in, base *[32]byte) { - var e [32]byte - copy(e[:], (*in)[:]) - e[0] &= 248 - e[31] &= 127 - e[31] |= 64 - - var t, z [5]uint64 - unpack(&t, base) - mladder(&t, &z, &e) - invert(&z, &z) - mul(&t, &t, &z) - pack(out, &t) -} - -func setint(r *[5]uint64, v uint64) { - r[0] = v - r[1] = 0 - r[2] = 0 - r[3] = 0 - r[4] = 0 -} - -// unpack sets r = x where r consists of 5, 51-bit limbs in little-endian -// order. -func unpack(r *[5]uint64, x *[32]byte) { - r[0] = uint64(x[0]) | - uint64(x[1])<<8 | - uint64(x[2])<<16 | - uint64(x[3])<<24 | - uint64(x[4])<<32 | - uint64(x[5])<<40 | - uint64(x[6]&7)<<48 - - r[1] = uint64(x[6])>>3 | - uint64(x[7])<<5 | - uint64(x[8])<<13 | - uint64(x[9])<<21 | - uint64(x[10])<<29 | - uint64(x[11])<<37 | - uint64(x[12]&63)<<45 - - r[2] = uint64(x[12])>>6 | - uint64(x[13])<<2 | - uint64(x[14])<<10 | - uint64(x[15])<<18 | - uint64(x[16])<<26 | - uint64(x[17])<<34 | - uint64(x[18])<<42 | - uint64(x[19]&1)<<50 - - r[3] = uint64(x[19])>>1 | - uint64(x[20])<<7 | - uint64(x[21])<<15 | - uint64(x[22])<<23 | - uint64(x[23])<<31 | - uint64(x[24])<<39 | - uint64(x[25]&15)<<47 - - r[4] = uint64(x[25])>>4 | - uint64(x[26])<<4 | - uint64(x[27])<<12 | - uint64(x[28])<<20 | - uint64(x[29])<<28 | - uint64(x[30])<<36 | - uint64(x[31]&127)<<44 -} - -// pack sets out = x where out is the usual, little-endian form of the 5, -// 51-bit limbs in x. -func pack(out *[32]byte, x *[5]uint64) { - t := *x - freeze(&t) - - out[0] = byte(t[0]) - out[1] = byte(t[0] >> 8) - out[2] = byte(t[0] >> 16) - out[3] = byte(t[0] >> 24) - out[4] = byte(t[0] >> 32) - out[5] = byte(t[0] >> 40) - out[6] = byte(t[0] >> 48) - - out[6] ^= byte(t[1]<<3) & 0xf8 - out[7] = byte(t[1] >> 5) - out[8] = byte(t[1] >> 13) - out[9] = byte(t[1] >> 21) - out[10] = byte(t[1] >> 29) - out[11] = byte(t[1] >> 37) - out[12] = byte(t[1] >> 45) - - out[12] ^= byte(t[2]<<6) & 0xc0 - out[13] = byte(t[2] >> 2) - out[14] = byte(t[2] >> 10) - out[15] = byte(t[2] >> 18) - out[16] = byte(t[2] >> 26) - out[17] = byte(t[2] >> 34) - out[18] = byte(t[2] >> 42) - out[19] = byte(t[2] >> 50) - - out[19] ^= byte(t[3]<<1) & 0xfe - out[20] = byte(t[3] >> 7) - out[21] = byte(t[3] >> 15) - out[22] = byte(t[3] >> 23) - out[23] = byte(t[3] >> 31) - out[24] = byte(t[3] >> 39) - out[25] = byte(t[3] >> 47) - - out[25] ^= byte(t[4]<<4) & 0xf0 - out[26] = byte(t[4] >> 4) - out[27] = byte(t[4] >> 12) - out[28] = byte(t[4] >> 20) - out[29] = byte(t[4] >> 28) - out[30] = byte(t[4] >> 36) - out[31] = byte(t[4] >> 44) -} - -// invert calculates r = x^-1 mod p using Fermat's little theorem. -func invert(r *[5]uint64, x *[5]uint64) { - var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t [5]uint64 - - square(&z2, x) /* 2 */ - square(&t, &z2) /* 4 */ - square(&t, &t) /* 8 */ - mul(&z9, &t, x) /* 9 */ - mul(&z11, &z9, &z2) /* 11 */ - square(&t, &z11) /* 22 */ - mul(&z2_5_0, &t, &z9) /* 2^5 - 2^0 = 31 */ - - square(&t, &z2_5_0) /* 2^6 - 2^1 */ - for i := 1; i < 5; i++ { /* 2^20 - 2^10 */ - square(&t, &t) - } - mul(&z2_10_0, &t, &z2_5_0) /* 2^10 - 2^0 */ - - square(&t, &z2_10_0) /* 2^11 - 2^1 */ - for i := 1; i < 10; i++ { /* 2^20 - 2^10 */ - square(&t, &t) - } - mul(&z2_20_0, &t, &z2_10_0) /* 2^20 - 2^0 */ - - square(&t, &z2_20_0) /* 2^21 - 2^1 */ - for i := 1; i < 20; i++ { /* 2^40 - 2^20 */ - square(&t, &t) - } - mul(&t, &t, &z2_20_0) /* 2^40 - 2^0 */ - - square(&t, &t) /* 2^41 - 2^1 */ - for i := 1; i < 10; i++ { /* 2^50 - 2^10 */ - square(&t, &t) - } - mul(&z2_50_0, &t, &z2_10_0) /* 2^50 - 2^0 */ - - square(&t, &z2_50_0) /* 2^51 - 2^1 */ - for i := 1; i < 50; i++ { /* 2^100 - 2^50 */ - square(&t, &t) - } - mul(&z2_100_0, &t, &z2_50_0) /* 2^100 - 2^0 */ - - square(&t, &z2_100_0) /* 2^101 - 2^1 */ - for i := 1; i < 100; i++ { /* 2^200 - 2^100 */ - square(&t, &t) - } - mul(&t, &t, &z2_100_0) /* 2^200 - 2^0 */ - - square(&t, &t) /* 2^201 - 2^1 */ - for i := 1; i < 50; i++ { /* 2^250 - 2^50 */ - square(&t, &t) - } - mul(&t, &t, &z2_50_0) /* 2^250 - 2^0 */ - - square(&t, &t) /* 2^251 - 2^1 */ - square(&t, &t) /* 2^252 - 2^2 */ - square(&t, &t) /* 2^253 - 2^3 */ - - square(&t, &t) /* 2^254 - 2^4 */ - - square(&t, &t) /* 2^255 - 2^5 */ - mul(r, &t, &z11) /* 2^255 - 21 */ -} diff --git a/vendor/golang.org/x/crypto/curve25519/curve25519_amd64.s b/vendor/golang.org/x/crypto/curve25519/curve25519_amd64.s deleted file mode 100644 index 6c533809266b1..0000000000000 --- a/vendor/golang.org/x/crypto/curve25519/curve25519_amd64.s +++ /dev/null @@ -1,1793 +0,0 @@ -// Copyright 2012 The 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. - -// This code was translated into a form compatible with 6a from the public -// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html - -// +build amd64,gc,!purego - -#define REDMASK51 0x0007FFFFFFFFFFFF - -// These constants cannot be encoded in non-MOVQ immediates. -// We access them directly from memory instead. - -DATA ·_121666_213(SB)/8, $996687872 -GLOBL ·_121666_213(SB), 8, $8 - -DATA ·_2P0(SB)/8, $0xFFFFFFFFFFFDA -GLOBL ·_2P0(SB), 8, $8 - -DATA ·_2P1234(SB)/8, $0xFFFFFFFFFFFFE -GLOBL ·_2P1234(SB), 8, $8 - -// func freeze(inout *[5]uint64) -TEXT ·freeze(SB),7,$0-8 - MOVQ inout+0(FP), DI - - MOVQ 0(DI),SI - MOVQ 8(DI),DX - MOVQ 16(DI),CX - MOVQ 24(DI),R8 - MOVQ 32(DI),R9 - MOVQ $REDMASK51,AX - MOVQ AX,R10 - SUBQ $18,R10 - MOVQ $3,R11 -REDUCELOOP: - MOVQ SI,R12 - SHRQ $51,R12 - ANDQ AX,SI - ADDQ R12,DX - MOVQ DX,R12 - SHRQ $51,R12 - ANDQ AX,DX - ADDQ R12,CX - MOVQ CX,R12 - SHRQ $51,R12 - ANDQ AX,CX - ADDQ R12,R8 - MOVQ R8,R12 - SHRQ $51,R12 - ANDQ AX,R8 - ADDQ R12,R9 - MOVQ R9,R12 - SHRQ $51,R12 - ANDQ AX,R9 - IMUL3Q $19,R12,R12 - ADDQ R12,SI - SUBQ $1,R11 - JA REDUCELOOP - MOVQ $1,R12 - CMPQ R10,SI - CMOVQLT R11,R12 - CMPQ AX,DX - CMOVQNE R11,R12 - CMPQ AX,CX - CMOVQNE R11,R12 - CMPQ AX,R8 - CMOVQNE R11,R12 - CMPQ AX,R9 - CMOVQNE R11,R12 - NEGQ R12 - ANDQ R12,AX - ANDQ R12,R10 - SUBQ R10,SI - SUBQ AX,DX - SUBQ AX,CX - SUBQ AX,R8 - SUBQ AX,R9 - MOVQ SI,0(DI) - MOVQ DX,8(DI) - MOVQ CX,16(DI) - MOVQ R8,24(DI) - MOVQ R9,32(DI) - RET - -// func ladderstep(inout *[5][5]uint64) -TEXT ·ladderstep(SB),0,$296-8 - MOVQ inout+0(FP),DI - - MOVQ 40(DI),SI - MOVQ 48(DI),DX - MOVQ 56(DI),CX - MOVQ 64(DI),R8 - MOVQ 72(DI),R9 - MOVQ SI,AX - MOVQ DX,R10 - MOVQ CX,R11 - MOVQ R8,R12 - MOVQ R9,R13 - ADDQ ·_2P0(SB),AX - ADDQ ·_2P1234(SB),R10 - ADDQ ·_2P1234(SB),R11 - ADDQ ·_2P1234(SB),R12 - ADDQ ·_2P1234(SB),R13 - ADDQ 80(DI),SI - ADDQ 88(DI),DX - ADDQ 96(DI),CX - ADDQ 104(DI),R8 - ADDQ 112(DI),R9 - SUBQ 80(DI),AX - SUBQ 88(DI),R10 - SUBQ 96(DI),R11 - SUBQ 104(DI),R12 - SUBQ 112(DI),R13 - MOVQ SI,0(SP) - MOVQ DX,8(SP) - MOVQ CX,16(SP) - MOVQ R8,24(SP) - MOVQ R9,32(SP) - MOVQ AX,40(SP) - MOVQ R10,48(SP) - MOVQ R11,56(SP) - MOVQ R12,64(SP) - MOVQ R13,72(SP) - MOVQ 40(SP),AX - MULQ 40(SP) - MOVQ AX,SI - MOVQ DX,CX - MOVQ 40(SP),AX - SHLQ $1,AX - MULQ 48(SP) - MOVQ AX,R8 - MOVQ DX,R9 - MOVQ 40(SP),AX - SHLQ $1,AX - MULQ 56(SP) - MOVQ AX,R10 - MOVQ DX,R11 - MOVQ 40(SP),AX - SHLQ $1,AX - MULQ 64(SP) - MOVQ AX,R12 - MOVQ DX,R13 - MOVQ 40(SP),AX - SHLQ $1,AX - MULQ 72(SP) - MOVQ AX,R14 - MOVQ DX,R15 - MOVQ 48(SP),AX - MULQ 48(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 48(SP),AX - SHLQ $1,AX - MULQ 56(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 48(SP),AX - SHLQ $1,AX - MULQ 64(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 48(SP),DX - IMUL3Q $38,DX,AX - MULQ 72(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 56(SP),AX - MULQ 56(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 56(SP),DX - IMUL3Q $38,DX,AX - MULQ 64(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 56(SP),DX - IMUL3Q $38,DX,AX - MULQ 72(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 64(SP),DX - IMUL3Q $19,DX,AX - MULQ 64(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 64(SP),DX - IMUL3Q $38,DX,AX - MULQ 72(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 72(SP),DX - IMUL3Q $19,DX,AX - MULQ 72(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ $REDMASK51,DX - SHLQ $13,SI,CX - ANDQ DX,SI - SHLQ $13,R8,R9 - ANDQ DX,R8 - ADDQ CX,R8 - SHLQ $13,R10,R11 - ANDQ DX,R10 - ADDQ R9,R10 - SHLQ $13,R12,R13 - ANDQ DX,R12 - ADDQ R11,R12 - SHLQ $13,R14,R15 - ANDQ DX,R14 - ADDQ R13,R14 - IMUL3Q $19,R15,CX - ADDQ CX,SI - MOVQ SI,CX - SHRQ $51,CX - ADDQ R8,CX - ANDQ DX,SI - MOVQ CX,R8 - SHRQ $51,CX - ADDQ R10,CX - ANDQ DX,R8 - MOVQ CX,R9 - SHRQ $51,CX - ADDQ R12,CX - ANDQ DX,R9 - MOVQ CX,AX - SHRQ $51,CX - ADDQ R14,CX - ANDQ DX,AX - MOVQ CX,R10 - SHRQ $51,CX - IMUL3Q $19,CX,CX - ADDQ CX,SI - ANDQ DX,R10 - MOVQ SI,80(SP) - MOVQ R8,88(SP) - MOVQ R9,96(SP) - MOVQ AX,104(SP) - MOVQ R10,112(SP) - MOVQ 0(SP),AX - MULQ 0(SP) - MOVQ AX,SI - MOVQ DX,CX - MOVQ 0(SP),AX - SHLQ $1,AX - MULQ 8(SP) - MOVQ AX,R8 - MOVQ DX,R9 - MOVQ 0(SP),AX - SHLQ $1,AX - MULQ 16(SP) - MOVQ AX,R10 - MOVQ DX,R11 - MOVQ 0(SP),AX - SHLQ $1,AX - MULQ 24(SP) - MOVQ AX,R12 - MOVQ DX,R13 - MOVQ 0(SP),AX - SHLQ $1,AX - MULQ 32(SP) - MOVQ AX,R14 - MOVQ DX,R15 - MOVQ 8(SP),AX - MULQ 8(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 8(SP),AX - SHLQ $1,AX - MULQ 16(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 8(SP),AX - SHLQ $1,AX - MULQ 24(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 8(SP),DX - IMUL3Q $38,DX,AX - MULQ 32(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 16(SP),AX - MULQ 16(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 16(SP),DX - IMUL3Q $38,DX,AX - MULQ 24(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 16(SP),DX - IMUL3Q $38,DX,AX - MULQ 32(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 24(SP),DX - IMUL3Q $19,DX,AX - MULQ 24(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 24(SP),DX - IMUL3Q $38,DX,AX - MULQ 32(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 32(SP),DX - IMUL3Q $19,DX,AX - MULQ 32(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ $REDMASK51,DX - SHLQ $13,SI,CX - ANDQ DX,SI - SHLQ $13,R8,R9 - ANDQ DX,R8 - ADDQ CX,R8 - SHLQ $13,R10,R11 - ANDQ DX,R10 - ADDQ R9,R10 - SHLQ $13,R12,R13 - ANDQ DX,R12 - ADDQ R11,R12 - SHLQ $13,R14,R15 - ANDQ DX,R14 - ADDQ R13,R14 - IMUL3Q $19,R15,CX - ADDQ CX,SI - MOVQ SI,CX - SHRQ $51,CX - ADDQ R8,CX - ANDQ DX,SI - MOVQ CX,R8 - SHRQ $51,CX - ADDQ R10,CX - ANDQ DX,R8 - MOVQ CX,R9 - SHRQ $51,CX - ADDQ R12,CX - ANDQ DX,R9 - MOVQ CX,AX - SHRQ $51,CX - ADDQ R14,CX - ANDQ DX,AX - MOVQ CX,R10 - SHRQ $51,CX - IMUL3Q $19,CX,CX - ADDQ CX,SI - ANDQ DX,R10 - MOVQ SI,120(SP) - MOVQ R8,128(SP) - MOVQ R9,136(SP) - MOVQ AX,144(SP) - MOVQ R10,152(SP) - MOVQ SI,SI - MOVQ R8,DX - MOVQ R9,CX - MOVQ AX,R8 - MOVQ R10,R9 - ADDQ ·_2P0(SB),SI - ADDQ ·_2P1234(SB),DX - ADDQ ·_2P1234(SB),CX - ADDQ ·_2P1234(SB),R8 - ADDQ ·_2P1234(SB),R9 - SUBQ 80(SP),SI - SUBQ 88(SP),DX - SUBQ 96(SP),CX - SUBQ 104(SP),R8 - SUBQ 112(SP),R9 - MOVQ SI,160(SP) - MOVQ DX,168(SP) - MOVQ CX,176(SP) - MOVQ R8,184(SP) - MOVQ R9,192(SP) - MOVQ 120(DI),SI - MOVQ 128(DI),DX - MOVQ 136(DI),CX - MOVQ 144(DI),R8 - MOVQ 152(DI),R9 - MOVQ SI,AX - MOVQ DX,R10 - MOVQ CX,R11 - MOVQ R8,R12 - MOVQ R9,R13 - ADDQ ·_2P0(SB),AX - ADDQ ·_2P1234(SB),R10 - ADDQ ·_2P1234(SB),R11 - ADDQ ·_2P1234(SB),R12 - ADDQ ·_2P1234(SB),R13 - ADDQ 160(DI),SI - ADDQ 168(DI),DX - ADDQ 176(DI),CX - ADDQ 184(DI),R8 - ADDQ 192(DI),R9 - SUBQ 160(DI),AX - SUBQ 168(DI),R10 - SUBQ 176(DI),R11 - SUBQ 184(DI),R12 - SUBQ 192(DI),R13 - MOVQ SI,200(SP) - MOVQ DX,208(SP) - MOVQ CX,216(SP) - MOVQ R8,224(SP) - MOVQ R9,232(SP) - MOVQ AX,240(SP) - MOVQ R10,248(SP) - MOVQ R11,256(SP) - MOVQ R12,264(SP) - MOVQ R13,272(SP) - MOVQ 224(SP),SI - IMUL3Q $19,SI,AX - MOVQ AX,280(SP) - MULQ 56(SP) - MOVQ AX,SI - MOVQ DX,CX - MOVQ 232(SP),DX - IMUL3Q $19,DX,AX - MOVQ AX,288(SP) - MULQ 48(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 200(SP),AX - MULQ 40(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 200(SP),AX - MULQ 48(SP) - MOVQ AX,R8 - MOVQ DX,R9 - MOVQ 200(SP),AX - MULQ 56(SP) - MOVQ AX,R10 - MOVQ DX,R11 - MOVQ 200(SP),AX - MULQ 64(SP) - MOVQ AX,R12 - MOVQ DX,R13 - MOVQ 200(SP),AX - MULQ 72(SP) - MOVQ AX,R14 - MOVQ DX,R15 - MOVQ 208(SP),AX - MULQ 40(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 208(SP),AX - MULQ 48(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 208(SP),AX - MULQ 56(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 208(SP),AX - MULQ 64(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 208(SP),DX - IMUL3Q $19,DX,AX - MULQ 72(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 216(SP),AX - MULQ 40(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 216(SP),AX - MULQ 48(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 216(SP),AX - MULQ 56(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 216(SP),DX - IMUL3Q $19,DX,AX - MULQ 64(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 216(SP),DX - IMUL3Q $19,DX,AX - MULQ 72(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 224(SP),AX - MULQ 40(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 224(SP),AX - MULQ 48(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 280(SP),AX - MULQ 64(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 280(SP),AX - MULQ 72(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 232(SP),AX - MULQ 40(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 288(SP),AX - MULQ 56(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 288(SP),AX - MULQ 64(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 288(SP),AX - MULQ 72(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ $REDMASK51,DX - SHLQ $13,SI,CX - ANDQ DX,SI - SHLQ $13,R8,R9 - ANDQ DX,R8 - ADDQ CX,R8 - SHLQ $13,R10,R11 - ANDQ DX,R10 - ADDQ R9,R10 - SHLQ $13,R12,R13 - ANDQ DX,R12 - ADDQ R11,R12 - SHLQ $13,R14,R15 - ANDQ DX,R14 - ADDQ R13,R14 - IMUL3Q $19,R15,CX - ADDQ CX,SI - MOVQ SI,CX - SHRQ $51,CX - ADDQ R8,CX - MOVQ CX,R8 - SHRQ $51,CX - ANDQ DX,SI - ADDQ R10,CX - MOVQ CX,R9 - SHRQ $51,CX - ANDQ DX,R8 - ADDQ R12,CX - MOVQ CX,AX - SHRQ $51,CX - ANDQ DX,R9 - ADDQ R14,CX - MOVQ CX,R10 - SHRQ $51,CX - ANDQ DX,AX - IMUL3Q $19,CX,CX - ADDQ CX,SI - ANDQ DX,R10 - MOVQ SI,40(SP) - MOVQ R8,48(SP) - MOVQ R9,56(SP) - MOVQ AX,64(SP) - MOVQ R10,72(SP) - MOVQ 264(SP),SI - IMUL3Q $19,SI,AX - MOVQ AX,200(SP) - MULQ 16(SP) - MOVQ AX,SI - MOVQ DX,CX - MOVQ 272(SP),DX - IMUL3Q $19,DX,AX - MOVQ AX,208(SP) - MULQ 8(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 240(SP),AX - MULQ 0(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 240(SP),AX - MULQ 8(SP) - MOVQ AX,R8 - MOVQ DX,R9 - MOVQ 240(SP),AX - MULQ 16(SP) - MOVQ AX,R10 - MOVQ DX,R11 - MOVQ 240(SP),AX - MULQ 24(SP) - MOVQ AX,R12 - MOVQ DX,R13 - MOVQ 240(SP),AX - MULQ 32(SP) - MOVQ AX,R14 - MOVQ DX,R15 - MOVQ 248(SP),AX - MULQ 0(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 248(SP),AX - MULQ 8(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 248(SP),AX - MULQ 16(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 248(SP),AX - MULQ 24(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 248(SP),DX - IMUL3Q $19,DX,AX - MULQ 32(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 256(SP),AX - MULQ 0(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 256(SP),AX - MULQ 8(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 256(SP),AX - MULQ 16(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 256(SP),DX - IMUL3Q $19,DX,AX - MULQ 24(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 256(SP),DX - IMUL3Q $19,DX,AX - MULQ 32(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 264(SP),AX - MULQ 0(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 264(SP),AX - MULQ 8(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 200(SP),AX - MULQ 24(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 200(SP),AX - MULQ 32(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 272(SP),AX - MULQ 0(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 208(SP),AX - MULQ 16(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 208(SP),AX - MULQ 24(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 208(SP),AX - MULQ 32(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ $REDMASK51,DX - SHLQ $13,SI,CX - ANDQ DX,SI - SHLQ $13,R8,R9 - ANDQ DX,R8 - ADDQ CX,R8 - SHLQ $13,R10,R11 - ANDQ DX,R10 - ADDQ R9,R10 - SHLQ $13,R12,R13 - ANDQ DX,R12 - ADDQ R11,R12 - SHLQ $13,R14,R15 - ANDQ DX,R14 - ADDQ R13,R14 - IMUL3Q $19,R15,CX - ADDQ CX,SI - MOVQ SI,CX - SHRQ $51,CX - ADDQ R8,CX - MOVQ CX,R8 - SHRQ $51,CX - ANDQ DX,SI - ADDQ R10,CX - MOVQ CX,R9 - SHRQ $51,CX - ANDQ DX,R8 - ADDQ R12,CX - MOVQ CX,AX - SHRQ $51,CX - ANDQ DX,R9 - ADDQ R14,CX - MOVQ CX,R10 - SHRQ $51,CX - ANDQ DX,AX - IMUL3Q $19,CX,CX - ADDQ CX,SI - ANDQ DX,R10 - MOVQ SI,DX - MOVQ R8,CX - MOVQ R9,R11 - MOVQ AX,R12 - MOVQ R10,R13 - ADDQ ·_2P0(SB),DX - ADDQ ·_2P1234(SB),CX - ADDQ ·_2P1234(SB),R11 - ADDQ ·_2P1234(SB),R12 - ADDQ ·_2P1234(SB),R13 - ADDQ 40(SP),SI - ADDQ 48(SP),R8 - ADDQ 56(SP),R9 - ADDQ 64(SP),AX - ADDQ 72(SP),R10 - SUBQ 40(SP),DX - SUBQ 48(SP),CX - SUBQ 56(SP),R11 - SUBQ 64(SP),R12 - SUBQ 72(SP),R13 - MOVQ SI,120(DI) - MOVQ R8,128(DI) - MOVQ R9,136(DI) - MOVQ AX,144(DI) - MOVQ R10,152(DI) - MOVQ DX,160(DI) - MOVQ CX,168(DI) - MOVQ R11,176(DI) - MOVQ R12,184(DI) - MOVQ R13,192(DI) - MOVQ 120(DI),AX - MULQ 120(DI) - MOVQ AX,SI - MOVQ DX,CX - MOVQ 120(DI),AX - SHLQ $1,AX - MULQ 128(DI) - MOVQ AX,R8 - MOVQ DX,R9 - MOVQ 120(DI),AX - SHLQ $1,AX - MULQ 136(DI) - MOVQ AX,R10 - MOVQ DX,R11 - MOVQ 120(DI),AX - SHLQ $1,AX - MULQ 144(DI) - MOVQ AX,R12 - MOVQ DX,R13 - MOVQ 120(DI),AX - SHLQ $1,AX - MULQ 152(DI) - MOVQ AX,R14 - MOVQ DX,R15 - MOVQ 128(DI),AX - MULQ 128(DI) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 128(DI),AX - SHLQ $1,AX - MULQ 136(DI) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 128(DI),AX - SHLQ $1,AX - MULQ 144(DI) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 128(DI),DX - IMUL3Q $38,DX,AX - MULQ 152(DI) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 136(DI),AX - MULQ 136(DI) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 136(DI),DX - IMUL3Q $38,DX,AX - MULQ 144(DI) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 136(DI),DX - IMUL3Q $38,DX,AX - MULQ 152(DI) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 144(DI),DX - IMUL3Q $19,DX,AX - MULQ 144(DI) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 144(DI),DX - IMUL3Q $38,DX,AX - MULQ 152(DI) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 152(DI),DX - IMUL3Q $19,DX,AX - MULQ 152(DI) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ $REDMASK51,DX - SHLQ $13,SI,CX - ANDQ DX,SI - SHLQ $13,R8,R9 - ANDQ DX,R8 - ADDQ CX,R8 - SHLQ $13,R10,R11 - ANDQ DX,R10 - ADDQ R9,R10 - SHLQ $13,R12,R13 - ANDQ DX,R12 - ADDQ R11,R12 - SHLQ $13,R14,R15 - ANDQ DX,R14 - ADDQ R13,R14 - IMUL3Q $19,R15,CX - ADDQ CX,SI - MOVQ SI,CX - SHRQ $51,CX - ADDQ R8,CX - ANDQ DX,SI - MOVQ CX,R8 - SHRQ $51,CX - ADDQ R10,CX - ANDQ DX,R8 - MOVQ CX,R9 - SHRQ $51,CX - ADDQ R12,CX - ANDQ DX,R9 - MOVQ CX,AX - SHRQ $51,CX - ADDQ R14,CX - ANDQ DX,AX - MOVQ CX,R10 - SHRQ $51,CX - IMUL3Q $19,CX,CX - ADDQ CX,SI - ANDQ DX,R10 - MOVQ SI,120(DI) - MOVQ R8,128(DI) - MOVQ R9,136(DI) - MOVQ AX,144(DI) - MOVQ R10,152(DI) - MOVQ 160(DI),AX - MULQ 160(DI) - MOVQ AX,SI - MOVQ DX,CX - MOVQ 160(DI),AX - SHLQ $1,AX - MULQ 168(DI) - MOVQ AX,R8 - MOVQ DX,R9 - MOVQ 160(DI),AX - SHLQ $1,AX - MULQ 176(DI) - MOVQ AX,R10 - MOVQ DX,R11 - MOVQ 160(DI),AX - SHLQ $1,AX - MULQ 184(DI) - MOVQ AX,R12 - MOVQ DX,R13 - MOVQ 160(DI),AX - SHLQ $1,AX - MULQ 192(DI) - MOVQ AX,R14 - MOVQ DX,R15 - MOVQ 168(DI),AX - MULQ 168(DI) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 168(DI),AX - SHLQ $1,AX - MULQ 176(DI) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 168(DI),AX - SHLQ $1,AX - MULQ 184(DI) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 168(DI),DX - IMUL3Q $38,DX,AX - MULQ 192(DI) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 176(DI),AX - MULQ 176(DI) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 176(DI),DX - IMUL3Q $38,DX,AX - MULQ 184(DI) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 176(DI),DX - IMUL3Q $38,DX,AX - MULQ 192(DI) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 184(DI),DX - IMUL3Q $19,DX,AX - MULQ 184(DI) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 184(DI),DX - IMUL3Q $38,DX,AX - MULQ 192(DI) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 192(DI),DX - IMUL3Q $19,DX,AX - MULQ 192(DI) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ $REDMASK51,DX - SHLQ $13,SI,CX - ANDQ DX,SI - SHLQ $13,R8,R9 - ANDQ DX,R8 - ADDQ CX,R8 - SHLQ $13,R10,R11 - ANDQ DX,R10 - ADDQ R9,R10 - SHLQ $13,R12,R13 - ANDQ DX,R12 - ADDQ R11,R12 - SHLQ $13,R14,R15 - ANDQ DX,R14 - ADDQ R13,R14 - IMUL3Q $19,R15,CX - ADDQ CX,SI - MOVQ SI,CX - SHRQ $51,CX - ADDQ R8,CX - ANDQ DX,SI - MOVQ CX,R8 - SHRQ $51,CX - ADDQ R10,CX - ANDQ DX,R8 - MOVQ CX,R9 - SHRQ $51,CX - ADDQ R12,CX - ANDQ DX,R9 - MOVQ CX,AX - SHRQ $51,CX - ADDQ R14,CX - ANDQ DX,AX - MOVQ CX,R10 - SHRQ $51,CX - IMUL3Q $19,CX,CX - ADDQ CX,SI - ANDQ DX,R10 - MOVQ SI,160(DI) - MOVQ R8,168(DI) - MOVQ R9,176(DI) - MOVQ AX,184(DI) - MOVQ R10,192(DI) - MOVQ 184(DI),SI - IMUL3Q $19,SI,AX - MOVQ AX,0(SP) - MULQ 16(DI) - MOVQ AX,SI - MOVQ DX,CX - MOVQ 192(DI),DX - IMUL3Q $19,DX,AX - MOVQ AX,8(SP) - MULQ 8(DI) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 160(DI),AX - MULQ 0(DI) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 160(DI),AX - MULQ 8(DI) - MOVQ AX,R8 - MOVQ DX,R9 - MOVQ 160(DI),AX - MULQ 16(DI) - MOVQ AX,R10 - MOVQ DX,R11 - MOVQ 160(DI),AX - MULQ 24(DI) - MOVQ AX,R12 - MOVQ DX,R13 - MOVQ 160(DI),AX - MULQ 32(DI) - MOVQ AX,R14 - MOVQ DX,R15 - MOVQ 168(DI),AX - MULQ 0(DI) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 168(DI),AX - MULQ 8(DI) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 168(DI),AX - MULQ 16(DI) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 168(DI),AX - MULQ 24(DI) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 168(DI),DX - IMUL3Q $19,DX,AX - MULQ 32(DI) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 176(DI),AX - MULQ 0(DI) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 176(DI),AX - MULQ 8(DI) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 176(DI),AX - MULQ 16(DI) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 176(DI),DX - IMUL3Q $19,DX,AX - MULQ 24(DI) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 176(DI),DX - IMUL3Q $19,DX,AX - MULQ 32(DI) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 184(DI),AX - MULQ 0(DI) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 184(DI),AX - MULQ 8(DI) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 0(SP),AX - MULQ 24(DI) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 0(SP),AX - MULQ 32(DI) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 192(DI),AX - MULQ 0(DI) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 8(SP),AX - MULQ 16(DI) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 8(SP),AX - MULQ 24(DI) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 8(SP),AX - MULQ 32(DI) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ $REDMASK51,DX - SHLQ $13,SI,CX - ANDQ DX,SI - SHLQ $13,R8,R9 - ANDQ DX,R8 - ADDQ CX,R8 - SHLQ $13,R10,R11 - ANDQ DX,R10 - ADDQ R9,R10 - SHLQ $13,R12,R13 - ANDQ DX,R12 - ADDQ R11,R12 - SHLQ $13,R14,R15 - ANDQ DX,R14 - ADDQ R13,R14 - IMUL3Q $19,R15,CX - ADDQ CX,SI - MOVQ SI,CX - SHRQ $51,CX - ADDQ R8,CX - MOVQ CX,R8 - SHRQ $51,CX - ANDQ DX,SI - ADDQ R10,CX - MOVQ CX,R9 - SHRQ $51,CX - ANDQ DX,R8 - ADDQ R12,CX - MOVQ CX,AX - SHRQ $51,CX - ANDQ DX,R9 - ADDQ R14,CX - MOVQ CX,R10 - SHRQ $51,CX - ANDQ DX,AX - IMUL3Q $19,CX,CX - ADDQ CX,SI - ANDQ DX,R10 - MOVQ SI,160(DI) - MOVQ R8,168(DI) - MOVQ R9,176(DI) - MOVQ AX,184(DI) - MOVQ R10,192(DI) - MOVQ 144(SP),SI - IMUL3Q $19,SI,AX - MOVQ AX,0(SP) - MULQ 96(SP) - MOVQ AX,SI - MOVQ DX,CX - MOVQ 152(SP),DX - IMUL3Q $19,DX,AX - MOVQ AX,8(SP) - MULQ 88(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 120(SP),AX - MULQ 80(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 120(SP),AX - MULQ 88(SP) - MOVQ AX,R8 - MOVQ DX,R9 - MOVQ 120(SP),AX - MULQ 96(SP) - MOVQ AX,R10 - MOVQ DX,R11 - MOVQ 120(SP),AX - MULQ 104(SP) - MOVQ AX,R12 - MOVQ DX,R13 - MOVQ 120(SP),AX - MULQ 112(SP) - MOVQ AX,R14 - MOVQ DX,R15 - MOVQ 128(SP),AX - MULQ 80(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 128(SP),AX - MULQ 88(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 128(SP),AX - MULQ 96(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 128(SP),AX - MULQ 104(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 128(SP),DX - IMUL3Q $19,DX,AX - MULQ 112(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 136(SP),AX - MULQ 80(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 136(SP),AX - MULQ 88(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 136(SP),AX - MULQ 96(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 136(SP),DX - IMUL3Q $19,DX,AX - MULQ 104(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 136(SP),DX - IMUL3Q $19,DX,AX - MULQ 112(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 144(SP),AX - MULQ 80(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 144(SP),AX - MULQ 88(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 0(SP),AX - MULQ 104(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 0(SP),AX - MULQ 112(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 152(SP),AX - MULQ 80(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 8(SP),AX - MULQ 96(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 8(SP),AX - MULQ 104(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 8(SP),AX - MULQ 112(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ $REDMASK51,DX - SHLQ $13,SI,CX - ANDQ DX,SI - SHLQ $13,R8,R9 - ANDQ DX,R8 - ADDQ CX,R8 - SHLQ $13,R10,R11 - ANDQ DX,R10 - ADDQ R9,R10 - SHLQ $13,R12,R13 - ANDQ DX,R12 - ADDQ R11,R12 - SHLQ $13,R14,R15 - ANDQ DX,R14 - ADDQ R13,R14 - IMUL3Q $19,R15,CX - ADDQ CX,SI - MOVQ SI,CX - SHRQ $51,CX - ADDQ R8,CX - MOVQ CX,R8 - SHRQ $51,CX - ANDQ DX,SI - ADDQ R10,CX - MOVQ CX,R9 - SHRQ $51,CX - ANDQ DX,R8 - ADDQ R12,CX - MOVQ CX,AX - SHRQ $51,CX - ANDQ DX,R9 - ADDQ R14,CX - MOVQ CX,R10 - SHRQ $51,CX - ANDQ DX,AX - IMUL3Q $19,CX,CX - ADDQ CX,SI - ANDQ DX,R10 - MOVQ SI,40(DI) - MOVQ R8,48(DI) - MOVQ R9,56(DI) - MOVQ AX,64(DI) - MOVQ R10,72(DI) - MOVQ 160(SP),AX - MULQ ·_121666_213(SB) - SHRQ $13,AX - MOVQ AX,SI - MOVQ DX,CX - MOVQ 168(SP),AX - MULQ ·_121666_213(SB) - SHRQ $13,AX - ADDQ AX,CX - MOVQ DX,R8 - MOVQ 176(SP),AX - MULQ ·_121666_213(SB) - SHRQ $13,AX - ADDQ AX,R8 - MOVQ DX,R9 - MOVQ 184(SP),AX - MULQ ·_121666_213(SB) - SHRQ $13,AX - ADDQ AX,R9 - MOVQ DX,R10 - MOVQ 192(SP),AX - MULQ ·_121666_213(SB) - SHRQ $13,AX - ADDQ AX,R10 - IMUL3Q $19,DX,DX - ADDQ DX,SI - ADDQ 80(SP),SI - ADDQ 88(SP),CX - ADDQ 96(SP),R8 - ADDQ 104(SP),R9 - ADDQ 112(SP),R10 - MOVQ SI,80(DI) - MOVQ CX,88(DI) - MOVQ R8,96(DI) - MOVQ R9,104(DI) - MOVQ R10,112(DI) - MOVQ 104(DI),SI - IMUL3Q $19,SI,AX - MOVQ AX,0(SP) - MULQ 176(SP) - MOVQ AX,SI - MOVQ DX,CX - MOVQ 112(DI),DX - IMUL3Q $19,DX,AX - MOVQ AX,8(SP) - MULQ 168(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 80(DI),AX - MULQ 160(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 80(DI),AX - MULQ 168(SP) - MOVQ AX,R8 - MOVQ DX,R9 - MOVQ 80(DI),AX - MULQ 176(SP) - MOVQ AX,R10 - MOVQ DX,R11 - MOVQ 80(DI),AX - MULQ 184(SP) - MOVQ AX,R12 - MOVQ DX,R13 - MOVQ 80(DI),AX - MULQ 192(SP) - MOVQ AX,R14 - MOVQ DX,R15 - MOVQ 88(DI),AX - MULQ 160(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 88(DI),AX - MULQ 168(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 88(DI),AX - MULQ 176(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 88(DI),AX - MULQ 184(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 88(DI),DX - IMUL3Q $19,DX,AX - MULQ 192(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 96(DI),AX - MULQ 160(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 96(DI),AX - MULQ 168(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 96(DI),AX - MULQ 176(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 96(DI),DX - IMUL3Q $19,DX,AX - MULQ 184(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 96(DI),DX - IMUL3Q $19,DX,AX - MULQ 192(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 104(DI),AX - MULQ 160(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 104(DI),AX - MULQ 168(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 0(SP),AX - MULQ 184(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 0(SP),AX - MULQ 192(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 112(DI),AX - MULQ 160(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 8(SP),AX - MULQ 176(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 8(SP),AX - MULQ 184(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 8(SP),AX - MULQ 192(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ $REDMASK51,DX - SHLQ $13,SI,CX - ANDQ DX,SI - SHLQ $13,R8,R9 - ANDQ DX,R8 - ADDQ CX,R8 - SHLQ $13,R10,R11 - ANDQ DX,R10 - ADDQ R9,R10 - SHLQ $13,R12,R13 - ANDQ DX,R12 - ADDQ R11,R12 - SHLQ $13,R14,R15 - ANDQ DX,R14 - ADDQ R13,R14 - IMUL3Q $19,R15,CX - ADDQ CX,SI - MOVQ SI,CX - SHRQ $51,CX - ADDQ R8,CX - MOVQ CX,R8 - SHRQ $51,CX - ANDQ DX,SI - ADDQ R10,CX - MOVQ CX,R9 - SHRQ $51,CX - ANDQ DX,R8 - ADDQ R12,CX - MOVQ CX,AX - SHRQ $51,CX - ANDQ DX,R9 - ADDQ R14,CX - MOVQ CX,R10 - SHRQ $51,CX - ANDQ DX,AX - IMUL3Q $19,CX,CX - ADDQ CX,SI - ANDQ DX,R10 - MOVQ SI,80(DI) - MOVQ R8,88(DI) - MOVQ R9,96(DI) - MOVQ AX,104(DI) - MOVQ R10,112(DI) - RET - -// func cswap(inout *[4][5]uint64, v uint64) -TEXT ·cswap(SB),7,$0 - MOVQ inout+0(FP),DI - MOVQ v+8(FP),SI - - SUBQ $1, SI - NOTQ SI - MOVQ SI, X15 - PSHUFD $0x44, X15, X15 - - MOVOU 0(DI), X0 - MOVOU 16(DI), X2 - MOVOU 32(DI), X4 - MOVOU 48(DI), X6 - MOVOU 64(DI), X8 - MOVOU 80(DI), X1 - MOVOU 96(DI), X3 - MOVOU 112(DI), X5 - MOVOU 128(DI), X7 - MOVOU 144(DI), X9 - - MOVO X1, X10 - MOVO X3, X11 - MOVO X5, X12 - MOVO X7, X13 - MOVO X9, X14 - - PXOR X0, X10 - PXOR X2, X11 - PXOR X4, X12 - PXOR X6, X13 - PXOR X8, X14 - PAND X15, X10 - PAND X15, X11 - PAND X15, X12 - PAND X15, X13 - PAND X15, X14 - PXOR X10, X0 - PXOR X10, X1 - PXOR X11, X2 - PXOR X11, X3 - PXOR X12, X4 - PXOR X12, X5 - PXOR X13, X6 - PXOR X13, X7 - PXOR X14, X8 - PXOR X14, X9 - - MOVOU X0, 0(DI) - MOVOU X2, 16(DI) - MOVOU X4, 32(DI) - MOVOU X6, 48(DI) - MOVOU X8, 64(DI) - MOVOU X1, 80(DI) - MOVOU X3, 96(DI) - MOVOU X5, 112(DI) - MOVOU X7, 128(DI) - MOVOU X9, 144(DI) - RET - -// func mul(dest, a, b *[5]uint64) -TEXT ·mul(SB),0,$16-24 - MOVQ dest+0(FP), DI - MOVQ a+8(FP), SI - MOVQ b+16(FP), DX - - MOVQ DX,CX - MOVQ 24(SI),DX - IMUL3Q $19,DX,AX - MOVQ AX,0(SP) - MULQ 16(CX) - MOVQ AX,R8 - MOVQ DX,R9 - MOVQ 32(SI),DX - IMUL3Q $19,DX,AX - MOVQ AX,8(SP) - MULQ 8(CX) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 0(SI),AX - MULQ 0(CX) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 0(SI),AX - MULQ 8(CX) - MOVQ AX,R10 - MOVQ DX,R11 - MOVQ 0(SI),AX - MULQ 16(CX) - MOVQ AX,R12 - MOVQ DX,R13 - MOVQ 0(SI),AX - MULQ 24(CX) - MOVQ AX,R14 - MOVQ DX,R15 - MOVQ 0(SI),AX - MULQ 32(CX) - MOVQ AX,BX - MOVQ DX,BP - MOVQ 8(SI),AX - MULQ 0(CX) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 8(SI),AX - MULQ 8(CX) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 8(SI),AX - MULQ 16(CX) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 8(SI),AX - MULQ 24(CX) - ADDQ AX,BX - ADCQ DX,BP - MOVQ 8(SI),DX - IMUL3Q $19,DX,AX - MULQ 32(CX) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 16(SI),AX - MULQ 0(CX) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 16(SI),AX - MULQ 8(CX) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 16(SI),AX - MULQ 16(CX) - ADDQ AX,BX - ADCQ DX,BP - MOVQ 16(SI),DX - IMUL3Q $19,DX,AX - MULQ 24(CX) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 16(SI),DX - IMUL3Q $19,DX,AX - MULQ 32(CX) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 24(SI),AX - MULQ 0(CX) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 24(SI),AX - MULQ 8(CX) - ADDQ AX,BX - ADCQ DX,BP - MOVQ 0(SP),AX - MULQ 24(CX) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 0(SP),AX - MULQ 32(CX) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 32(SI),AX - MULQ 0(CX) - ADDQ AX,BX - ADCQ DX,BP - MOVQ 8(SP),AX - MULQ 16(CX) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 8(SP),AX - MULQ 24(CX) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 8(SP),AX - MULQ 32(CX) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ $REDMASK51,SI - SHLQ $13,R8,R9 - ANDQ SI,R8 - SHLQ $13,R10,R11 - ANDQ SI,R10 - ADDQ R9,R10 - SHLQ $13,R12,R13 - ANDQ SI,R12 - ADDQ R11,R12 - SHLQ $13,R14,R15 - ANDQ SI,R14 - ADDQ R13,R14 - SHLQ $13,BX,BP - ANDQ SI,BX - ADDQ R15,BX - IMUL3Q $19,BP,DX - ADDQ DX,R8 - MOVQ R8,DX - SHRQ $51,DX - ADDQ R10,DX - MOVQ DX,CX - SHRQ $51,DX - ANDQ SI,R8 - ADDQ R12,DX - MOVQ DX,R9 - SHRQ $51,DX - ANDQ SI,CX - ADDQ R14,DX - MOVQ DX,AX - SHRQ $51,DX - ANDQ SI,R9 - ADDQ BX,DX - MOVQ DX,R10 - SHRQ $51,DX - ANDQ SI,AX - IMUL3Q $19,DX,DX - ADDQ DX,R8 - ANDQ SI,R10 - MOVQ R8,0(DI) - MOVQ CX,8(DI) - MOVQ R9,16(DI) - MOVQ AX,24(DI) - MOVQ R10,32(DI) - RET - -// func square(out, in *[5]uint64) -TEXT ·square(SB),7,$0-16 - MOVQ out+0(FP), DI - MOVQ in+8(FP), SI - - MOVQ 0(SI),AX - MULQ 0(SI) - MOVQ AX,CX - MOVQ DX,R8 - MOVQ 0(SI),AX - SHLQ $1,AX - MULQ 8(SI) - MOVQ AX,R9 - MOVQ DX,R10 - MOVQ 0(SI),AX - SHLQ $1,AX - MULQ 16(SI) - MOVQ AX,R11 - MOVQ DX,R12 - MOVQ 0(SI),AX - SHLQ $1,AX - MULQ 24(SI) - MOVQ AX,R13 - MOVQ DX,R14 - MOVQ 0(SI),AX - SHLQ $1,AX - MULQ 32(SI) - MOVQ AX,R15 - MOVQ DX,BX - MOVQ 8(SI),AX - MULQ 8(SI) - ADDQ AX,R11 - ADCQ DX,R12 - MOVQ 8(SI),AX - SHLQ $1,AX - MULQ 16(SI) - ADDQ AX,R13 - ADCQ DX,R14 - MOVQ 8(SI),AX - SHLQ $1,AX - MULQ 24(SI) - ADDQ AX,R15 - ADCQ DX,BX - MOVQ 8(SI),DX - IMUL3Q $38,DX,AX - MULQ 32(SI) - ADDQ AX,CX - ADCQ DX,R8 - MOVQ 16(SI),AX - MULQ 16(SI) - ADDQ AX,R15 - ADCQ DX,BX - MOVQ 16(SI),DX - IMUL3Q $38,DX,AX - MULQ 24(SI) - ADDQ AX,CX - ADCQ DX,R8 - MOVQ 16(SI),DX - IMUL3Q $38,DX,AX - MULQ 32(SI) - ADDQ AX,R9 - ADCQ DX,R10 - MOVQ 24(SI),DX - IMUL3Q $19,DX,AX - MULQ 24(SI) - ADDQ AX,R9 - ADCQ DX,R10 - MOVQ 24(SI),DX - IMUL3Q $38,DX,AX - MULQ 32(SI) - ADDQ AX,R11 - ADCQ DX,R12 - MOVQ 32(SI),DX - IMUL3Q $19,DX,AX - MULQ 32(SI) - ADDQ AX,R13 - ADCQ DX,R14 - MOVQ $REDMASK51,SI - SHLQ $13,CX,R8 - ANDQ SI,CX - SHLQ $13,R9,R10 - ANDQ SI,R9 - ADDQ R8,R9 - SHLQ $13,R11,R12 - ANDQ SI,R11 - ADDQ R10,R11 - SHLQ $13,R13,R14 - ANDQ SI,R13 - ADDQ R12,R13 - SHLQ $13,R15,BX - ANDQ SI,R15 - ADDQ R14,R15 - IMUL3Q $19,BX,DX - ADDQ DX,CX - MOVQ CX,DX - SHRQ $51,DX - ADDQ R9,DX - ANDQ SI,CX - MOVQ DX,R8 - SHRQ $51,DX - ADDQ R11,DX - ANDQ SI,R8 - MOVQ DX,R9 - SHRQ $51,DX - ADDQ R13,DX - ANDQ SI,R9 - MOVQ DX,AX - SHRQ $51,DX - ADDQ R15,DX - ANDQ SI,AX - MOVQ DX,R10 - SHRQ $51,DX - IMUL3Q $19,DX,DX - ADDQ DX,CX - ANDQ SI,R10 - MOVQ CX,0(DI) - MOVQ R8,8(DI) - MOVQ R9,16(DI) - MOVQ AX,24(DI) - MOVQ R10,32(DI) - RET diff --git a/vendor/golang.org/x/crypto/curve25519/curve25519_generic.go b/vendor/golang.org/x/crypto/curve25519/curve25519_generic.go deleted file mode 100644 index c43b13fc83e70..0000000000000 --- a/vendor/golang.org/x/crypto/curve25519/curve25519_generic.go +++ /dev/null @@ -1,828 +0,0 @@ -// Copyright 2013 The 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 curve25519 - -import "encoding/binary" - -// This code is a port of the public domain, "ref10" implementation of -// curve25519 from SUPERCOP 20130419 by D. J. Bernstein. - -// fieldElement represents an element of the field GF(2^255 - 19). An element -// t, entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77 -// t[3]+2^102 t[4]+...+2^230 t[9]. Bounds on each t[i] vary depending on -// context. -type fieldElement [10]int32 - -func feZero(fe *fieldElement) { - for i := range fe { - fe[i] = 0 - } -} - -func feOne(fe *fieldElement) { - feZero(fe) - fe[0] = 1 -} - -func feAdd(dst, a, b *fieldElement) { - for i := range dst { - dst[i] = a[i] + b[i] - } -} - -func feSub(dst, a, b *fieldElement) { - for i := range dst { - dst[i] = a[i] - b[i] - } -} - -func feCopy(dst, src *fieldElement) { - for i := range dst { - dst[i] = src[i] - } -} - -// feCSwap replaces (f,g) with (g,f) if b == 1; replaces (f,g) with (f,g) if b == 0. -// -// Preconditions: b in {0,1}. -func feCSwap(f, g *fieldElement, b int32) { - b = -b - for i := range f { - t := b & (f[i] ^ g[i]) - f[i] ^= t - g[i] ^= t - } -} - -// load3 reads a 24-bit, little-endian value from in. -func load3(in []byte) int64 { - var r int64 - r = int64(in[0]) - r |= int64(in[1]) << 8 - r |= int64(in[2]) << 16 - return r -} - -// load4 reads a 32-bit, little-endian value from in. -func load4(in []byte) int64 { - return int64(binary.LittleEndian.Uint32(in)) -} - -func feFromBytes(dst *fieldElement, src *[32]byte) { - h0 := load4(src[:]) - h1 := load3(src[4:]) << 6 - h2 := load3(src[7:]) << 5 - h3 := load3(src[10:]) << 3 - h4 := load3(src[13:]) << 2 - h5 := load4(src[16:]) - h6 := load3(src[20:]) << 7 - h7 := load3(src[23:]) << 5 - h8 := load3(src[26:]) << 4 - h9 := (load3(src[29:]) & 0x7fffff) << 2 - - var carry [10]int64 - carry[9] = (h9 + 1<<24) >> 25 - h0 += carry[9] * 19 - h9 -= carry[9] << 25 - carry[1] = (h1 + 1<<24) >> 25 - h2 += carry[1] - h1 -= carry[1] << 25 - carry[3] = (h3 + 1<<24) >> 25 - h4 += carry[3] - h3 -= carry[3] << 25 - carry[5] = (h5 + 1<<24) >> 25 - h6 += carry[5] - h5 -= carry[5] << 25 - carry[7] = (h7 + 1<<24) >> 25 - h8 += carry[7] - h7 -= carry[7] << 25 - - carry[0] = (h0 + 1<<25) >> 26 - h1 += carry[0] - h0 -= carry[0] << 26 - carry[2] = (h2 + 1<<25) >> 26 - h3 += carry[2] - h2 -= carry[2] << 26 - carry[4] = (h4 + 1<<25) >> 26 - h5 += carry[4] - h4 -= carry[4] << 26 - carry[6] = (h6 + 1<<25) >> 26 - h7 += carry[6] - h6 -= carry[6] << 26 - carry[8] = (h8 + 1<<25) >> 26 - h9 += carry[8] - h8 -= carry[8] << 26 - - dst[0] = int32(h0) - dst[1] = int32(h1) - dst[2] = int32(h2) - dst[3] = int32(h3) - dst[4] = int32(h4) - dst[5] = int32(h5) - dst[6] = int32(h6) - dst[7] = int32(h7) - dst[8] = int32(h8) - dst[9] = int32(h9) -} - -// feToBytes marshals h to s. -// Preconditions: -// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. -// -// Write p=2^255-19; q=floor(h/p). -// Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))). -// -// Proof: -// Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4. -// Also have |h-2^230 h9|<2^230 so |19 2^(-255)(h-2^230 h9)|<1/4. -// -// Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9). -// Then 0> 25 - q = (h[0] + q) >> 26 - q = (h[1] + q) >> 25 - q = (h[2] + q) >> 26 - q = (h[3] + q) >> 25 - q = (h[4] + q) >> 26 - q = (h[5] + q) >> 25 - q = (h[6] + q) >> 26 - q = (h[7] + q) >> 25 - q = (h[8] + q) >> 26 - q = (h[9] + q) >> 25 - - // Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. - h[0] += 19 * q - // Goal: Output h-2^255 q, which is between 0 and 2^255-20. - - carry[0] = h[0] >> 26 - h[1] += carry[0] - h[0] -= carry[0] << 26 - carry[1] = h[1] >> 25 - h[2] += carry[1] - h[1] -= carry[1] << 25 - carry[2] = h[2] >> 26 - h[3] += carry[2] - h[2] -= carry[2] << 26 - carry[3] = h[3] >> 25 - h[4] += carry[3] - h[3] -= carry[3] << 25 - carry[4] = h[4] >> 26 - h[5] += carry[4] - h[4] -= carry[4] << 26 - carry[5] = h[5] >> 25 - h[6] += carry[5] - h[5] -= carry[5] << 25 - carry[6] = h[6] >> 26 - h[7] += carry[6] - h[6] -= carry[6] << 26 - carry[7] = h[7] >> 25 - h[8] += carry[7] - h[7] -= carry[7] << 25 - carry[8] = h[8] >> 26 - h[9] += carry[8] - h[8] -= carry[8] << 26 - carry[9] = h[9] >> 25 - h[9] -= carry[9] << 25 - // h10 = carry9 - - // Goal: Output h[0]+...+2^255 h10-2^255 q, which is between 0 and 2^255-20. - // Have h[0]+...+2^230 h[9] between 0 and 2^255-1; - // evidently 2^255 h10-2^255 q = 0. - // Goal: Output h[0]+...+2^230 h[9]. - - s[0] = byte(h[0] >> 0) - s[1] = byte(h[0] >> 8) - s[2] = byte(h[0] >> 16) - s[3] = byte((h[0] >> 24) | (h[1] << 2)) - s[4] = byte(h[1] >> 6) - s[5] = byte(h[1] >> 14) - s[6] = byte((h[1] >> 22) | (h[2] << 3)) - s[7] = byte(h[2] >> 5) - s[8] = byte(h[2] >> 13) - s[9] = byte((h[2] >> 21) | (h[3] << 5)) - s[10] = byte(h[3] >> 3) - s[11] = byte(h[3] >> 11) - s[12] = byte((h[3] >> 19) | (h[4] << 6)) - s[13] = byte(h[4] >> 2) - s[14] = byte(h[4] >> 10) - s[15] = byte(h[4] >> 18) - s[16] = byte(h[5] >> 0) - s[17] = byte(h[5] >> 8) - s[18] = byte(h[5] >> 16) - s[19] = byte((h[5] >> 24) | (h[6] << 1)) - s[20] = byte(h[6] >> 7) - s[21] = byte(h[6] >> 15) - s[22] = byte((h[6] >> 23) | (h[7] << 3)) - s[23] = byte(h[7] >> 5) - s[24] = byte(h[7] >> 13) - s[25] = byte((h[7] >> 21) | (h[8] << 4)) - s[26] = byte(h[8] >> 4) - s[27] = byte(h[8] >> 12) - s[28] = byte((h[8] >> 20) | (h[9] << 6)) - s[29] = byte(h[9] >> 2) - s[30] = byte(h[9] >> 10) - s[31] = byte(h[9] >> 18) -} - -// feMul calculates h = f * g -// Can overlap h with f or g. -// -// Preconditions: -// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. -// |g| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. -// -// Postconditions: -// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. -// -// Notes on implementation strategy: -// -// Using schoolbook multiplication. -// Karatsuba would save a little in some cost models. -// -// Most multiplications by 2 and 19 are 32-bit precomputations; -// cheaper than 64-bit postcomputations. -// -// There is one remaining multiplication by 19 in the carry chain; -// one *19 precomputation can be merged into this, -// but the resulting data flow is considerably less clean. -// -// There are 12 carries below. -// 10 of them are 2-way parallelizable and vectorizable. -// Can get away with 11 carries, but then data flow is much deeper. -// -// With tighter constraints on inputs can squeeze carries into int32. -func feMul(h, f, g *fieldElement) { - f0 := f[0] - f1 := f[1] - f2 := f[2] - f3 := f[3] - f4 := f[4] - f5 := f[5] - f6 := f[6] - f7 := f[7] - f8 := f[8] - f9 := f[9] - g0 := g[0] - g1 := g[1] - g2 := g[2] - g3 := g[3] - g4 := g[4] - g5 := g[5] - g6 := g[6] - g7 := g[7] - g8 := g[8] - g9 := g[9] - g1_19 := 19 * g1 // 1.4*2^29 - g2_19 := 19 * g2 // 1.4*2^30; still ok - g3_19 := 19 * g3 - g4_19 := 19 * g4 - g5_19 := 19 * g5 - g6_19 := 19 * g6 - g7_19 := 19 * g7 - g8_19 := 19 * g8 - g9_19 := 19 * g9 - f1_2 := 2 * f1 - f3_2 := 2 * f3 - f5_2 := 2 * f5 - f7_2 := 2 * f7 - f9_2 := 2 * f9 - f0g0 := int64(f0) * int64(g0) - f0g1 := int64(f0) * int64(g1) - f0g2 := int64(f0) * int64(g2) - f0g3 := int64(f0) * int64(g3) - f0g4 := int64(f0) * int64(g4) - f0g5 := int64(f0) * int64(g5) - f0g6 := int64(f0) * int64(g6) - f0g7 := int64(f0) * int64(g7) - f0g8 := int64(f0) * int64(g8) - f0g9 := int64(f0) * int64(g9) - f1g0 := int64(f1) * int64(g0) - f1g1_2 := int64(f1_2) * int64(g1) - f1g2 := int64(f1) * int64(g2) - f1g3_2 := int64(f1_2) * int64(g3) - f1g4 := int64(f1) * int64(g4) - f1g5_2 := int64(f1_2) * int64(g5) - f1g6 := int64(f1) * int64(g6) - f1g7_2 := int64(f1_2) * int64(g7) - f1g8 := int64(f1) * int64(g8) - f1g9_38 := int64(f1_2) * int64(g9_19) - f2g0 := int64(f2) * int64(g0) - f2g1 := int64(f2) * int64(g1) - f2g2 := int64(f2) * int64(g2) - f2g3 := int64(f2) * int64(g3) - f2g4 := int64(f2) * int64(g4) - f2g5 := int64(f2) * int64(g5) - f2g6 := int64(f2) * int64(g6) - f2g7 := int64(f2) * int64(g7) - f2g8_19 := int64(f2) * int64(g8_19) - f2g9_19 := int64(f2) * int64(g9_19) - f3g0 := int64(f3) * int64(g0) - f3g1_2 := int64(f3_2) * int64(g1) - f3g2 := int64(f3) * int64(g2) - f3g3_2 := int64(f3_2) * int64(g3) - f3g4 := int64(f3) * int64(g4) - f3g5_2 := int64(f3_2) * int64(g5) - f3g6 := int64(f3) * int64(g6) - f3g7_38 := int64(f3_2) * int64(g7_19) - f3g8_19 := int64(f3) * int64(g8_19) - f3g9_38 := int64(f3_2) * int64(g9_19) - f4g0 := int64(f4) * int64(g0) - f4g1 := int64(f4) * int64(g1) - f4g2 := int64(f4) * int64(g2) - f4g3 := int64(f4) * int64(g3) - f4g4 := int64(f4) * int64(g4) - f4g5 := int64(f4) * int64(g5) - f4g6_19 := int64(f4) * int64(g6_19) - f4g7_19 := int64(f4) * int64(g7_19) - f4g8_19 := int64(f4) * int64(g8_19) - f4g9_19 := int64(f4) * int64(g9_19) - f5g0 := int64(f5) * int64(g0) - f5g1_2 := int64(f5_2) * int64(g1) - f5g2 := int64(f5) * int64(g2) - f5g3_2 := int64(f5_2) * int64(g3) - f5g4 := int64(f5) * int64(g4) - f5g5_38 := int64(f5_2) * int64(g5_19) - f5g6_19 := int64(f5) * int64(g6_19) - f5g7_38 := int64(f5_2) * int64(g7_19) - f5g8_19 := int64(f5) * int64(g8_19) - f5g9_38 := int64(f5_2) * int64(g9_19) - f6g0 := int64(f6) * int64(g0) - f6g1 := int64(f6) * int64(g1) - f6g2 := int64(f6) * int64(g2) - f6g3 := int64(f6) * int64(g3) - f6g4_19 := int64(f6) * int64(g4_19) - f6g5_19 := int64(f6) * int64(g5_19) - f6g6_19 := int64(f6) * int64(g6_19) - f6g7_19 := int64(f6) * int64(g7_19) - f6g8_19 := int64(f6) * int64(g8_19) - f6g9_19 := int64(f6) * int64(g9_19) - f7g0 := int64(f7) * int64(g0) - f7g1_2 := int64(f7_2) * int64(g1) - f7g2 := int64(f7) * int64(g2) - f7g3_38 := int64(f7_2) * int64(g3_19) - f7g4_19 := int64(f7) * int64(g4_19) - f7g5_38 := int64(f7_2) * int64(g5_19) - f7g6_19 := int64(f7) * int64(g6_19) - f7g7_38 := int64(f7_2) * int64(g7_19) - f7g8_19 := int64(f7) * int64(g8_19) - f7g9_38 := int64(f7_2) * int64(g9_19) - f8g0 := int64(f8) * int64(g0) - f8g1 := int64(f8) * int64(g1) - f8g2_19 := int64(f8) * int64(g2_19) - f8g3_19 := int64(f8) * int64(g3_19) - f8g4_19 := int64(f8) * int64(g4_19) - f8g5_19 := int64(f8) * int64(g5_19) - f8g6_19 := int64(f8) * int64(g6_19) - f8g7_19 := int64(f8) * int64(g7_19) - f8g8_19 := int64(f8) * int64(g8_19) - f8g9_19 := int64(f8) * int64(g9_19) - f9g0 := int64(f9) * int64(g0) - f9g1_38 := int64(f9_2) * int64(g1_19) - f9g2_19 := int64(f9) * int64(g2_19) - f9g3_38 := int64(f9_2) * int64(g3_19) - f9g4_19 := int64(f9) * int64(g4_19) - f9g5_38 := int64(f9_2) * int64(g5_19) - f9g6_19 := int64(f9) * int64(g6_19) - f9g7_38 := int64(f9_2) * int64(g7_19) - f9g8_19 := int64(f9) * int64(g8_19) - f9g9_38 := int64(f9_2) * int64(g9_19) - h0 := f0g0 + f1g9_38 + f2g8_19 + f3g7_38 + f4g6_19 + f5g5_38 + f6g4_19 + f7g3_38 + f8g2_19 + f9g1_38 - h1 := f0g1 + f1g0 + f2g9_19 + f3g8_19 + f4g7_19 + f5g6_19 + f6g5_19 + f7g4_19 + f8g3_19 + f9g2_19 - h2 := f0g2 + f1g1_2 + f2g0 + f3g9_38 + f4g8_19 + f5g7_38 + f6g6_19 + f7g5_38 + f8g4_19 + f9g3_38 - h3 := f0g3 + f1g2 + f2g1 + f3g0 + f4g9_19 + f5g8_19 + f6g7_19 + f7g6_19 + f8g5_19 + f9g4_19 - h4 := f0g4 + f1g3_2 + f2g2 + f3g1_2 + f4g0 + f5g9_38 + f6g8_19 + f7g7_38 + f8g6_19 + f9g5_38 - h5 := f0g5 + f1g4 + f2g3 + f3g2 + f4g1 + f5g0 + f6g9_19 + f7g8_19 + f8g7_19 + f9g6_19 - h6 := f0g6 + f1g5_2 + f2g4 + f3g3_2 + f4g2 + f5g1_2 + f6g0 + f7g9_38 + f8g8_19 + f9g7_38 - h7 := f0g7 + f1g6 + f2g5 + f3g4 + f4g3 + f5g2 + f6g1 + f7g0 + f8g9_19 + f9g8_19 - h8 := f0g8 + f1g7_2 + f2g6 + f3g5_2 + f4g4 + f5g3_2 + f6g2 + f7g1_2 + f8g0 + f9g9_38 - h9 := f0g9 + f1g8 + f2g7 + f3g6 + f4g5 + f5g4 + f6g3 + f7g2 + f8g1 + f9g0 - var carry [10]int64 - - // |h0| <= (1.1*1.1*2^52*(1+19+19+19+19)+1.1*1.1*2^50*(38+38+38+38+38)) - // i.e. |h0| <= 1.2*2^59; narrower ranges for h2, h4, h6, h8 - // |h1| <= (1.1*1.1*2^51*(1+1+19+19+19+19+19+19+19+19)) - // i.e. |h1| <= 1.5*2^58; narrower ranges for h3, h5, h7, h9 - - carry[0] = (h0 + (1 << 25)) >> 26 - h1 += carry[0] - h0 -= carry[0] << 26 - carry[4] = (h4 + (1 << 25)) >> 26 - h5 += carry[4] - h4 -= carry[4] << 26 - // |h0| <= 2^25 - // |h4| <= 2^25 - // |h1| <= 1.51*2^58 - // |h5| <= 1.51*2^58 - - carry[1] = (h1 + (1 << 24)) >> 25 - h2 += carry[1] - h1 -= carry[1] << 25 - carry[5] = (h5 + (1 << 24)) >> 25 - h6 += carry[5] - h5 -= carry[5] << 25 - // |h1| <= 2^24; from now on fits into int32 - // |h5| <= 2^24; from now on fits into int32 - // |h2| <= 1.21*2^59 - // |h6| <= 1.21*2^59 - - carry[2] = (h2 + (1 << 25)) >> 26 - h3 += carry[2] - h2 -= carry[2] << 26 - carry[6] = (h6 + (1 << 25)) >> 26 - h7 += carry[6] - h6 -= carry[6] << 26 - // |h2| <= 2^25; from now on fits into int32 unchanged - // |h6| <= 2^25; from now on fits into int32 unchanged - // |h3| <= 1.51*2^58 - // |h7| <= 1.51*2^58 - - carry[3] = (h3 + (1 << 24)) >> 25 - h4 += carry[3] - h3 -= carry[3] << 25 - carry[7] = (h7 + (1 << 24)) >> 25 - h8 += carry[7] - h7 -= carry[7] << 25 - // |h3| <= 2^24; from now on fits into int32 unchanged - // |h7| <= 2^24; from now on fits into int32 unchanged - // |h4| <= 1.52*2^33 - // |h8| <= 1.52*2^33 - - carry[4] = (h4 + (1 << 25)) >> 26 - h5 += carry[4] - h4 -= carry[4] << 26 - carry[8] = (h8 + (1 << 25)) >> 26 - h9 += carry[8] - h8 -= carry[8] << 26 - // |h4| <= 2^25; from now on fits into int32 unchanged - // |h8| <= 2^25; from now on fits into int32 unchanged - // |h5| <= 1.01*2^24 - // |h9| <= 1.51*2^58 - - carry[9] = (h9 + (1 << 24)) >> 25 - h0 += carry[9] * 19 - h9 -= carry[9] << 25 - // |h9| <= 2^24; from now on fits into int32 unchanged - // |h0| <= 1.8*2^37 - - carry[0] = (h0 + (1 << 25)) >> 26 - h1 += carry[0] - h0 -= carry[0] << 26 - // |h0| <= 2^25; from now on fits into int32 unchanged - // |h1| <= 1.01*2^24 - - h[0] = int32(h0) - h[1] = int32(h1) - h[2] = int32(h2) - h[3] = int32(h3) - h[4] = int32(h4) - h[5] = int32(h5) - h[6] = int32(h6) - h[7] = int32(h7) - h[8] = int32(h8) - h[9] = int32(h9) -} - -// feSquare calculates h = f*f. Can overlap h with f. -// -// Preconditions: -// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. -// -// Postconditions: -// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. -func feSquare(h, f *fieldElement) { - f0 := f[0] - f1 := f[1] - f2 := f[2] - f3 := f[3] - f4 := f[4] - f5 := f[5] - f6 := f[6] - f7 := f[7] - f8 := f[8] - f9 := f[9] - f0_2 := 2 * f0 - f1_2 := 2 * f1 - f2_2 := 2 * f2 - f3_2 := 2 * f3 - f4_2 := 2 * f4 - f5_2 := 2 * f5 - f6_2 := 2 * f6 - f7_2 := 2 * f7 - f5_38 := 38 * f5 // 1.31*2^30 - f6_19 := 19 * f6 // 1.31*2^30 - f7_38 := 38 * f7 // 1.31*2^30 - f8_19 := 19 * f8 // 1.31*2^30 - f9_38 := 38 * f9 // 1.31*2^30 - f0f0 := int64(f0) * int64(f0) - f0f1_2 := int64(f0_2) * int64(f1) - f0f2_2 := int64(f0_2) * int64(f2) - f0f3_2 := int64(f0_2) * int64(f3) - f0f4_2 := int64(f0_2) * int64(f4) - f0f5_2 := int64(f0_2) * int64(f5) - f0f6_2 := int64(f0_2) * int64(f6) - f0f7_2 := int64(f0_2) * int64(f7) - f0f8_2 := int64(f0_2) * int64(f8) - f0f9_2 := int64(f0_2) * int64(f9) - f1f1_2 := int64(f1_2) * int64(f1) - f1f2_2 := int64(f1_2) * int64(f2) - f1f3_4 := int64(f1_2) * int64(f3_2) - f1f4_2 := int64(f1_2) * int64(f4) - f1f5_4 := int64(f1_2) * int64(f5_2) - f1f6_2 := int64(f1_2) * int64(f6) - f1f7_4 := int64(f1_2) * int64(f7_2) - f1f8_2 := int64(f1_2) * int64(f8) - f1f9_76 := int64(f1_2) * int64(f9_38) - f2f2 := int64(f2) * int64(f2) - f2f3_2 := int64(f2_2) * int64(f3) - f2f4_2 := int64(f2_2) * int64(f4) - f2f5_2 := int64(f2_2) * int64(f5) - f2f6_2 := int64(f2_2) * int64(f6) - f2f7_2 := int64(f2_2) * int64(f7) - f2f8_38 := int64(f2_2) * int64(f8_19) - f2f9_38 := int64(f2) * int64(f9_38) - f3f3_2 := int64(f3_2) * int64(f3) - f3f4_2 := int64(f3_2) * int64(f4) - f3f5_4 := int64(f3_2) * int64(f5_2) - f3f6_2 := int64(f3_2) * int64(f6) - f3f7_76 := int64(f3_2) * int64(f7_38) - f3f8_38 := int64(f3_2) * int64(f8_19) - f3f9_76 := int64(f3_2) * int64(f9_38) - f4f4 := int64(f4) * int64(f4) - f4f5_2 := int64(f4_2) * int64(f5) - f4f6_38 := int64(f4_2) * int64(f6_19) - f4f7_38 := int64(f4) * int64(f7_38) - f4f8_38 := int64(f4_2) * int64(f8_19) - f4f9_38 := int64(f4) * int64(f9_38) - f5f5_38 := int64(f5) * int64(f5_38) - f5f6_38 := int64(f5_2) * int64(f6_19) - f5f7_76 := int64(f5_2) * int64(f7_38) - f5f8_38 := int64(f5_2) * int64(f8_19) - f5f9_76 := int64(f5_2) * int64(f9_38) - f6f6_19 := int64(f6) * int64(f6_19) - f6f7_38 := int64(f6) * int64(f7_38) - f6f8_38 := int64(f6_2) * int64(f8_19) - f6f9_38 := int64(f6) * int64(f9_38) - f7f7_38 := int64(f7) * int64(f7_38) - f7f8_38 := int64(f7_2) * int64(f8_19) - f7f9_76 := int64(f7_2) * int64(f9_38) - f8f8_19 := int64(f8) * int64(f8_19) - f8f9_38 := int64(f8) * int64(f9_38) - f9f9_38 := int64(f9) * int64(f9_38) - h0 := f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38 - h1 := f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38 - h2 := f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19 - h3 := f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38 - h4 := f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38 - h5 := f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38 - h6 := f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19 - h7 := f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38 - h8 := f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38 - h9 := f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2 - var carry [10]int64 - - carry[0] = (h0 + (1 << 25)) >> 26 - h1 += carry[0] - h0 -= carry[0] << 26 - carry[4] = (h4 + (1 << 25)) >> 26 - h5 += carry[4] - h4 -= carry[4] << 26 - - carry[1] = (h1 + (1 << 24)) >> 25 - h2 += carry[1] - h1 -= carry[1] << 25 - carry[5] = (h5 + (1 << 24)) >> 25 - h6 += carry[5] - h5 -= carry[5] << 25 - - carry[2] = (h2 + (1 << 25)) >> 26 - h3 += carry[2] - h2 -= carry[2] << 26 - carry[6] = (h6 + (1 << 25)) >> 26 - h7 += carry[6] - h6 -= carry[6] << 26 - - carry[3] = (h3 + (1 << 24)) >> 25 - h4 += carry[3] - h3 -= carry[3] << 25 - carry[7] = (h7 + (1 << 24)) >> 25 - h8 += carry[7] - h7 -= carry[7] << 25 - - carry[4] = (h4 + (1 << 25)) >> 26 - h5 += carry[4] - h4 -= carry[4] << 26 - carry[8] = (h8 + (1 << 25)) >> 26 - h9 += carry[8] - h8 -= carry[8] << 26 - - carry[9] = (h9 + (1 << 24)) >> 25 - h0 += carry[9] * 19 - h9 -= carry[9] << 25 - - carry[0] = (h0 + (1 << 25)) >> 26 - h1 += carry[0] - h0 -= carry[0] << 26 - - h[0] = int32(h0) - h[1] = int32(h1) - h[2] = int32(h2) - h[3] = int32(h3) - h[4] = int32(h4) - h[5] = int32(h5) - h[6] = int32(h6) - h[7] = int32(h7) - h[8] = int32(h8) - h[9] = int32(h9) -} - -// feMul121666 calculates h = f * 121666. Can overlap h with f. -// -// Preconditions: -// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. -// -// Postconditions: -// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. -func feMul121666(h, f *fieldElement) { - h0 := int64(f[0]) * 121666 - h1 := int64(f[1]) * 121666 - h2 := int64(f[2]) * 121666 - h3 := int64(f[3]) * 121666 - h4 := int64(f[4]) * 121666 - h5 := int64(f[5]) * 121666 - h6 := int64(f[6]) * 121666 - h7 := int64(f[7]) * 121666 - h8 := int64(f[8]) * 121666 - h9 := int64(f[9]) * 121666 - var carry [10]int64 - - carry[9] = (h9 + (1 << 24)) >> 25 - h0 += carry[9] * 19 - h9 -= carry[9] << 25 - carry[1] = (h1 + (1 << 24)) >> 25 - h2 += carry[1] - h1 -= carry[1] << 25 - carry[3] = (h3 + (1 << 24)) >> 25 - h4 += carry[3] - h3 -= carry[3] << 25 - carry[5] = (h5 + (1 << 24)) >> 25 - h6 += carry[5] - h5 -= carry[5] << 25 - carry[7] = (h7 + (1 << 24)) >> 25 - h8 += carry[7] - h7 -= carry[7] << 25 - - carry[0] = (h0 + (1 << 25)) >> 26 - h1 += carry[0] - h0 -= carry[0] << 26 - carry[2] = (h2 + (1 << 25)) >> 26 - h3 += carry[2] - h2 -= carry[2] << 26 - carry[4] = (h4 + (1 << 25)) >> 26 - h5 += carry[4] - h4 -= carry[4] << 26 - carry[6] = (h6 + (1 << 25)) >> 26 - h7 += carry[6] - h6 -= carry[6] << 26 - carry[8] = (h8 + (1 << 25)) >> 26 - h9 += carry[8] - h8 -= carry[8] << 26 - - h[0] = int32(h0) - h[1] = int32(h1) - h[2] = int32(h2) - h[3] = int32(h3) - h[4] = int32(h4) - h[5] = int32(h5) - h[6] = int32(h6) - h[7] = int32(h7) - h[8] = int32(h8) - h[9] = int32(h9) -} - -// feInvert sets out = z^-1. -func feInvert(out, z *fieldElement) { - var t0, t1, t2, t3 fieldElement - var i int - - feSquare(&t0, z) - for i = 1; i < 1; i++ { - feSquare(&t0, &t0) - } - feSquare(&t1, &t0) - for i = 1; i < 2; i++ { - feSquare(&t1, &t1) - } - feMul(&t1, z, &t1) - feMul(&t0, &t0, &t1) - feSquare(&t2, &t0) - for i = 1; i < 1; i++ { - feSquare(&t2, &t2) - } - feMul(&t1, &t1, &t2) - feSquare(&t2, &t1) - for i = 1; i < 5; i++ { - feSquare(&t2, &t2) - } - feMul(&t1, &t2, &t1) - feSquare(&t2, &t1) - for i = 1; i < 10; i++ { - feSquare(&t2, &t2) - } - feMul(&t2, &t2, &t1) - feSquare(&t3, &t2) - for i = 1; i < 20; i++ { - feSquare(&t3, &t3) - } - feMul(&t2, &t3, &t2) - feSquare(&t2, &t2) - for i = 1; i < 10; i++ { - feSquare(&t2, &t2) - } - feMul(&t1, &t2, &t1) - feSquare(&t2, &t1) - for i = 1; i < 50; i++ { - feSquare(&t2, &t2) - } - feMul(&t2, &t2, &t1) - feSquare(&t3, &t2) - for i = 1; i < 100; i++ { - feSquare(&t3, &t3) - } - feMul(&t2, &t3, &t2) - feSquare(&t2, &t2) - for i = 1; i < 50; i++ { - feSquare(&t2, &t2) - } - feMul(&t1, &t2, &t1) - feSquare(&t1, &t1) - for i = 1; i < 5; i++ { - feSquare(&t1, &t1) - } - feMul(out, &t1, &t0) -} - -func scalarMultGeneric(out, in, base *[32]byte) { - var e [32]byte - - copy(e[:], in[:]) - e[0] &= 248 - e[31] &= 127 - e[31] |= 64 - - var x1, x2, z2, x3, z3, tmp0, tmp1 fieldElement - feFromBytes(&x1, base) - feOne(&x2) - feCopy(&x3, &x1) - feOne(&z3) - - swap := int32(0) - for pos := 254; pos >= 0; pos-- { - b := e[pos/8] >> uint(pos&7) - b &= 1 - swap ^= int32(b) - feCSwap(&x2, &x3, swap) - feCSwap(&z2, &z3, swap) - swap = int32(b) - - feSub(&tmp0, &x3, &z3) - feSub(&tmp1, &x2, &z2) - feAdd(&x2, &x2, &z2) - feAdd(&z2, &x3, &z3) - feMul(&z3, &tmp0, &x2) - feMul(&z2, &z2, &tmp1) - feSquare(&tmp0, &tmp1) - feSquare(&tmp1, &x2) - feAdd(&x3, &z3, &z2) - feSub(&z2, &z3, &z2) - feMul(&x2, &tmp1, &tmp0) - feSub(&tmp1, &tmp1, &tmp0) - feSquare(&z2, &z2) - feMul121666(&z3, &tmp1) - feSquare(&x3, &x3) - feAdd(&tmp0, &tmp0, &z3) - feMul(&z3, &x1, &z2) - feMul(&z2, &tmp1, &tmp0) - } - - feCSwap(&x2, &x3, swap) - feCSwap(&z2, &z3, swap) - - feInvert(&z2, &z2) - feMul(&x2, &x2, &z2) - feToBytes(out, &x2) -} diff --git a/vendor/golang.org/x/crypto/curve25519/curve25519_noasm.go b/vendor/golang.org/x/crypto/curve25519/curve25519_noasm.go deleted file mode 100644 index 259728af7dad7..0000000000000 --- a/vendor/golang.org/x/crypto/curve25519/curve25519_noasm.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2019 The 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. - -//go:build !amd64 || !gc || purego -// +build !amd64 !gc purego - -package curve25519 - -func scalarMult(out, in, base *[32]byte) { - scalarMultGeneric(out, in, base) -} diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/README b/vendor/golang.org/x/crypto/curve25519/internal/field/README new file mode 100644 index 0000000000000..e25bca7dc806b --- /dev/null +++ b/vendor/golang.org/x/crypto/curve25519/internal/field/README @@ -0,0 +1,7 @@ +This package is kept in sync with crypto/ed25519/internal/edwards25519/field in +the standard library. + +If there are any changes in the standard library that need to be synced to this +package, run sync.sh. It will not overwrite any local changes made since the +previous sync, so it's ok to land changes in this package first, and then sync +to the standard library later. diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe.go b/vendor/golang.org/x/crypto/curve25519/internal/field/fe.go new file mode 100644 index 0000000000000..ca841ad99e3ab --- /dev/null +++ b/vendor/golang.org/x/crypto/curve25519/internal/field/fe.go @@ -0,0 +1,416 @@ +// Copyright (c) 2017 The 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 field implements fast arithmetic modulo 2^255-19. +package field + +import ( + "crypto/subtle" + "encoding/binary" + "math/bits" +) + +// Element represents an element of the field GF(2^255-19). Note that this +// is not a cryptographically secure group, and should only be used to interact +// with edwards25519.Point coordinates. +// +// This type works similarly to math/big.Int, and all arguments and receivers +// are allowed to alias. +// +// The zero value is a valid zero element. +type Element struct { + // An element t represents the integer + // t.l0 + t.l1*2^51 + t.l2*2^102 + t.l3*2^153 + t.l4*2^204 + // + // Between operations, all limbs are expected to be lower than 2^52. + l0 uint64 + l1 uint64 + l2 uint64 + l3 uint64 + l4 uint64 +} + +const maskLow51Bits uint64 = (1 << 51) - 1 + +var feZero = &Element{0, 0, 0, 0, 0} + +// Zero sets v = 0, and returns v. +func (v *Element) Zero() *Element { + *v = *feZero + return v +} + +var feOne = &Element{1, 0, 0, 0, 0} + +// One sets v = 1, and returns v. +func (v *Element) One() *Element { + *v = *feOne + return v +} + +// reduce reduces v modulo 2^255 - 19 and returns it. +func (v *Element) reduce() *Element { + v.carryPropagate() + + // After the light reduction we now have a field element representation + // v < 2^255 + 2^13 * 19, but need v < 2^255 - 19. + + // If v >= 2^255 - 19, then v + 19 >= 2^255, which would overflow 2^255 - 1, + // generating a carry. That is, c will be 0 if v < 2^255 - 19, and 1 otherwise. + c := (v.l0 + 19) >> 51 + c = (v.l1 + c) >> 51 + c = (v.l2 + c) >> 51 + c = (v.l3 + c) >> 51 + c = (v.l4 + c) >> 51 + + // If v < 2^255 - 19 and c = 0, this will be a no-op. Otherwise, it's + // effectively applying the reduction identity to the carry. + v.l0 += 19 * c + + v.l1 += v.l0 >> 51 + v.l0 = v.l0 & maskLow51Bits + v.l2 += v.l1 >> 51 + v.l1 = v.l1 & maskLow51Bits + v.l3 += v.l2 >> 51 + v.l2 = v.l2 & maskLow51Bits + v.l4 += v.l3 >> 51 + v.l3 = v.l3 & maskLow51Bits + // no additional carry + v.l4 = v.l4 & maskLow51Bits + + return v +} + +// Add sets v = a + b, and returns v. +func (v *Element) Add(a, b *Element) *Element { + v.l0 = a.l0 + b.l0 + v.l1 = a.l1 + b.l1 + v.l2 = a.l2 + b.l2 + v.l3 = a.l3 + b.l3 + v.l4 = a.l4 + b.l4 + // Using the generic implementation here is actually faster than the + // assembly. Probably because the body of this function is so simple that + // the compiler can figure out better optimizations by inlining the carry + // propagation. TODO + return v.carryPropagateGeneric() +} + +// Subtract sets v = a - b, and returns v. +func (v *Element) Subtract(a, b *Element) *Element { + // We first add 2 * p, to guarantee the subtraction won't underflow, and + // then subtract b (which can be up to 2^255 + 2^13 * 19). + v.l0 = (a.l0 + 0xFFFFFFFFFFFDA) - b.l0 + v.l1 = (a.l1 + 0xFFFFFFFFFFFFE) - b.l1 + v.l2 = (a.l2 + 0xFFFFFFFFFFFFE) - b.l2 + v.l3 = (a.l3 + 0xFFFFFFFFFFFFE) - b.l3 + v.l4 = (a.l4 + 0xFFFFFFFFFFFFE) - b.l4 + return v.carryPropagate() +} + +// Negate sets v = -a, and returns v. +func (v *Element) Negate(a *Element) *Element { + return v.Subtract(feZero, a) +} + +// Invert sets v = 1/z mod p, and returns v. +// +// If z == 0, Invert returns v = 0. +func (v *Element) Invert(z *Element) *Element { + // Inversion is implemented as exponentiation with exponent p − 2. It uses the + // same sequence of 255 squarings and 11 multiplications as [Curve25519]. + var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t Element + + z2.Square(z) // 2 + t.Square(&z2) // 4 + t.Square(&t) // 8 + z9.Multiply(&t, z) // 9 + z11.Multiply(&z9, &z2) // 11 + t.Square(&z11) // 22 + z2_5_0.Multiply(&t, &z9) // 31 = 2^5 - 2^0 + + t.Square(&z2_5_0) // 2^6 - 2^1 + for i := 0; i < 4; i++ { + t.Square(&t) // 2^10 - 2^5 + } + z2_10_0.Multiply(&t, &z2_5_0) // 2^10 - 2^0 + + t.Square(&z2_10_0) // 2^11 - 2^1 + for i := 0; i < 9; i++ { + t.Square(&t) // 2^20 - 2^10 + } + z2_20_0.Multiply(&t, &z2_10_0) // 2^20 - 2^0 + + t.Square(&z2_20_0) // 2^21 - 2^1 + for i := 0; i < 19; i++ { + t.Square(&t) // 2^40 - 2^20 + } + t.Multiply(&t, &z2_20_0) // 2^40 - 2^0 + + t.Square(&t) // 2^41 - 2^1 + for i := 0; i < 9; i++ { + t.Square(&t) // 2^50 - 2^10 + } + z2_50_0.Multiply(&t, &z2_10_0) // 2^50 - 2^0 + + t.Square(&z2_50_0) // 2^51 - 2^1 + for i := 0; i < 49; i++ { + t.Square(&t) // 2^100 - 2^50 + } + z2_100_0.Multiply(&t, &z2_50_0) // 2^100 - 2^0 + + t.Square(&z2_100_0) // 2^101 - 2^1 + for i := 0; i < 99; i++ { + t.Square(&t) // 2^200 - 2^100 + } + t.Multiply(&t, &z2_100_0) // 2^200 - 2^0 + + t.Square(&t) // 2^201 - 2^1 + for i := 0; i < 49; i++ { + t.Square(&t) // 2^250 - 2^50 + } + t.Multiply(&t, &z2_50_0) // 2^250 - 2^0 + + t.Square(&t) // 2^251 - 2^1 + t.Square(&t) // 2^252 - 2^2 + t.Square(&t) // 2^253 - 2^3 + t.Square(&t) // 2^254 - 2^4 + t.Square(&t) // 2^255 - 2^5 + + return v.Multiply(&t, &z11) // 2^255 - 21 +} + +// Set sets v = a, and returns v. +func (v *Element) Set(a *Element) *Element { + *v = *a + return v +} + +// SetBytes sets v to x, which must be a 32-byte little-endian encoding. +// +// Consistent with RFC 7748, the most significant bit (the high bit of the +// last byte) is ignored, and non-canonical values (2^255-19 through 2^255-1) +// are accepted. Note that this is laxer than specified by RFC 8032. +func (v *Element) SetBytes(x []byte) *Element { + if len(x) != 32 { + panic("edwards25519: invalid field element input size") + } + + // Bits 0:51 (bytes 0:8, bits 0:64, shift 0, mask 51). + v.l0 = binary.LittleEndian.Uint64(x[0:8]) + v.l0 &= maskLow51Bits + // Bits 51:102 (bytes 6:14, bits 48:112, shift 3, mask 51). + v.l1 = binary.LittleEndian.Uint64(x[6:14]) >> 3 + v.l1 &= maskLow51Bits + // Bits 102:153 (bytes 12:20, bits 96:160, shift 6, mask 51). + v.l2 = binary.LittleEndian.Uint64(x[12:20]) >> 6 + v.l2 &= maskLow51Bits + // Bits 153:204 (bytes 19:27, bits 152:216, shift 1, mask 51). + v.l3 = binary.LittleEndian.Uint64(x[19:27]) >> 1 + v.l3 &= maskLow51Bits + // Bits 204:251 (bytes 24:32, bits 192:256, shift 12, mask 51). + // Note: not bytes 25:33, shift 4, to avoid overread. + v.l4 = binary.LittleEndian.Uint64(x[24:32]) >> 12 + v.l4 &= maskLow51Bits + + return v +} + +// Bytes returns the canonical 32-byte little-endian encoding of v. +func (v *Element) Bytes() []byte { + // This function is outlined to make the allocations inline in the caller + // rather than happen on the heap. + var out [32]byte + return v.bytes(&out) +} + +func (v *Element) bytes(out *[32]byte) []byte { + t := *v + t.reduce() + + var buf [8]byte + for i, l := range [5]uint64{t.l0, t.l1, t.l2, t.l3, t.l4} { + bitsOffset := i * 51 + binary.LittleEndian.PutUint64(buf[:], l<= len(out) { + break + } + out[off] |= bb + } + } + + return out[:] +} + +// Equal returns 1 if v and u are equal, and 0 otherwise. +func (v *Element) Equal(u *Element) int { + sa, sv := u.Bytes(), v.Bytes() + return subtle.ConstantTimeCompare(sa, sv) +} + +// mask64Bits returns 0xffffffff if cond is 1, and 0 otherwise. +func mask64Bits(cond int) uint64 { return ^(uint64(cond) - 1) } + +// Select sets v to a if cond == 1, and to b if cond == 0. +func (v *Element) Select(a, b *Element, cond int) *Element { + m := mask64Bits(cond) + v.l0 = (m & a.l0) | (^m & b.l0) + v.l1 = (m & a.l1) | (^m & b.l1) + v.l2 = (m & a.l2) | (^m & b.l2) + v.l3 = (m & a.l3) | (^m & b.l3) + v.l4 = (m & a.l4) | (^m & b.l4) + return v +} + +// Swap swaps v and u if cond == 1 or leaves them unchanged if cond == 0, and returns v. +func (v *Element) Swap(u *Element, cond int) { + m := mask64Bits(cond) + t := m & (v.l0 ^ u.l0) + v.l0 ^= t + u.l0 ^= t + t = m & (v.l1 ^ u.l1) + v.l1 ^= t + u.l1 ^= t + t = m & (v.l2 ^ u.l2) + v.l2 ^= t + u.l2 ^= t + t = m & (v.l3 ^ u.l3) + v.l3 ^= t + u.l3 ^= t + t = m & (v.l4 ^ u.l4) + v.l4 ^= t + u.l4 ^= t +} + +// IsNegative returns 1 if v is negative, and 0 otherwise. +func (v *Element) IsNegative() int { + return int(v.Bytes()[0] & 1) +} + +// Absolute sets v to |u|, and returns v. +func (v *Element) Absolute(u *Element) *Element { + return v.Select(new(Element).Negate(u), u, u.IsNegative()) +} + +// Multiply sets v = x * y, and returns v. +func (v *Element) Multiply(x, y *Element) *Element { + feMul(v, x, y) + return v +} + +// Square sets v = x * x, and returns v. +func (v *Element) Square(x *Element) *Element { + feSquare(v, x) + return v +} + +// Mult32 sets v = x * y, and returns v. +func (v *Element) Mult32(x *Element, y uint32) *Element { + x0lo, x0hi := mul51(x.l0, y) + x1lo, x1hi := mul51(x.l1, y) + x2lo, x2hi := mul51(x.l2, y) + x3lo, x3hi := mul51(x.l3, y) + x4lo, x4hi := mul51(x.l4, y) + v.l0 = x0lo + 19*x4hi // carried over per the reduction identity + v.l1 = x1lo + x0hi + v.l2 = x2lo + x1hi + v.l3 = x3lo + x2hi + v.l4 = x4lo + x3hi + // The hi portions are going to be only 32 bits, plus any previous excess, + // so we can skip the carry propagation. + return v +} + +// mul51 returns lo + hi * 2⁵¹ = a * b. +func mul51(a uint64, b uint32) (lo uint64, hi uint64) { + mh, ml := bits.Mul64(a, uint64(b)) + lo = ml & maskLow51Bits + hi = (mh << 13) | (ml >> 51) + return +} + +// Pow22523 set v = x^((p-5)/8), and returns v. (p-5)/8 is 2^252-3. +func (v *Element) Pow22523(x *Element) *Element { + var t0, t1, t2 Element + + t0.Square(x) // x^2 + t1.Square(&t0) // x^4 + t1.Square(&t1) // x^8 + t1.Multiply(x, &t1) // x^9 + t0.Multiply(&t0, &t1) // x^11 + t0.Square(&t0) // x^22 + t0.Multiply(&t1, &t0) // x^31 + t1.Square(&t0) // x^62 + for i := 1; i < 5; i++ { // x^992 + t1.Square(&t1) + } + t0.Multiply(&t1, &t0) // x^1023 -> 1023 = 2^10 - 1 + t1.Square(&t0) // 2^11 - 2 + for i := 1; i < 10; i++ { // 2^20 - 2^10 + t1.Square(&t1) + } + t1.Multiply(&t1, &t0) // 2^20 - 1 + t2.Square(&t1) // 2^21 - 2 + for i := 1; i < 20; i++ { // 2^40 - 2^20 + t2.Square(&t2) + } + t1.Multiply(&t2, &t1) // 2^40 - 1 + t1.Square(&t1) // 2^41 - 2 + for i := 1; i < 10; i++ { // 2^50 - 2^10 + t1.Square(&t1) + } + t0.Multiply(&t1, &t0) // 2^50 - 1 + t1.Square(&t0) // 2^51 - 2 + for i := 1; i < 50; i++ { // 2^100 - 2^50 + t1.Square(&t1) + } + t1.Multiply(&t1, &t0) // 2^100 - 1 + t2.Square(&t1) // 2^101 - 2 + for i := 1; i < 100; i++ { // 2^200 - 2^100 + t2.Square(&t2) + } + t1.Multiply(&t2, &t1) // 2^200 - 1 + t1.Square(&t1) // 2^201 - 2 + for i := 1; i < 50; i++ { // 2^250 - 2^50 + t1.Square(&t1) + } + t0.Multiply(&t1, &t0) // 2^250 - 1 + t0.Square(&t0) // 2^251 - 2 + t0.Square(&t0) // 2^252 - 4 + return v.Multiply(&t0, x) // 2^252 - 3 -> x^(2^252-3) +} + +// sqrtM1 is 2^((p-1)/4), which squared is equal to -1 by Euler's Criterion. +var sqrtM1 = &Element{1718705420411056, 234908883556509, + 2233514472574048, 2117202627021982, 765476049583133} + +// SqrtRatio sets r to the non-negative square root of the ratio of u and v. +// +// If u/v is square, SqrtRatio returns r and 1. If u/v is not square, SqrtRatio +// sets r according to Section 4.3 of draft-irtf-cfrg-ristretto255-decaf448-00, +// and returns r and 0. +func (r *Element) SqrtRatio(u, v *Element) (rr *Element, wasSquare int) { + var a, b Element + + // r = (u * v3) * (u * v7)^((p-5)/8) + v2 := a.Square(v) + uv3 := b.Multiply(u, b.Multiply(v2, v)) + uv7 := a.Multiply(uv3, a.Square(v2)) + r.Multiply(uv3, r.Pow22523(uv7)) + + check := a.Multiply(v, a.Square(r)) // check = v * r^2 + + uNeg := b.Negate(u) + correctSignSqrt := check.Equal(u) + flippedSignSqrt := check.Equal(uNeg) + flippedSignSqrtI := check.Equal(uNeg.Multiply(uNeg, sqrtM1)) + + rPrime := b.Multiply(r, sqrtM1) // r_prime = SQRT_M1 * r + // r = CT_SELECT(r_prime IF flipped_sign_sqrt | flipped_sign_sqrt_i ELSE r) + r.Select(rPrime, r, flippedSignSqrt|flippedSignSqrtI) + + r.Absolute(r) // Choose the nonnegative square root. + return r, correctSignSqrt | flippedSignSqrt +} diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.go b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.go new file mode 100644 index 0000000000000..44dc8e8caf916 --- /dev/null +++ b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.go @@ -0,0 +1,13 @@ +// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT. + +// +build amd64,gc,!purego + +package field + +// feMul sets out = a * b. It works like feMulGeneric. +//go:noescape +func feMul(out *Element, a *Element, b *Element) + +// feSquare sets out = a * a. It works like feSquareGeneric. +//go:noescape +func feSquare(out *Element, a *Element) diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.s b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.s new file mode 100644 index 0000000000000..293f013c94a66 --- /dev/null +++ b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.s @@ -0,0 +1,379 @@ +// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT. + +//go:build amd64 && gc && !purego +// +build amd64,gc,!purego + +#include "textflag.h" + +// func feMul(out *Element, a *Element, b *Element) +TEXT ·feMul(SB), NOSPLIT, $0-24 + MOVQ a+8(FP), CX + MOVQ b+16(FP), BX + + // r0 = a0×b0 + MOVQ (CX), AX + MULQ (BX) + MOVQ AX, DI + MOVQ DX, SI + + // r0 += 19×a1×b4 + MOVQ 8(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 32(BX) + ADDQ AX, DI + ADCQ DX, SI + + // r0 += 19×a2×b3 + MOVQ 16(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 24(BX) + ADDQ AX, DI + ADCQ DX, SI + + // r0 += 19×a3×b2 + MOVQ 24(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 16(BX) + ADDQ AX, DI + ADCQ DX, SI + + // r0 += 19×a4×b1 + MOVQ 32(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 8(BX) + ADDQ AX, DI + ADCQ DX, SI + + // r1 = a0×b1 + MOVQ (CX), AX + MULQ 8(BX) + MOVQ AX, R9 + MOVQ DX, R8 + + // r1 += a1×b0 + MOVQ 8(CX), AX + MULQ (BX) + ADDQ AX, R9 + ADCQ DX, R8 + + // r1 += 19×a2×b4 + MOVQ 16(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 32(BX) + ADDQ AX, R9 + ADCQ DX, R8 + + // r1 += 19×a3×b3 + MOVQ 24(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 24(BX) + ADDQ AX, R9 + ADCQ DX, R8 + + // r1 += 19×a4×b2 + MOVQ 32(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 16(BX) + ADDQ AX, R9 + ADCQ DX, R8 + + // r2 = a0×b2 + MOVQ (CX), AX + MULQ 16(BX) + MOVQ AX, R11 + MOVQ DX, R10 + + // r2 += a1×b1 + MOVQ 8(CX), AX + MULQ 8(BX) + ADDQ AX, R11 + ADCQ DX, R10 + + // r2 += a2×b0 + MOVQ 16(CX), AX + MULQ (BX) + ADDQ AX, R11 + ADCQ DX, R10 + + // r2 += 19×a3×b4 + MOVQ 24(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 32(BX) + ADDQ AX, R11 + ADCQ DX, R10 + + // r2 += 19×a4×b3 + MOVQ 32(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 24(BX) + ADDQ AX, R11 + ADCQ DX, R10 + + // r3 = a0×b3 + MOVQ (CX), AX + MULQ 24(BX) + MOVQ AX, R13 + MOVQ DX, R12 + + // r3 += a1×b2 + MOVQ 8(CX), AX + MULQ 16(BX) + ADDQ AX, R13 + ADCQ DX, R12 + + // r3 += a2×b1 + MOVQ 16(CX), AX + MULQ 8(BX) + ADDQ AX, R13 + ADCQ DX, R12 + + // r3 += a3×b0 + MOVQ 24(CX), AX + MULQ (BX) + ADDQ AX, R13 + ADCQ DX, R12 + + // r3 += 19×a4×b4 + MOVQ 32(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 32(BX) + ADDQ AX, R13 + ADCQ DX, R12 + + // r4 = a0×b4 + MOVQ (CX), AX + MULQ 32(BX) + MOVQ AX, R15 + MOVQ DX, R14 + + // r4 += a1×b3 + MOVQ 8(CX), AX + MULQ 24(BX) + ADDQ AX, R15 + ADCQ DX, R14 + + // r4 += a2×b2 + MOVQ 16(CX), AX + MULQ 16(BX) + ADDQ AX, R15 + ADCQ DX, R14 + + // r4 += a3×b1 + MOVQ 24(CX), AX + MULQ 8(BX) + ADDQ AX, R15 + ADCQ DX, R14 + + // r4 += a4×b0 + MOVQ 32(CX), AX + MULQ (BX) + ADDQ AX, R15 + ADCQ DX, R14 + + // First reduction chain + MOVQ $0x0007ffffffffffff, AX + SHLQ $0x0d, DI, SI + SHLQ $0x0d, R9, R8 + SHLQ $0x0d, R11, R10 + SHLQ $0x0d, R13, R12 + SHLQ $0x0d, R15, R14 + ANDQ AX, DI + IMUL3Q $0x13, R14, R14 + ADDQ R14, DI + ANDQ AX, R9 + ADDQ SI, R9 + ANDQ AX, R11 + ADDQ R8, R11 + ANDQ AX, R13 + ADDQ R10, R13 + ANDQ AX, R15 + ADDQ R12, R15 + + // Second reduction chain (carryPropagate) + MOVQ DI, SI + SHRQ $0x33, SI + MOVQ R9, R8 + SHRQ $0x33, R8 + MOVQ R11, R10 + SHRQ $0x33, R10 + MOVQ R13, R12 + SHRQ $0x33, R12 + MOVQ R15, R14 + SHRQ $0x33, R14 + ANDQ AX, DI + IMUL3Q $0x13, R14, R14 + ADDQ R14, DI + ANDQ AX, R9 + ADDQ SI, R9 + ANDQ AX, R11 + ADDQ R8, R11 + ANDQ AX, R13 + ADDQ R10, R13 + ANDQ AX, R15 + ADDQ R12, R15 + + // Store output + MOVQ out+0(FP), AX + MOVQ DI, (AX) + MOVQ R9, 8(AX) + MOVQ R11, 16(AX) + MOVQ R13, 24(AX) + MOVQ R15, 32(AX) + RET + +// func feSquare(out *Element, a *Element) +TEXT ·feSquare(SB), NOSPLIT, $0-16 + MOVQ a+8(FP), CX + + // r0 = l0×l0 + MOVQ (CX), AX + MULQ (CX) + MOVQ AX, SI + MOVQ DX, BX + + // r0 += 38×l1×l4 + MOVQ 8(CX), AX + IMUL3Q $0x26, AX, AX + MULQ 32(CX) + ADDQ AX, SI + ADCQ DX, BX + + // r0 += 38×l2×l3 + MOVQ 16(CX), AX + IMUL3Q $0x26, AX, AX + MULQ 24(CX) + ADDQ AX, SI + ADCQ DX, BX + + // r1 = 2×l0×l1 + MOVQ (CX), AX + SHLQ $0x01, AX + MULQ 8(CX) + MOVQ AX, R8 + MOVQ DX, DI + + // r1 += 38×l2×l4 + MOVQ 16(CX), AX + IMUL3Q $0x26, AX, AX + MULQ 32(CX) + ADDQ AX, R8 + ADCQ DX, DI + + // r1 += 19×l3×l3 + MOVQ 24(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 24(CX) + ADDQ AX, R8 + ADCQ DX, DI + + // r2 = 2×l0×l2 + MOVQ (CX), AX + SHLQ $0x01, AX + MULQ 16(CX) + MOVQ AX, R10 + MOVQ DX, R9 + + // r2 += l1×l1 + MOVQ 8(CX), AX + MULQ 8(CX) + ADDQ AX, R10 + ADCQ DX, R9 + + // r2 += 38×l3×l4 + MOVQ 24(CX), AX + IMUL3Q $0x26, AX, AX + MULQ 32(CX) + ADDQ AX, R10 + ADCQ DX, R9 + + // r3 = 2×l0×l3 + MOVQ (CX), AX + SHLQ $0x01, AX + MULQ 24(CX) + MOVQ AX, R12 + MOVQ DX, R11 + + // r3 += 2×l1×l2 + MOVQ 8(CX), AX + IMUL3Q $0x02, AX, AX + MULQ 16(CX) + ADDQ AX, R12 + ADCQ DX, R11 + + // r3 += 19×l4×l4 + MOVQ 32(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 32(CX) + ADDQ AX, R12 + ADCQ DX, R11 + + // r4 = 2×l0×l4 + MOVQ (CX), AX + SHLQ $0x01, AX + MULQ 32(CX) + MOVQ AX, R14 + MOVQ DX, R13 + + // r4 += 2×l1×l3 + MOVQ 8(CX), AX + IMUL3Q $0x02, AX, AX + MULQ 24(CX) + ADDQ AX, R14 + ADCQ DX, R13 + + // r4 += l2×l2 + MOVQ 16(CX), AX + MULQ 16(CX) + ADDQ AX, R14 + ADCQ DX, R13 + + // First reduction chain + MOVQ $0x0007ffffffffffff, AX + SHLQ $0x0d, SI, BX + SHLQ $0x0d, R8, DI + SHLQ $0x0d, R10, R9 + SHLQ $0x0d, R12, R11 + SHLQ $0x0d, R14, R13 + ANDQ AX, SI + IMUL3Q $0x13, R13, R13 + ADDQ R13, SI + ANDQ AX, R8 + ADDQ BX, R8 + ANDQ AX, R10 + ADDQ DI, R10 + ANDQ AX, R12 + ADDQ R9, R12 + ANDQ AX, R14 + ADDQ R11, R14 + + // Second reduction chain (carryPropagate) + MOVQ SI, BX + SHRQ $0x33, BX + MOVQ R8, DI + SHRQ $0x33, DI + MOVQ R10, R9 + SHRQ $0x33, R9 + MOVQ R12, R11 + SHRQ $0x33, R11 + MOVQ R14, R13 + SHRQ $0x33, R13 + ANDQ AX, SI + IMUL3Q $0x13, R13, R13 + ADDQ R13, SI + ANDQ AX, R8 + ADDQ BX, R8 + ANDQ AX, R10 + ADDQ DI, R10 + ANDQ AX, R12 + ADDQ R9, R12 + ANDQ AX, R14 + ADDQ R11, R14 + + // Store output + MOVQ out+0(FP), AX + MOVQ SI, (AX) + MOVQ R8, 8(AX) + MOVQ R10, 16(AX) + MOVQ R12, 24(AX) + MOVQ R14, 32(AX) + RET diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64_noasm.go b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64_noasm.go new file mode 100644 index 0000000000000..ddb6c9b8f7f24 --- /dev/null +++ b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64_noasm.go @@ -0,0 +1,12 @@ +// Copyright (c) 2019 The 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. + +//go:build !amd64 || !gc || purego +// +build !amd64 !gc purego + +package field + +func feMul(v, x, y *Element) { feMulGeneric(v, x, y) } + +func feSquare(v, x *Element) { feSquareGeneric(v, x) } diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.go b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.go new file mode 100644 index 0000000000000..af459ef51549e --- /dev/null +++ b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.go @@ -0,0 +1,16 @@ +// Copyright (c) 2020 The 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. + +//go:build arm64 && gc && !purego +// +build arm64,gc,!purego + +package field + +//go:noescape +func carryPropagate(v *Element) + +func (v *Element) carryPropagate() *Element { + carryPropagate(v) + return v +} diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.s b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.s new file mode 100644 index 0000000000000..5c91e458923e3 --- /dev/null +++ b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.s @@ -0,0 +1,43 @@ +// Copyright (c) 2020 The 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. + +//go:build arm64 && gc && !purego +// +build arm64,gc,!purego + +#include "textflag.h" + +// carryPropagate works exactly like carryPropagateGeneric and uses the +// same AND, ADD, and LSR+MADD instructions emitted by the compiler, but +// avoids loading R0-R4 twice and uses LDP and STP. +// +// See https://golang.org/issues/43145 for the main compiler issue. +// +// func carryPropagate(v *Element) +TEXT ·carryPropagate(SB),NOFRAME|NOSPLIT,$0-8 + MOVD v+0(FP), R20 + + LDP 0(R20), (R0, R1) + LDP 16(R20), (R2, R3) + MOVD 32(R20), R4 + + AND $0x7ffffffffffff, R0, R10 + AND $0x7ffffffffffff, R1, R11 + AND $0x7ffffffffffff, R2, R12 + AND $0x7ffffffffffff, R3, R13 + AND $0x7ffffffffffff, R4, R14 + + ADD R0>>51, R11, R11 + ADD R1>>51, R12, R12 + ADD R2>>51, R13, R13 + ADD R3>>51, R14, R14 + // R4>>51 * 19 + R10 -> R10 + LSR $51, R4, R21 + MOVD $19, R22 + MADD R22, R10, R21, R10 + + STP (R10, R11), 0(R20) + STP (R12, R13), 16(R20) + MOVD R14, 32(R20) + + RET diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64_noasm.go b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64_noasm.go new file mode 100644 index 0000000000000..234a5b2e5d18a --- /dev/null +++ b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64_noasm.go @@ -0,0 +1,12 @@ +// Copyright (c) 2021 The 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. + +//go:build !arm64 || !gc || purego +// +build !arm64 !gc purego + +package field + +func (v *Element) carryPropagate() *Element { + return v.carryPropagateGeneric() +} diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_generic.go b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_generic.go new file mode 100644 index 0000000000000..7b5b78cbd6d7b --- /dev/null +++ b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_generic.go @@ -0,0 +1,264 @@ +// Copyright (c) 2017 The 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 field + +import "math/bits" + +// uint128 holds a 128-bit number as two 64-bit limbs, for use with the +// bits.Mul64 and bits.Add64 intrinsics. +type uint128 struct { + lo, hi uint64 +} + +// mul64 returns a * b. +func mul64(a, b uint64) uint128 { + hi, lo := bits.Mul64(a, b) + return uint128{lo, hi} +} + +// addMul64 returns v + a * b. +func addMul64(v uint128, a, b uint64) uint128 { + hi, lo := bits.Mul64(a, b) + lo, c := bits.Add64(lo, v.lo, 0) + hi, _ = bits.Add64(hi, v.hi, c) + return uint128{lo, hi} +} + +// shiftRightBy51 returns a >> 51. a is assumed to be at most 115 bits. +func shiftRightBy51(a uint128) uint64 { + return (a.hi << (64 - 51)) | (a.lo >> 51) +} + +func feMulGeneric(v, a, b *Element) { + a0 := a.l0 + a1 := a.l1 + a2 := a.l2 + a3 := a.l3 + a4 := a.l4 + + b0 := b.l0 + b1 := b.l1 + b2 := b.l2 + b3 := b.l3 + b4 := b.l4 + + // Limb multiplication works like pen-and-paper columnar multiplication, but + // with 51-bit limbs instead of digits. + // + // a4 a3 a2 a1 a0 x + // b4 b3 b2 b1 b0 = + // ------------------------ + // a4b0 a3b0 a2b0 a1b0 a0b0 + + // a4b1 a3b1 a2b1 a1b1 a0b1 + + // a4b2 a3b2 a2b2 a1b2 a0b2 + + // a4b3 a3b3 a2b3 a1b3 a0b3 + + // a4b4 a3b4 a2b4 a1b4 a0b4 = + // ---------------------------------------------- + // r8 r7 r6 r5 r4 r3 r2 r1 r0 + // + // We can then use the reduction identity (a * 2²⁵⁵ + b = a * 19 + b) to + // reduce the limbs that would overflow 255 bits. r5 * 2²⁵⁵ becomes 19 * r5, + // r6 * 2³⁰⁶ becomes 19 * r6 * 2⁵¹, etc. + // + // Reduction can be carried out simultaneously to multiplication. For + // example, we do not compute r5: whenever the result of a multiplication + // belongs to r5, like a1b4, we multiply it by 19 and add the result to r0. + // + // a4b0 a3b0 a2b0 a1b0 a0b0 + + // a3b1 a2b1 a1b1 a0b1 19×a4b1 + + // a2b2 a1b2 a0b2 19×a4b2 19×a3b2 + + // a1b3 a0b3 19×a4b3 19×a3b3 19×a2b3 + + // a0b4 19×a4b4 19×a3b4 19×a2b4 19×a1b4 = + // -------------------------------------- + // r4 r3 r2 r1 r0 + // + // Finally we add up the columns into wide, overlapping limbs. + + a1_19 := a1 * 19 + a2_19 := a2 * 19 + a3_19 := a3 * 19 + a4_19 := a4 * 19 + + // r0 = a0×b0 + 19×(a1×b4 + a2×b3 + a3×b2 + a4×b1) + r0 := mul64(a0, b0) + r0 = addMul64(r0, a1_19, b4) + r0 = addMul64(r0, a2_19, b3) + r0 = addMul64(r0, a3_19, b2) + r0 = addMul64(r0, a4_19, b1) + + // r1 = a0×b1 + a1×b0 + 19×(a2×b4 + a3×b3 + a4×b2) + r1 := mul64(a0, b1) + r1 = addMul64(r1, a1, b0) + r1 = addMul64(r1, a2_19, b4) + r1 = addMul64(r1, a3_19, b3) + r1 = addMul64(r1, a4_19, b2) + + // r2 = a0×b2 + a1×b1 + a2×b0 + 19×(a3×b4 + a4×b3) + r2 := mul64(a0, b2) + r2 = addMul64(r2, a1, b1) + r2 = addMul64(r2, a2, b0) + r2 = addMul64(r2, a3_19, b4) + r2 = addMul64(r2, a4_19, b3) + + // r3 = a0×b3 + a1×b2 + a2×b1 + a3×b0 + 19×a4×b4 + r3 := mul64(a0, b3) + r3 = addMul64(r3, a1, b2) + r3 = addMul64(r3, a2, b1) + r3 = addMul64(r3, a3, b0) + r3 = addMul64(r3, a4_19, b4) + + // r4 = a0×b4 + a1×b3 + a2×b2 + a3×b1 + a4×b0 + r4 := mul64(a0, b4) + r4 = addMul64(r4, a1, b3) + r4 = addMul64(r4, a2, b2) + r4 = addMul64(r4, a3, b1) + r4 = addMul64(r4, a4, b0) + + // After the multiplication, we need to reduce (carry) the five coefficients + // to obtain a result with limbs that are at most slightly larger than 2⁵¹, + // to respect the Element invariant. + // + // Overall, the reduction works the same as carryPropagate, except with + // wider inputs: we take the carry for each coefficient by shifting it right + // by 51, and add it to the limb above it. The top carry is multiplied by 19 + // according to the reduction identity and added to the lowest limb. + // + // The largest coefficient (r0) will be at most 111 bits, which guarantees + // that all carries are at most 111 - 51 = 60 bits, which fits in a uint64. + // + // r0 = a0×b0 + 19×(a1×b4 + a2×b3 + a3×b2 + a4×b1) + // r0 < 2⁵²×2⁵² + 19×(2⁵²×2⁵² + 2⁵²×2⁵² + 2⁵²×2⁵² + 2⁵²×2⁵²) + // r0 < (1 + 19 × 4) × 2⁵² × 2⁵² + // r0 < 2⁷ × 2⁵² × 2⁵² + // r0 < 2¹¹¹ + // + // Moreover, the top coefficient (r4) is at most 107 bits, so c4 is at most + // 56 bits, and c4 * 19 is at most 61 bits, which again fits in a uint64 and + // allows us to easily apply the reduction identity. + // + // r4 = a0×b4 + a1×b3 + a2×b2 + a3×b1 + a4×b0 + // r4 < 5 × 2⁵² × 2⁵² + // r4 < 2¹⁰⁷ + // + + c0 := shiftRightBy51(r0) + c1 := shiftRightBy51(r1) + c2 := shiftRightBy51(r2) + c3 := shiftRightBy51(r3) + c4 := shiftRightBy51(r4) + + rr0 := r0.lo&maskLow51Bits + c4*19 + rr1 := r1.lo&maskLow51Bits + c0 + rr2 := r2.lo&maskLow51Bits + c1 + rr3 := r3.lo&maskLow51Bits + c2 + rr4 := r4.lo&maskLow51Bits + c3 + + // Now all coefficients fit into 64-bit registers but are still too large to + // be passed around as a Element. We therefore do one last carry chain, + // where the carries will be small enough to fit in the wiggle room above 2⁵¹. + *v = Element{rr0, rr1, rr2, rr3, rr4} + v.carryPropagate() +} + +func feSquareGeneric(v, a *Element) { + l0 := a.l0 + l1 := a.l1 + l2 := a.l2 + l3 := a.l3 + l4 := a.l4 + + // Squaring works precisely like multiplication above, but thanks to its + // symmetry we get to group a few terms together. + // + // l4 l3 l2 l1 l0 x + // l4 l3 l2 l1 l0 = + // ------------------------ + // l4l0 l3l0 l2l0 l1l0 l0l0 + + // l4l1 l3l1 l2l1 l1l1 l0l1 + + // l4l2 l3l2 l2l2 l1l2 l0l2 + + // l4l3 l3l3 l2l3 l1l3 l0l3 + + // l4l4 l3l4 l2l4 l1l4 l0l4 = + // ---------------------------------------------- + // r8 r7 r6 r5 r4 r3 r2 r1 r0 + // + // l4l0 l3l0 l2l0 l1l0 l0l0 + + // l3l1 l2l1 l1l1 l0l1 19×l4l1 + + // l2l2 l1l2 l0l2 19×l4l2 19×l3l2 + + // l1l3 l0l3 19×l4l3 19×l3l3 19×l2l3 + + // l0l4 19×l4l4 19×l3l4 19×l2l4 19×l1l4 = + // -------------------------------------- + // r4 r3 r2 r1 r0 + // + // With precomputed 2×, 19×, and 2×19× terms, we can compute each limb with + // only three Mul64 and four Add64, instead of five and eight. + + l0_2 := l0 * 2 + l1_2 := l1 * 2 + + l1_38 := l1 * 38 + l2_38 := l2 * 38 + l3_38 := l3 * 38 + + l3_19 := l3 * 19 + l4_19 := l4 * 19 + + // r0 = l0×l0 + 19×(l1×l4 + l2×l3 + l3×l2 + l4×l1) = l0×l0 + 19×2×(l1×l4 + l2×l3) + r0 := mul64(l0, l0) + r0 = addMul64(r0, l1_38, l4) + r0 = addMul64(r0, l2_38, l3) + + // r1 = l0×l1 + l1×l0 + 19×(l2×l4 + l3×l3 + l4×l2) = 2×l0×l1 + 19×2×l2×l4 + 19×l3×l3 + r1 := mul64(l0_2, l1) + r1 = addMul64(r1, l2_38, l4) + r1 = addMul64(r1, l3_19, l3) + + // r2 = l0×l2 + l1×l1 + l2×l0 + 19×(l3×l4 + l4×l3) = 2×l0×l2 + l1×l1 + 19×2×l3×l4 + r2 := mul64(l0_2, l2) + r2 = addMul64(r2, l1, l1) + r2 = addMul64(r2, l3_38, l4) + + // r3 = l0×l3 + l1×l2 + l2×l1 + l3×l0 + 19×l4×l4 = 2×l0×l3 + 2×l1×l2 + 19×l4×l4 + r3 := mul64(l0_2, l3) + r3 = addMul64(r3, l1_2, l2) + r3 = addMul64(r3, l4_19, l4) + + // r4 = l0×l4 + l1×l3 + l2×l2 + l3×l1 + l4×l0 = 2×l0×l4 + 2×l1×l3 + l2×l2 + r4 := mul64(l0_2, l4) + r4 = addMul64(r4, l1_2, l3) + r4 = addMul64(r4, l2, l2) + + c0 := shiftRightBy51(r0) + c1 := shiftRightBy51(r1) + c2 := shiftRightBy51(r2) + c3 := shiftRightBy51(r3) + c4 := shiftRightBy51(r4) + + rr0 := r0.lo&maskLow51Bits + c4*19 + rr1 := r1.lo&maskLow51Bits + c0 + rr2 := r2.lo&maskLow51Bits + c1 + rr3 := r3.lo&maskLow51Bits + c2 + rr4 := r4.lo&maskLow51Bits + c3 + + *v = Element{rr0, rr1, rr2, rr3, rr4} + v.carryPropagate() +} + +// carryPropagate brings the limbs below 52 bits by applying the reduction +// identity (a * 2²⁵⁵ + b = a * 19 + b) to the l4 carry. TODO inline +func (v *Element) carryPropagateGeneric() *Element { + c0 := v.l0 >> 51 + c1 := v.l1 >> 51 + c2 := v.l2 >> 51 + c3 := v.l3 >> 51 + c4 := v.l4 >> 51 + + v.l0 = v.l0&maskLow51Bits + c4*19 + v.l1 = v.l1&maskLow51Bits + c0 + v.l2 = v.l2&maskLow51Bits + c1 + v.l3 = v.l3&maskLow51Bits + c2 + v.l4 = v.l4&maskLow51Bits + c3 + + return v +} diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/sync.checkpoint b/vendor/golang.org/x/crypto/curve25519/internal/field/sync.checkpoint new file mode 100644 index 0000000000000..e3685f95cab22 --- /dev/null +++ b/vendor/golang.org/x/crypto/curve25519/internal/field/sync.checkpoint @@ -0,0 +1 @@ +b0c49ae9f59d233526f8934262c5bbbe14d4358d diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/sync.sh b/vendor/golang.org/x/crypto/curve25519/internal/field/sync.sh new file mode 100644 index 0000000000000..1ba22a8b4c9a2 --- /dev/null +++ b/vendor/golang.org/x/crypto/curve25519/internal/field/sync.sh @@ -0,0 +1,19 @@ +#! /bin/bash +set -euo pipefail + +cd "$(git rev-parse --show-toplevel)" + +STD_PATH=src/crypto/ed25519/internal/edwards25519/field +LOCAL_PATH=curve25519/internal/field +LAST_SYNC_REF=$(cat $LOCAL_PATH/sync.checkpoint) + +git fetch https://go.googlesource.com/go master + +if git diff --quiet $LAST_SYNC_REF:$STD_PATH FETCH_HEAD:$STD_PATH; then + echo "No changes." +else + NEW_REF=$(git rev-parse FETCH_HEAD | tee $LOCAL_PATH/sync.checkpoint) + echo "Applying changes from $LAST_SYNC_REF to $NEW_REF..." + git diff $LAST_SYNC_REF:$STD_PATH FETCH_HEAD:$STD_PATH | \ + git apply -3 --directory=$LOCAL_PATH +fi diff --git a/vendor/golang.org/x/crypto/poly1305/sum_amd64.s b/vendor/golang.org/x/crypto/poly1305/sum_amd64.s index 2cb03731408c9..1d74f0f88189b 100644 --- a/vendor/golang.org/x/crypto/poly1305/sum_amd64.s +++ b/vendor/golang.org/x/crypto/poly1305/sum_amd64.s @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build gc && !purego // +build gc,!purego #include "textflag.h" diff --git a/vendor/golang.org/x/crypto/poly1305/sum_ppc64le.s b/vendor/golang.org/x/crypto/poly1305/sum_ppc64le.s index 3cede539dc40d..58422aad23057 100644 --- a/vendor/golang.org/x/crypto/poly1305/sum_ppc64le.s +++ b/vendor/golang.org/x/crypto/poly1305/sum_ppc64le.s @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build gc && !purego // +build gc,!purego #include "textflag.h" diff --git a/vendor/golang.org/x/crypto/poly1305/sum_s390x.s b/vendor/golang.org/x/crypto/poly1305/sum_s390x.s index bdd882c606def..69c64f84217de 100644 --- a/vendor/golang.org/x/crypto/poly1305/sum_s390x.s +++ b/vendor/golang.org/x/crypto/poly1305/sum_s390x.s @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build gc && !purego // +build gc,!purego #include "textflag.h" diff --git a/vendor/golang.org/x/crypto/ssh/client.go b/vendor/golang.org/x/crypto/ssh/client.go index 7b00bff1caa5e..99f68bd32e959 100644 --- a/vendor/golang.org/x/crypto/ssh/client.go +++ b/vendor/golang.org/x/crypto/ssh/client.go @@ -77,7 +77,7 @@ func NewClientConn(c net.Conn, addr string, config *ClientConfig) (Conn, <-chan } conn := &connection{ - sshConn: sshConn{conn: c}, + sshConn: sshConn{conn: c, user: fullConf.User}, } if err := conn.clientHandshake(addr, &fullConf); err != nil { diff --git a/vendor/golang.org/x/net/html/parse.go b/vendor/golang.org/x/net/html/parse.go index f91466f7cd745..038941d708522 100644 --- a/vendor/golang.org/x/net/html/parse.go +++ b/vendor/golang.org/x/net/html/parse.go @@ -663,6 +663,24 @@ func inHeadIM(p *parser) bool { // Ignore the token. return true case a.Template: + // TODO: remove this divergence from the HTML5 spec. + // + // We don't handle all of the corner cases when mixing foreign + // content (i.e. or ) with