diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index a92d007..d0bd927 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,4 +1,3 @@
# These are supported funding model platforms
open_collective: webhook
-github: adnanh
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
deleted file mode 100644
index 4cd74f4..0000000
--- a/.github/workflows/build.yml
+++ /dev/null
@@ -1,30 +0,0 @@
-name: build
-on: [push, pull_request]
-jobs:
- build:
- env:
- # The special value "local" tells Go to use the bundled Go
- # version rather than trying to fetch one according to a
- # `toolchain` value in `go.mod`. This ensures that we're
- # really running the Go version in the CI matrix rather than
- # one that the Go command has upgraded to automatically.
- GOTOOLCHAIN: local
- strategy:
- matrix:
- go-version: [1.21.x, 1.22.x]
- os: [ubuntu-latest, macos-latest, windows-latest]
-
- runs-on: ${{ matrix.os }}
-
- steps:
- - uses: actions/checkout@v4
- - uses: actions/setup-go@v5
- with:
- go-version: ${{ matrix.go-version }}
- id: go
-
- - name: Build
- run: go build -v
-
- - name: Test
- run: go test -v ./...
diff --git a/.gitignore b/.gitignore
index 9ad2297..ad87e5e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,3 @@
coverage
webhook
/test/hookecho
-build
diff --git a/.travis.yml b/.travis.yml
index 54e4652..4df32e3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,29 +1,23 @@
language: go
go:
- - 1.14.x
- - master
+ - 1.11.x
+ - 1.12.x
+ - 1.13.x
+ - tip
os:
- linux
- osx
- windows
-arch:
- - amd64
- - arm64
-
matrix:
fast_finish: true
allow_failures:
- - go: master
+ - go: tip
exclude:
- os: windows
- go: master
- - os: windows
- arch: arm64
- - os: osx
- arch: arm64
+ go: tip
install:
- go get -d -v -t ./...
diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json
new file mode 100644
index 0000000..f4ab75a
--- /dev/null
+++ b/Godeps/Godeps.json
@@ -0,0 +1,40 @@
+{
+ "ImportPath": "github.com/adnanh/webhook",
+ "GoVersion": "go1.9",
+ "GodepVersion": "v79",
+ "Deps": [
+ {
+ "ImportPath": "github.com/codegangsta/negroni",
+ "Comment": "v0.2.0-151-g5bc66cf",
+ "Rev": "5bc66cf1ad89af58511e07e108a31f219ed61012"
+ },
+ {
+ "ImportPath": "github.com/ghodss/yaml",
+ "Comment": "v1.0.0",
+ "Rev": "0ca9ea5df5451ffdf184b4428c902747c2c11cd7"
+ },
+ {
+ "ImportPath": "github.com/gorilla/mux",
+ "Comment": "v1.5.0-2-gbdd5a5a",
+ "Rev": "bdd5a5a1b0b489d297b73eb62b5f6328df198bfc"
+ },
+ {
+ "ImportPath": "github.com/satori/go.uuid",
+ "Comment": "v1.1.0-8-g5bf94b6",
+ "Rev": "5bf94b69c6b68ee1b541973bb8e1144db23a194b"
+ },
+ {
+ "ImportPath": "golang.org/x/sys/unix",
+ "Rev": "ebfc5b4631820b793c9010c87fd8fef0f39eb082"
+ },
+ {
+ "ImportPath": "gopkg.in/fsnotify.v1",
+ "Comment": "v1.4.2",
+ "Rev": "629574ca2a5df945712d3079857300b5e4da0236"
+ },
+ {
+ "ImportPath": "gopkg.in/yaml.v2",
+ "Rev": "eb3733d160e74a9c7e442f435eb3bea458e1d19f"
+ }
+ ]
+}
diff --git a/Godeps/Readme b/Godeps/Readme
new file mode 100644
index 0000000..4cdaa53
--- /dev/null
+++ b/Godeps/Readme
@@ -0,0 +1,5 @@
+This directory tree is generated automatically by godep.
+
+Please do not edit.
+
+See https://github.com/tools/godep for more information.
diff --git a/Makefile b/Makefile
index 903b7a3..b341c1f 100644
--- a/Makefile
+++ b/Makefile
@@ -1,30 +1,24 @@
OS = darwin freebsd linux openbsd
ARCHS = 386 arm amd64 arm64
-.DEFAULT_GOAL := help
-
-.PHONY: help
-help:
- @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-16s\033[0m %s\n", $$1, $$2}'
-
all: build release release-windows
-build: deps ## Build the project
+build: deps
go build
-release: clean deps ## Generate releases for unix systems
+release: clean deps
@for arch in $(ARCHS);\
do \
for os in $(OS);\
do \
echo "Building $$os-$$arch"; \
mkdir -p build/webhook-$$os-$$arch/; \
- CGO_ENABLED=0 GOOS=$$os GOARCH=$$arch go build -o build/webhook-$$os-$$arch/webhook; \
+ GOOS=$$os GOARCH=$$arch go build -o build/webhook-$$os-$$arch/webhook; \
tar cz -C build -f build/webhook-$$os-$$arch.tar.gz webhook-$$os-$$arch; \
done \
done
-release-windows: clean deps ## Generate release for windows
+release-windows: clean deps
@for arch in $(ARCHS);\
do \
echo "Building windows-$$arch"; \
@@ -33,12 +27,12 @@ release-windows: clean deps ## Generate release for windows
tar cz -C build -f build/webhook-windows-$$arch.tar.gz webhook-windows-$$arch; \
done
-test: deps ## Execute tests
+test: deps
go test ./...
-deps: ## Install dependencies using go get
+deps:
go get -d -v -t ./...
-clean: ## Remove building artifacts
+clean:
rm -rf build
rm -f webhook
diff --git a/README.md b/README.md
index 279c494..1338a32 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# What is webhook? ![build-status][badge]
+# What is webhook?
@@ -17,20 +17,20 @@ If you use Mattermost or Slack, you can set up an "Outgoing webhook integration"
Everything else is the responsibility of the command's author.
-## Not what you're looking for?
-| | |
-| :-: | :-: |
-| Scriptable webhook gateway to safely run your custom builds, deploys, and proxy scripts on your servers. | An event gateway to reliably ingest, verify, queue, transform, filter, inspect, monitor, and replay webhooks. |
-
+# Hookdoo
+
+
+If you don't have time to waste configuring, hosting, debugging and maintaining your webhook instance, we offer a __SaaS__ solution that has all of the capabilities webhook provides, plus a lot more, and all that packaged in a nice friendly web interface. If you are interested, find out more at [hookdoo website](https://www.hookdoo.com/?ref=github-webhook-readme). If you have any questions, you can contact us at info@hookdoo.com
+
# Getting started
## Installation
### Building from source
-To get started, first make sure you've properly set up your [Go](http://golang.org/doc/install) 1.21 or newer environment and then run
+To get started, first make sure you've properly set up your [Golang](http://golang.org/doc/install) environment and then run the
```bash
-$ go build github.com/adnanh/webhook
+$ go get github.com/adnanh/webhook
```
-to build the latest version of the [webhook][w].
+to get the latest version of the [webhook][w].
### Using package manager
#### Snap store
@@ -42,16 +42,11 @@ If you are using Ubuntu linux (17.04 or later), you can install webhook using `s
#### Debian
If you are using Debian linux ("stretch" or later), you can install webhook using `sudo apt-get install webhook` which will install community packaged version (thanks [@freeekanayaka](https://github.com/freeekanayaka)) from https://packages.debian.org/sid/webhook
-#### FreeBSD
-If you are using FreeBSD, you can install webhook using `pkg install webhook`.
-
### Download prebuilt binaries
Prebuilt binaries for different architectures are available at [GitHub Releases](https://github.com/adnanh/webhook/releases).
## Configuration
-Next step is to define some hooks you want [webhook][w] to serve.
-[webhook][w] supports JSON or YAML configuration files, but we'll focus primarily on JSON in the following example.
-Begin by creating an empty file named `hooks.json`. This file will contain an array of hooks the [webhook][w] will serve. Check [Hook definition page](docs/Hook-Definition.md) to see the detailed description of what properties a hook can contain, and how to use them.
+Next step is to define some hooks you want [webhook][w] to serve. Begin by creating an empty file named `hooks.json`. This file will contain an array of hooks the [webhook][w] will serve. Check [Hook definition page](docs/Hook-Definition.md) to see the detailed description of what properties a hook can contain, and how to use them.
Let's define a simple hook named `redeploy-webhook` that will run a redeploy script located in `/var/scripts/redeploy.sh`. Make sure that your bash script has `#!/bin/sh` shebang on top.
@@ -66,13 +61,6 @@ Our `hooks.json` file will now look like this:
]
```
-**NOTE:** If you prefer YAML, the equivalent `hooks.yaml` file would be:
-```yaml
-- id: redeploy-webhook
- execute-command: "/var/scripts/redeploy.sh"
- command-working-directory: "/var/webhook"
-```
-
You can now run [webhook][w] using
```bash
$ /path/to/webhook -hooks hooks.json -verbose
@@ -89,50 +77,23 @@ By performing a simple HTTP GET or POST request to that endpoint, your specified
However, hook defined like that could pose a security threat to your system, because anyone who knows your endpoint, can send a request and execute your command. To prevent that, you can use the `"trigger-rule"` property for your hook, to specify the exact circumstances under which the hook would be triggered. For example, you can use them to add a secret that you must supply as a parameter in order to successfully trigger the hook. Please check out the [Hook rules page](docs/Hook-Rules.md) for detailed list of available rules and their usage.
-## Multipart Form Data
-[webhook][w] provides limited support the parsing of multipart form data.
-Multipart form data can contain two types of parts: values and files.
-All form _values_ are automatically added to the `payload` scope.
-Use the `parse-parameters-as-json` settings to parse a given value as JSON.
-All files are ignored unless they match one of the following criteria:
-
-1. The `Content-Type` header is `application/json`.
-1. The part is named in the `parse-parameters-as-json` setting.
-
-In either case, the given file part will be parsed as JSON and added to the `payload` map.
-
## Templates
-[webhook][w] can parse the hooks configuration file as a Go template when given the `-template` [CLI parameter](docs/Webhook-Parameters.md). See the [Templates page](docs/Templates.md) for more details on template usage.
+[webhook][w] can parse the `hooks.json` input file as a Go template when given the `-template` [CLI parameter](docs/Webhook-Parameters.md). See the [Templates page](docs/Templates.md) for more details on template usage.
## Using HTTPS
[webhook][w] by default serves hooks using http. If you want [webhook][w] to serve secure content using https, you can use the `-secure` flag while starting [webhook][w]. Files containing a certificate and matching private key for the server must be provided using the `-cert /path/to/cert.pem` and `-key /path/to/key.pem` flags. If the certificate is signed by a certificate authority, the cert file should be the concatenation of the server's certificate followed by the CA's certificate.
-TLS version and cipher suite selection flags are available from the command line. To list available cipher suites, use the `-list-cipher-suites` flag. The `-tls-min-version` flag can be used with `-list-cipher-suites`.
-
-## Running behind a reverse proxy
-[webhook][w] may be run behind a "reverse proxy" - another web-facing server such as [Apache httpd](https://httpd.apache.org) or [Nginx](https://nginx.org) that accepts requests from clients and forwards them on to [webhook][h]. You can have [webhook][w] listen on a regular TCP port or on a Unix domain socket (with the `-socket` flag), then configure your proxy to send requests for a specific host name or sub-path over that port or socket to [webhook][w].
-
-Note that when running in this mode the [`ip-whitelist`](docs/Hook-Rules.md#match-whitelisted-ip-range) trigger rule will not work as expected, since it will be checking the address of the _proxy_, not the _client_. Client IP restrictions will need to be enforced within the proxy, before it decides whether to forward the request to [webhook][w].
-
## CORS Headers
If you want to set CORS headers, you can use the `-header name=value` flag while starting [webhook][w] to set the appropriate CORS headers that will be returned with each response.
-## Running under `systemd`
-On platforms that use [systemd](https://systemd.io), [webhook][w] supports the _socket activation_ mechanism. If [webhook][w] detects that it has been launched from a systemd-managed socket it will automatically use that instead of opening its own listening port. See [the systemd page](docs/Systemd-Activation.md) for full details.
-
## Interested in running webhook inside of a Docker container?
-You can use one of the following Docker images, or create your own (please read [this discussion](https://github.com/adnanh/webhook/issues/63)):
-- [almir/webhook](https://github.com/almir/docker-webhook)
-- [roxedus/webhook](https://github.com/Roxedus/docker-webhook)
-- [thecatlady/webhook](https://github.com/thecatlady/docker-webhook)
-- [lwlook/webhook](https://hub.docker.com/r/lwlook/webhook) - This setup allows direct access to the Docker host, providing a streamlined and efficient way to manage webhooks.
+You can use [almir/webhook](https://hub.docker.com/r/almir/webhook/) docker image, or create your own (please read [this discussion](https://github.com/adnanh/webhook/issues/63)).
## Examples
Check out [Hook examples page](docs/Hook-Examples.md) for more complex examples of hooks.
### Guides featuring webhook
- - [Plex 2 Telegram](https://gitlab.com/-/snippets/1972594) by [@psyhomb](https://github.com/psyhomb)
- - [Webhook & JIRA](https://sites.google.com/site/mrxpalmeiras/more/jira-webhooks) by [@perfecto25](https://github.com/perfecto25)
+ - [Webhook & JIRA](https://sites.google.com/site/mrxpalmeiras/notes/jira-webhooks) by [@perfecto25](https://github.com/perfecto25)
- [Trigger Ansible AWX job runs on SCM (e.g. git) commit](http://jpmens.net/2017/10/23/trigger-awx-job-runs-on-scm-commit/) by [@jpmens](http://mens.de/)
- [Deploy using GitHub webhooks](https://davidauthier.wearemd.com/blog/deploy-using-github-webhooks.html) by [@awea](https://davidauthier.wearemd.com)
- [Setting up Automatic Deployment and Builds Using Webhooks](https://willbrowning.me/setting-up-automatic-deployment-and-builds-using-webhooks/) by [Will Browning](https://willbrowning.me/about/)
@@ -145,12 +106,7 @@ Check out [Hook examples page](docs/Hook-Examples.md) for more complex examples
- [Adventures in webhooks](https://medium.com/@draketech/adventures-in-webhooks-2d6584501c62) by [Drake](https://medium.com/@draketech)
- [GitHub pro tips](http://notes.spencerlyon.com/2016/01/04/github-pro-tips/) by [Spencer Lyon](http://notes.spencerlyon.com/)
- [XiaoMi Vacuum + Amazon Button = Dash Cleaning](https://www.instructables.com/id/XiaoMi-Vacuum-Amazon-Button-Dash-Cleaning/) by [c0mmensal](https://www.instructables.com/member/c0mmensal/)
- - [Set up Automated Deployments From Github With Webhook](https://maximorlov.com/automated-deployments-from-github-with-webhook/) by [Maxim Orlov](https://twitter.com/_maximization)
- - [Kick Me Now with Webhooks](https://freebsdfoundation.org/kick-me-now-with-webhooks/) By Dave Cottlehuber
- VIDEO: [Gitlab CI/CD configuration using Docker and adnanh/webhook to deploy on VPS - Tutorial #1](https://www.youtube.com/watch?v=Qhn-lXjyrZA&feature=youtu.be) by [Yes! Let's Learn Software Engineering](https://www.youtube.com/channel/UCH4XJf2BZ_52fbf8fOBMF3w)
- - [Integrate automatic deployment in 20 minutes using webhooks + Nginx setup](https://anksus.me/blog/integrate-automatic-deployment-in-20-minutes-using-webhooks) by [Anksus](https://github.com/Anksus)
- - [Automatically redeploy your static blog with Gitea, Uberspace & Webhook](https://by.arran.nz/posts/code/webhook-deploy/) by [Arran](https://arran.nz)
-- [Automatically Updating My Zola Site Using a Webhook](https://osc.garden/blog/updating-site-with-webhook/) by [Óscar Fernández](https://osc.garden/)
- ...
- Want to add your own? Open an Issue or create a PR :-)
@@ -232,4 +188,3 @@ THE SOFTWARE.
[w]: https://github.com/adnanh/webhook
[wc]: https://github.com/adnanh/webhook-contrib
-[badge]: https://github.com/adnanh/webhook/workflows/build/badge.svg
diff --git a/docs/Hook-Definition.md b/docs/Hook-Definition.md
index 8a7e744..fd8836a 100644
--- a/docs/Hook-Definition.md
+++ b/docs/Hook-Definition.md
@@ -1,6 +1,5 @@
# Hook definition
-
-Hooks are defined as objects in the JSON or YAML hooks configuration file. Please note that in order to be considered valid, a hook object must contain the `id` and `execute-command` properties. All other properties are considered optional.
+Hooks are defined as JSON objects. Please note that in order to be considered valid, a hook object must contain the `id` and `execute-command` properties. All other properties are considered optional.
## Properties (keys)
@@ -11,7 +10,6 @@ Hooks are defined as objects in the JSON or YAML hooks configuration file. Pleas
* `response-headers` - specifies the list of headers in format `{"name": "X-Example-Header", "value": "it works"}` that will be returned in HTTP response for the hook
* `success-http-response-code` - specifies the HTTP status code to be returned upon success
* `incoming-payload-content-type` - sets the `Content-Type` of the incoming HTTP request (ie. `application/json`); useful when the request lacks a `Content-Type` or sends an erroneous value
- * `http-methods` - a list of allowed HTTP methods, such as `POST` and `GET`
* `include-command-output-in-response` - boolean whether webhook should wait for the command to finish and return the raw output as a response to the hook initiator. If the command fails to execute or encounters any errors while executing the response will result in 500 Internal Server Error HTTP status code, otherwise the 200 OK status code will be returned.
* `include-command-output-in-response-on-error` - boolean whether webhook should include command stdout & stderror as a response in failed executions. It only works if `include-command-output-in-response` is set to `true`.
* `parse-parameters-as-json` - specifies the list of arguments that contain JSON strings. These parameters will be decoded by webhook and you can access them like regular objects in rules and `pass-arguments-to-command`.
@@ -22,7 +20,6 @@ Hooks are defined as objects in the JSON or YAML hooks configuration file. Pleas
* `pass-file-to-command` - specifies a list of entries that will be serialized as a file. Incoming [data](Referencing-Request-Values.md) will be serialized in a request-temporary-file (otherwise parallel calls of the hook would lead to concurrent overwritings of the file). The filename to be addressed within the subsequent script is provided via an environment variable. Use `envname` to specify the name of the environment variable. If `envname` is not provided `HOOK_` and the name used to reference the request value are used. Defining `command-working-directory` will store the file relative to this location, if not provided, the systems temporary file directory will be used. If `base64decode` is true, the incoming binary data will be base 64 decoded prior to storing it into the file. By default the corresponding file will be removed after the webhook exited.
* `trigger-rule` - specifies the rule that will be evaluated in order to determine should the hook be triggered. Check [Hook rules page](Hook-Rules.md) to see the list of valid rules and their usage
* `trigger-rule-mismatch-http-response-code` - specifies the HTTP status code to be returned when the trigger rule is not satisfied
- * `trigger-signature-soft-failures` - allow signature validation failures within Or rules; by default, signature failures are treated as errors.
## Examples
Check out [Hook examples page](Hook-Examples.md) for more complex examples of hooks.
diff --git a/docs/Hook-Examples.md b/docs/Hook-Examples.md
index 1b01ea4..f2cab8c 100644
--- a/docs/Hook-Examples.md
+++ b/docs/Hook-Examples.md
@@ -1,32 +1,7 @@
-# Hook Examples
-
-Hooks are defined in a hooks configuration file in either JSON or YAML format,
-although the examples on this page all use the JSON format.
-
-🌱 This page is still a work in progress. Feel free to contribute!
-
-### Table of Contents
-
-* [Incoming Github webhook](#incoming-github-webhook)
-* [Incoming Bitbucket webhook](#incoming-bitbucket-webhook)
-* [Incoming Gitlab webhook](#incoming-gitlab-webhook)
-* [Incoming Gogs webhook](#incoming-gogs-webhook)
-* [Incoming Gitea webhook](#incoming-gitea-webhook)
-* [Slack slash command](#slack-slash-command)
-* [A simple webhook with a secret key in GET query](#a-simple-webhook-with-a-secret-key-in-get-query)
-* [JIRA Webhooks](#jira-webhooks)
-* [Pass File-to-command sample](#pass-file-to-command-sample)
-* [Incoming Scalr Webhook](#incoming-scalr-webhook)
-* [Travis CI webhook](#travis-ci-webhook)
-* [XML Payload](#xml-payload)
-* [Multipart Form Data](#multipart-form-data)
-* [Pass string arguments to command](#pass-string-arguments-to-command)
-* [Receive Synology DSM notifications](#receive-synology-notifications)
+# Hook examples
+This page is still work in progress. Feel free to contribute!
## Incoming Github webhook
-
-This example works on 2.8+ versions of Webhook - if you are on a previous series, change `payload-hmac-sha1` to `payload-hash-sha1`.
-
```json
[
{
@@ -55,7 +30,7 @@ This example works on 2.8+ versions of Webhook - if you are on a previous series
{
"match":
{
- "type": "payload-hmac-sha1",
+ "type": "payload-hash-sha1",
"secret": "mysecret",
"parameter":
{
@@ -84,7 +59,7 @@ This example works on 2.8+ versions of Webhook - if you are on a previous series
## Incoming Bitbucket webhook
-Bitbucket does not pass any secrets back to the webhook. [Per their documentation](https://support.atlassian.com/organization-administration/docs/ip-addresses-and-domains-for-atlassian-cloud-products/#Outgoing-Connections), in order to verify that the webhook came from Bitbucket you must whitelist a set of IP ranges:
+Bitbucket does not pass any secrets back to the webhook. [Per their documentation](https://confluence.atlassian.com/bitbucket/manage-webhooks-735643732.html#Managewebhooks-trigger_webhookTriggeringwebhooks), in order to verify that the webhook came from Bitbucket you must whitelist the IP range `104.192.143.0/24`:
```json
[
@@ -101,23 +76,11 @@ Bitbucket does not pass any secrets back to the webhook. [Per their documentati
],
"trigger-rule":
{
- "or":
- [
- { "match": { "type": "ip-whitelist", "ip-range": "13.52.5.96/28" } },
- { "match": { "type": "ip-whitelist", "ip-range": "13.236.8.224/28" } },
- { "match": { "type": "ip-whitelist", "ip-range": "18.136.214.96/28" } },
- { "match": { "type": "ip-whitelist", "ip-range": "18.184.99.224/28" } },
- { "match": { "type": "ip-whitelist", "ip-range": "18.234.32.224/28" } },
- { "match": { "type": "ip-whitelist", "ip-range": "18.246.31.224/28" } },
- { "match": { "type": "ip-whitelist", "ip-range": "52.215.192.224/28" } },
- { "match": { "type": "ip-whitelist", "ip-range": "104.192.137.240/28" } },
- { "match": { "type": "ip-whitelist", "ip-range": "104.192.138.240/28" } },
- { "match": { "type": "ip-whitelist", "ip-range": "104.192.140.240/28" } },
- { "match": { "type": "ip-whitelist", "ip-range": "104.192.142.240/28" } },
- { "match": { "type": "ip-whitelist", "ip-range": "104.192.143.240/28" } },
- { "match": { "type": "ip-whitelist", "ip-range": "185.166.143.240/28" } },
- { "match": { "type": "ip-whitelist", "ip-range": "185.166.142.240/28" } }
- ]
+ "match":
+ {
+ "type": "ip-whitelist",
+ "ip-range": "104.192.143.0/24"
+ }
}
}
]
@@ -187,7 +150,7 @@ Values in the request body can be accessed in the command or to the match rule b
{
"match":
{
- "type": "payload-hmac-sha256",
+ "type": "payload-hash-sha256",
"secret": "mysecret",
"parameter":
{
@@ -324,14 +287,14 @@ __Not recommended in production due to low security__
]
```
-## JIRA Webhooks
-[Guide by @perfecto25](https://sites.google.com/site/mrxpalmeiras/more/jira-webhooks)
+# JIRA Webhooks
+[Guide by @perfecto25](https://sites.google.com/site/mrxpalmeiras/notes/jira-webhooks)
-## Pass File-to-command sample
+# Pass File-to-command sample
-### Webhook configuration
+## Webhook configuration
-```json
+
[
{
"id": "test-file-webhook",
@@ -350,29 +313,29 @@ __Not recommended in production due to low security__
"include-command-output-in-response": true
}
]
-```
+
-### Sample client usage
+## Sample client usage
Store the following file as `testRequest.json`.
-```json
+
or in a single line, using https://github.com/jpmens/jo to generate the JSON code
-```console
+
jo binary=%filename.zip | curl -H "Content-Type:application/json" -X POST -d @- \
http://localhost:9000/hooks/test-file-webhook
-```
+
## Incoming Scalr Webhook
@@ -380,7 +343,7 @@ http://localhost:9000/hooks/test-file-webhook
Scalr makes webhook calls based on an event to a configured webhook endpoint (for example Host Down, Host Up). Webhook endpoints are URLs where Scalr will deliver Webhook notifications.
Scalr assigns a unique signing key for every configured webhook endpoint.
Refer to this URL for information on how to setup the webhook call on the Scalr side: [Scalr Wiki Webhooks](https://scalr-wiki.atlassian.net/wiki/spaces/docs/pages/6193173/Webhooks)
-In order to leverage the Signing Key for additional authentication/security you must configure the trigger rule with a match type of "scalr-signature".
+In order to leverage the Signing Key for addtional authentication/security you must configure the trigger rule with a match type of "scalr-signature".
```json
[
@@ -461,215 +424,3 @@ Travis sends webhooks as `payload=`, so the payload needs to be par
}
]
```
-
-## JSON Array Payload
-
-If the JSON payload is an array instead of an object, `webhook` will process the payload and place it into a "root" object.
-Therefore, references to payload values must begin with `root.`.
-
-For example, given the following payload (taken from the Sendgrid Event Webhook documentation):
-```json
-[
- {
- "email": "example@test.com",
- "timestamp": 1513299569,
- "smtp-id": "<14c5d75ce93.dfd.64b469@ismtpd-555>",
- "event": "processed",
- "category": "cat facts",
- "sg_event_id": "sg_event_id",
- "sg_message_id": "sg_message_id"
- },
- {
- "email": "example@test.com",
- "timestamp": 1513299569,
- "smtp-id": "<14c5d75ce93.dfd.64b469@ismtpd-555>",
- "event": "deferred",
- "category": "cat facts",
- "sg_event_id": "sg_event_id",
- "sg_message_id": "sg_message_id",
- "response": "400 try again later",
- "attempt": "5"
- }
-]
-```
-
-A reference to the second item in the array would look like this:
-```json
-[
- {
- "id": "sendgrid",
- "execute-command": "/root/my-server/deployment.sh",
- "command-working-directory": "/root/my-server",
- "trigger-rule": {
- "match": {
- "type": "value",
- "parameter": {
- "source": "payload",
- "name": "root.1.event"
- },
- "value": "deferred"
- }
- }
- }
-]
-```
-
-## XML Payload
-
-Given the following payload:
-
-```xml
-
-
-
-
-
-
- Hello!!
-
-
-```
-
-```json
-[
- {
- "id": "deploy",
- "execute-command": "/root/my-server/deployment.sh",
- "command-working-directory": "/root/my-server",
- "trigger-rule": {
- "and": [
- {
- "match": {
- "type": "value",
- "parameter": {
- "source": "payload",
- "name": "app.users.user.0.-name"
- },
- "value": "Jeff"
- }
- },
- {
- "match": {
- "type": "value",
- "parameter": {
- "source": "payload",
- "name": "app.messages.message.#text"
- },
- "value": "Hello!!"
- }
- },
- ],
- }
- }
-]
-```
-
-## Multipart Form Data
-
-Example of a [Plex Media Server webhook](https://support.plex.tv/articles/115002267687-webhooks/).
-The Plex Media Server will send two parts: payload and thumb.
-We only care about the payload part.
-
-```json
-[
- {
- "id": "plex",
- "execute-command": "play-command.sh",
- "parse-parameters-as-json": [
- {
- "source": "payload",
- "name": "payload"
- }
- ],
- "trigger-rule":
- {
- "match":
- {
- "type": "value",
- "parameter": {
- "source": "payload",
- "name": "payload.event"
- },
- "value": "media.play"
- }
- }
- }
-]
-```
-
-Each part of a multipart form data body will have a `Content-Disposition` header.
-Some example headers:
-
-```
-Content-Disposition: form-data; name="payload"
-Content-Disposition: form-data; name="thumb"; filename="thumb.jpg"
-```
-
-We key off of the `name` attribute in the `Content-Disposition` value.
-
-## Pass string arguments to command
-
-To pass simple string arguments to a command, use the `string` parameter source.
-The following example will pass two static string parameters ("-e 123123") to the
-`execute-command` before appending the `pusher.email` value from the payload:
-
-```json
-[
- {
- "id": "webhook",
- "execute-command": "/home/adnan/redeploy-go-webhook.sh",
- "command-working-directory": "/home/adnan/go",
- "pass-arguments-to-command":
- [
- {
- "source": "string",
- "name": "-e"
- },
- {
- "source": "string",
- "name": "123123"
- },
- {
- "source": "payload",
- "name": "pusher.email"
- }
- ]
- }
-]
-```
-
-## Receive Synology DSM notifications
-
-It's possible to securely receive Synology push notifications via webhooks.
-Webhooks feature introduced in DSM 7.x seems to be incomplete & broken, but you can use Synology SMS notification service to push webhooks. To configure SMS notifications on DSM follow instructions found here: https://github.com/ryancurrah/synology-notifications this will allow you to set up everything needed for webhook to accept any and all notifications sent by Synology. During setup an 'api_key' is specified - you can generate your own 32-char string and use it as an authentication mechanism to secure your webhook. Additionally, you can specify what notifications to receive via this method by going and selecting the "SMS" checkboxes under topics of interes in DSM: Control Panel -> Notification -> Rules
-
-```json
-[
- {
- "id": "synology",
- "execute-command": "do-something.sh",
- "command-working-directory": "/opt/webhook-linux-amd64/synology",
- "response-message": "Request accepted",
- "pass-arguments-to-command":
- [
- {
- "source": "payload",
- "name": "message"
- }
- ],
- "trigger-rule":
- {
- "match":
- {
- "type": "value",
- "value": "PUT_YOUR_API_KEY_HERE",
- "parameter":
- {
- "source": "header",
- "name": "api_key"
- }
- }
- }
- }
-]
-```
diff --git a/docs/Hook-Rules.md b/docs/Hook-Rules.md
index 84914ca..52947aa 100644
--- a/docs/Hook-Rules.md
+++ b/docs/Hook-Rules.md
@@ -1,20 +1,5 @@
# Hook rules
-### Table of Contents
-
-* [And](#and)
-* [Or](#or)
-* [Not](#not)
-* [Multi-level](#multi-level)
-* [Match](#match)
- * [Match value](#match-value)
- * [Match regex](#match-regex)
- * [Match payload-hmac-sha1](#match-payload-hmac-sha1)
- * [Match payload-hmac-sha256](#match-payload-hmac-sha256)
- * [Match payload-hmac-sha512](#match-payload-hmac-sha512)
- * [Match Whitelisted IP range](#match-whitelisted-ip-range)
- * [Match scalr-signature](#match-scalr-signature)
-
## And
*And rule* will evaluate to _true_, if and only if all of the sub rules evaluate to _true_.
```json
@@ -110,7 +95,7 @@
"source": "header",
"name": "X-Hub-Signature"
},
- "type": "payload-hmac-sha1",
+ "type": "payload-hash-sha1",
"secret": "mysecret"
}
},
@@ -150,7 +135,9 @@
*Please note:* Due to technical reasons, _number_ and _boolean_ values in the _match rule_ must be wrapped around with a pair of quotes.
-### Match value
+There are three different match rules:
+
+### 1. Match value
```json
{
"match":
@@ -166,7 +153,7 @@
}
```
-### Match regex
+### 2. Match regex
For the regex syntax, check out
```json
{
@@ -183,13 +170,12 @@ For the regex syntax, check out
}
```
-### Match payload-hmac-sha1
-Validate the HMAC of the payload using the SHA1 hash and the given *secret*.
+### 3. Match payload-hash-sha1
```json
{
"match":
{
- "type": "payload-hmac-sha1",
+ "type": "payload-hash-sha1",
"secret": "yoursecret",
"parameter":
{
@@ -200,62 +186,7 @@ Validate the HMAC of the payload using the SHA1 hash and the given *secret*.
}
```
-Note that if multiple signatures were passed via a comma separated string, each
-will be tried unless a match is found. For example:
-
-```
-X-Hub-Signature: sha1=the-first-signature,sha1=the-second-signature
-```
-
-### Match payload-hmac-sha256
-Validate the HMAC of the payload using the SHA256 hash and the given *secret*.
-```json
-{
- "match":
- {
- "type": "payload-hmac-sha256",
- "secret": "yoursecret",
- "parameter":
- {
- "source": "header",
- "name": "X-Signature"
- }
- }
-}
-```
-
-Note that if multiple signatures were passed via a comma separated string, each
-will be tried unless a match is found. For example:
-
-```
-X-Hub-Signature: sha256=the-first-signature,sha256=the-second-signature
-```
-
-### Match payload-hmac-sha512
-Validate the HMAC of the payload using the SHA512 hash and the given *secret*.
-```json
-{
- "match":
- {
- "type": "payload-hmac-sha512",
- "secret": "yoursecret",
- "parameter":
- {
- "source": "header",
- "name": "X-Signature"
- }
- }
-}
-```
-
-Note that if multiple signatures were passed via a comma separated string, each
-will be tried unless a match is found. For example:
-
-```
-X-Hub-Signature: sha512=the-first-signature,sha512=the-second-signature
-```
-
-### Match Whitelisted IP range
+### 4. Match Whitelisted IP range
The IP can be IPv4- or IPv6-formatted, using [CIDR notation](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_blocks). To match a single IP address only, use `/32`.
@@ -269,12 +200,10 @@ The IP can be IPv4- or IPv6-formatted, using [CIDR notation](https://en.wikipedi
}
```
-Note this does not work if webhook is running behind a reverse proxy, as the "client IP" will either not be available at all (if webhook is using a Unix socket or named pipe) or it will be the address of the _proxy_, not of the real client. You will probably need to enforce client IP restrictions in the reverse proxy itself, before forwarding the requests to webhook.
-
-### Match scalr-signature
+### 5. Match scalr-signature
The trigger rule checks the scalr signature and also checks that the request was signed less than 5 minutes before it was received.
-A unique signing key is generated for each webhook endpoint URL you register in Scalr.
+A unqiue signing key is generated for each webhook endpoint URL you register in Scalr.
Given the time check make sure that NTP is enabled on both your Scalr and webhook server to prevent any issues
```json
@@ -285,4 +214,4 @@ Given the time check make sure that NTP is enabled on both your Scalr and webhoo
"secret": "Scalr-provided signing key"
}
}
-```
+```
\ No newline at end of file
diff --git a/docs/Referencing-Request-Values.md b/docs/Referencing-Request-Values.md
index ba35363..6e05566 100644
--- a/docs/Referencing-Request-Values.md
+++ b/docs/Referencing-Request-Values.md
@@ -1,5 +1,5 @@
# Referencing request values
-There are four types of request values:
+There are three types of request values:
1. HTTP Request Header values
@@ -19,23 +19,7 @@ There are four types of request values:
}
```
-3. HTTP Request parameters
-
- ```json
- {
- "source": "request",
- "name": "method"
- }
- ```
-
- ```json
- {
- "source": "request",
- "name": "remote-addr"
- }
- ```
-
-4. Payload (JSON or form-value encoded)
+3. Payload (JSON or form-value encoded)
```json
{
"source": "payload",
@@ -73,34 +57,6 @@ There are four types of request values:
If the payload contains a key with the specified name "commits.0.commit.id", then the value of that key has priority over the dot-notation referencing.
-4. XML Payload
-
- Referencing XML payload parameters is much like the JSON examples above, but XML is more complex.
- Element attributes are prefixed by a hyphen (`-`).
- Element values are prefixed by a pound (`#`).
-
- Take the following XML payload:
-
- ```xml
-
-
-
-
-
-
- Hello!!
-
-
- ```
-
- To access a given `user` element, you must treat them as an array.
- So `app.users.user.0.name` yields `Jeff`.
-
- Since there's only one `message` tag, it's not treated as an array.
- So `app.messages.message.id` yields `1`.
-
- To access the text within the `message` tag, you would use: `app.messages.message.#text`.
-
If you are referencing values for environment, you can use `envname` property to set the name of the environment variable like so
```json
{
@@ -131,4 +87,4 @@ and for query variables you can use
{
"source": "entire-query"
}
-```
+```
\ No newline at end of file
diff --git a/docs/Systemd-Activation.md b/docs/Systemd-Activation.md
deleted file mode 100644
index a136fac..0000000
--- a/docs/Systemd-Activation.md
+++ /dev/null
@@ -1,61 +0,0 @@
-# Using systemd socket activation
-
-_New in v2.8.2_
-
-On platforms that use [systemd](https://systemd.io), [webhook][w]
-supports the _socket activation_ mechanism. In this mode, systemd itself is responsible for managing the listening socket, and it launches [webhook][w] the first time it receives a request on the socket. This has a number of advantages over the standard mode:
-
-- [webhook][w] can run as a normal user while still being able to use a port number like 80 or 443 that would normally require root privilege
-- if the [webhook][w] process dies and is restarted, pending connections are not dropped - they just keep waiting until the restarted [webhook][w] is ready
-
-No special configuration is necessary to tell [webhook][w] that socket activation is being used - socket activation sets specific environment variables when launching the activated service, if [webhook][w] detects these variables it will ignore the `-port` and `-socket` options and simply use the systemd-provided socket instead of opening its own.
-
-## Configuration
-To run [webhook][w] with socket activation you need to create _two_ separate unit files in your systemd configuration directory (typically `/etc/systemd/system`), one for the socket and one for the service. They must have matching names; in this example we use `webhook.socket` and `webhook.service`. At their simplest, these files should look like:
-
-**webhook.socket**
-```
-[Unit]
-Description=Webhook server socket
-
-[Socket]
-# Listen on all network interfaces, port 9000
-ListenStream=9000
-
-# Alternatives:
-
-## Listen on one specific interface only
-# ListenStream=10.0.0.1:9000
-# FreeBind=true
-
-## Listen on a Unix domain socket
-# ListenStream=/tmp/webhook.sock
-
-[Install]
-WantedBy=multi-user.target
-```
-
-**webhook.service**
-```
-[Unit]
-Description=Webhook server
-
-[Service]
-Type=exec
-ExecStart=webhook -nopanic -hooks /etc/webhook/hooks.yml
-
-# Which user should the webhooks run as?
-User=nobody
-Group=nogroup
-```
-
-You should enable and start the _socket_, but it is not necessary to enable the _service_ - this will be started automatically when the socket receives its first request.
-
-```sh
-sudo systemctl enable webhook.socket
-sudo systemctl start webhook.socket
-```
-
-Systemd unit files support many other options, see the [systemd.socket](https://www.freedesktop.org/software/systemd/man/latest/systemd.socket.html) and [systemd.service](https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html) manual pages for full details.
-
-[w]: https://github.com/adnanh/webhook
diff --git a/docs/Templates.md b/docs/Templates.md
index 35f10a0..2adeab3 100644
--- a/docs/Templates.md
+++ b/docs/Templates.md
@@ -1,16 +1,15 @@
# Templates in Webhook
-[`webhook`][w] can parse a hooks configuration file as a Go template when given the `-template` [CLI parameter](Webhook-Parameters.md).
+[`webhook`][w] can parse the `hooks.json` input file as a Go template when given the `-template` [CLI parameter](Webhook-Parameters.md).
-In additional to the [built-in Go template functions and features][tt], `webhook` provides a `getenv` template function for inserting environment variables into a templated configuration file.
+In additional to the [built-in Go template functions and features][tt], `webhook` provides a `getenv` template function for inserting environment variables into a `hooks.json` file.
## Example Usage
-In the example JSON template file below (YAML is also supported), the `payload-hmac-sha1` matching rule looks up the HMAC secret from the environment using the `getenv` template function.
+In the example `hooks.json` file below, the `payload-hash-sha1` matching rule looks up the secret hash from the environment using the `getenv` template function.
Additionally, the result is piped through the built-in Go template function `js` to ensure that the result is a well-formed Javascript/JSON string.
```
-[
{
"id": "webhook",
"execute-command": "/home/adnan/redeploy-go-webhook.sh",
@@ -45,7 +44,7 @@ Additionally, the result is piped through the built-in Go template function `js`
{
"match":
{
- "type": "payload-hmac-sha1",
+ "type": "payload-hash-sha1",
"secret": "{{ getenv "XXXTEST_SECRET" | js }}",
"parameter":
{
diff --git a/docs/Webhook-Parameters.md b/docs/Webhook-Parameters.md
index 31e6d61..ab51bea 100644
--- a/docs/Webhook-Parameters.md
+++ b/docs/Webhook-Parameters.md
@@ -3,64 +3,36 @@
Usage of webhook:
-cert string
path to the HTTPS certificate pem file (default "cert.pem")
- -cipher-suites string
- comma-separated list of supported TLS cipher suites
- -debug
- show debug output
-header value
response header to return, specified in format name=value, use multiple times to set multiple headers
-hooks value
path to the json file containing defined hooks the webhook should serve, use multiple times to load from different files
-hotreload
watch hooks file for changes and reload them automatically
- -http-methods string
- set default allowed HTTP methods (ie. "POST"); separate methods with comma
-ip string
ip the webhook should serve hooks on (default "0.0.0.0")
-key string
path to the HTTPS certificate private key pem file (default "key.pem")
- -list-cipher-suites
- list available TLS cipher suites
- -logfile string
- send log output to a file; implicitly enables verbose logging
- -max-multipart-mem int
- maximum memory in bytes for parsing multipart form data before disk caching (default 1048576)
-nopanic
do not panic if hooks cannot be loaded when webhook is not running in verbose mode
- -pidfile string
- create PID file at the given path
-port int
port the webhook should serve hooks on (default 9000)
-secure
use HTTPS instead of HTTP
- -setgid int
- set group ID after opening listening port; must be used with setuid
- -setuid int
- set user ID after opening listening port; must be used with setgid
- -socket string
- path to a Unix socket (e.g. /tmp/webhook.sock) or Windows named pipe (e.g. \\.\pipe\webhook) to use instead of listening on an ip and port; if specified, the ip and port options are ignored
-template
parse hooks file as a Go template
- -tls-min-version string
- minimum TLS version (1.0, 1.1, 1.2, 1.3) (default "1.2")
-urlprefix string
url prefix to use for served hooks (protocol://yourserver:port/PREFIX/:hook-id) (default "hooks")
-verbose
show verbose output
-version
display webhook version and quit
- -x-request-id
- use X-Request-Id header, if present, as request ID
- -x-request-id-limit int
- truncate X-Request-Id header to limit; default no limit
```
Use any of the above specified flags to override their default behavior.
# Live reloading hooks
-If you are running an OS that supports the HUP or USR1 signal, you can use it to trigger hooks reload from hooks file, without restarting the webhook instance.
+If you are running an OS that supports USR1 signal, you can use it to trigger hooks reload from hooks file, without restarting the webhook instance.
```bash
kill -USR1 webhookpid
-
-kill -HUP webhookpid
-```
+```
\ No newline at end of file
diff --git a/droppriv_nope.go b/droppriv_nope.go
deleted file mode 100644
index 696f5ab..0000000
--- a/droppriv_nope.go
+++ /dev/null
@@ -1,12 +0,0 @@
-// +build windows
-
-package main
-
-import (
- "errors"
- "runtime"
-)
-
-func dropPrivileges(uid, gid int) error {
- return errors.New("setuid and setgid not supported on " + runtime.GOOS)
-}
diff --git a/droppriv_unix.go b/droppriv_unix.go
deleted file mode 100644
index 146de7e..0000000
--- a/droppriv_unix.go
+++ /dev/null
@@ -1,26 +0,0 @@
-// +build linux !windows
-
-package main
-
-import (
- "syscall"
-)
-
-func dropPrivileges(uid, gid int) error {
- err := syscall.Setgroups([]int{})
- if err != nil {
- return err
- }
-
- err = syscall.Setgid(gid)
- if err != nil {
- return err
- }
-
- err = syscall.Setuid(uid)
- if err != nil {
- return err
- }
-
- return nil
-}
diff --git a/go.mod b/go.mod
deleted file mode 100644
index 2e17076..0000000
--- a/go.mod
+++ /dev/null
@@ -1,25 +0,0 @@
-module github.com/adnanh/webhook
-
-go 1.21
-
-toolchain go1.22.0
-
-require (
- github.com/Microsoft/go-winio v0.6.2
- github.com/clbanning/mxj/v2 v2.7.0
- github.com/coreos/go-systemd/v22 v22.5.0
- github.com/dustin/go-humanize v1.0.1
- github.com/fsnotify/fsnotify v1.7.0
- github.com/ghodss/yaml v1.0.0
- github.com/go-chi/chi/v5 v5.0.12
- github.com/gofrs/uuid/v5 v5.0.0
- github.com/gorilla/mux v1.8.1
- golang.org/x/sys v0.18.0
-)
-
-require (
- github.com/google/go-cmp v0.6.0 // indirect
- github.com/kr/pretty v0.1.0 // indirect
- gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
- gopkg.in/yaml.v2 v2.4.0 // indirect
-)
diff --git a/go.sum b/go.sum
deleted file mode 100644
index 8f0fb80..0000000
--- a/go.sum
+++ /dev/null
@@ -1,33 +0,0 @@
-github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
-github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
-github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
-github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
-github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
-github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
-github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
-github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
-github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
-github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
-github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
-github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
-github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
-github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
-github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
-github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
-github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
-github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
-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=
-golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
-golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
-gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
diff --git a/internal/hook/hook.go b/hook/hook.go
similarity index 59%
rename from internal/hook/hook.go
rename to hook/hook.go
index 394dd79..98fb975 100644
--- a/internal/hook/hook.go
+++ b/hook/hook.go
@@ -5,20 +5,17 @@ import (
"crypto/hmac"
"crypto/sha1"
"crypto/sha256"
- "crypto/sha512"
- "crypto/subtle"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
- "hash"
+ "io/ioutil"
"log"
"math"
"net"
"net/textproto"
"os"
- "path"
"reflect"
"regexp"
"strconv"
@@ -31,16 +28,14 @@ import (
// Constants used to specify the parameter source
const (
- SourceHeader string = "header"
- SourceQuery string = "url"
- SourceQueryAlias string = "query"
- SourcePayload string = "payload"
- SourceRawRequestBody string = "raw-request-body"
- SourceRequest string = "request"
- SourceString string = "string"
- SourceEntirePayload string = "entire-payload"
- SourceEntireQuery string = "entire-query"
- SourceEntireHeaders string = "entire-headers"
+ SourceHeader string = "header"
+ SourceQuery string = "url"
+ SourceQueryAlias string = "query"
+ SourcePayload string = "payload"
+ SourceString string = "string"
+ SourceEntirePayload string = "entire-payload"
+ SourceEntireQuery string = "entire-query"
+ SourceEntireHeaders string = "entire-headers"
)
const (
@@ -49,61 +44,16 @@ const (
EnvNamespace string = "HOOK_"
)
-// ParameterNodeError describes an error walking a parameter node.
-type ParameterNodeError struct {
- key string
-}
-
-func (e *ParameterNodeError) Error() string {
- if e == nil {
- return ""
- }
- return fmt.Sprintf("parameter node not found: %s", e.key)
-}
-
-// IsParameterNodeError returns whether err is of type ParameterNodeError.
-func IsParameterNodeError(err error) bool {
- switch err.(type) {
- case *ParameterNodeError:
- return true
- default:
- return false
- }
-}
-
// SignatureError describes an invalid payload signature passed to Hook.
type SignatureError struct {
- Signature string
- Signatures []string
-
- emptyPayload bool
+ Signature string
}
func (e *SignatureError) Error() string {
if e == nil {
return ""
}
-
- var empty string
- if e.emptyPayload {
- empty = " on empty payload"
- }
-
- if e.Signatures != nil {
- return fmt.Sprintf("invalid payload signatures %s%s", e.Signatures, empty)
- }
-
- return fmt.Sprintf("invalid payload signature %s%s", e.Signature, empty)
-}
-
-// IsSignatureError returns whether err is of type SignatureError.
-func IsSignatureError(err error) bool {
- switch err.(type) {
- case *SignatureError:
- return true
- default:
- return false
- }
+ return fmt.Sprintf("invalid payload signature %s", e.Signature)
}
// ArgumentError describes an invalid argument passed to Hook.
@@ -142,123 +92,69 @@ func (e *ParseError) Error() string {
return e.Err.Error()
}
-// ExtractCommaSeparatedValues will extract the values matching the key.
-func ExtractCommaSeparatedValues(source, prefix string) []string {
- parts := strings.Split(source, ",")
- values := make([]string, 0)
- for _, part := range parts {
- if strings.HasPrefix(part, prefix) {
- values = append(values, strings.TrimPrefix(part, prefix))
- }
+// CheckPayloadSignature calculates and verifies SHA1 signature of the given payload
+func CheckPayloadSignature(payload []byte, secret string, signature string) (string, error) {
+ if secret == "" {
+ return "", errors.New("signature validation secret can not be empty")
}
- return values
-}
+ signature = strings.TrimPrefix(signature, "sha1=")
-// ExtractSignatures will extract all the signatures from the source.
-func ExtractSignatures(source, prefix string) []string {
- // If there are multiple possible matches, let the comma separated extractor
- // do it's work.
- if strings.Contains(source, ",") {
- return ExtractCommaSeparatedValues(source, prefix)
- }
-
- // There were no commas, so just trim the prefix (if it even exists) and
- // pass it back.
- return []string{
- strings.TrimPrefix(source, prefix),
- }
-}
-
-// ValidateMAC will verify that the expected mac for the given hash will match
-// the one provided.
-func ValidateMAC(payload []byte, mac hash.Hash, signatures []string) (string, error) {
- // Write the payload to the provided hash.
+ mac := hmac.New(sha1.New, []byte(secret))
_, err := mac.Write(payload)
if err != nil {
return "", err
}
+ expectedMAC := hex.EncodeToString(mac.Sum(nil))
- actualMAC := hex.EncodeToString(mac.Sum(nil))
-
- for _, signature := range signatures {
- if hmac.Equal([]byte(signature), []byte(actualMAC)) {
- return actualMAC, err
- }
+ if !hmac.Equal([]byte(signature), []byte(expectedMAC)) {
+ return expectedMAC, &SignatureError{signature}
}
-
- e := &SignatureError{Signatures: signatures}
- if len(payload) == 0 {
- e.emptyPayload = true
- }
-
- return actualMAC, e
-}
-
-// CheckPayloadSignature calculates and verifies SHA1 signature of the given payload
-func CheckPayloadSignature(payload []byte, secret, signature string) (string, error) {
- if secret == "" {
- return "", errors.New("signature validation secret can not be empty")
- }
-
- // Extract the signatures.
- signatures := ExtractSignatures(signature, "sha1=")
-
- // Validate the MAC.
- return ValidateMAC(payload, hmac.New(sha1.New, []byte(secret)), signatures)
+ return expectedMAC, err
}
// CheckPayloadSignature256 calculates and verifies SHA256 signature of the given payload
-func CheckPayloadSignature256(payload []byte, secret, signature string) (string, error) {
+func CheckPayloadSignature256(payload []byte, secret string, signature string) (string, error) {
if secret == "" {
return "", errors.New("signature validation secret can not be empty")
}
- // Extract the signatures.
- signatures := ExtractSignatures(signature, "sha256=")
+ signature = strings.TrimPrefix(signature, "sha256=")
- // Validate the MAC.
- return ValidateMAC(payload, hmac.New(sha256.New, []byte(secret)), signatures)
+ mac := hmac.New(sha256.New, []byte(secret))
+ _, err := mac.Write(payload)
+ if err != nil {
+ return "", err
+ }
+ expectedMAC := hex.EncodeToString(mac.Sum(nil))
+
+ if !hmac.Equal([]byte(signature), []byte(expectedMAC)) {
+ return expectedMAC, &SignatureError{signature}
+ }
+ return expectedMAC, err
}
-// CheckPayloadSignature512 calculates and verifies SHA512 signature of the given payload
-func CheckPayloadSignature512(payload []byte, secret, signature string) (string, error) {
- if secret == "" {
- return "", errors.New("signature validation secret can not be empty")
- }
-
- // Extract the signatures.
- signatures := ExtractSignatures(signature, "sha512=")
-
- // Validate the MAC.
- return ValidateMAC(payload, hmac.New(sha512.New, []byte(secret)), signatures)
-}
-
-func CheckScalrSignature(r *Request, signingKey string, checkDate bool) (bool, error) {
- if r.Headers == nil {
- return false, nil
- }
-
+func CheckScalrSignature(headers map[string]interface{}, body []byte, signingKey string, checkDate bool) (bool, error) {
// Check for the signature and date headers
- if _, ok := r.Headers["X-Signature"]; !ok {
+ if _, ok := headers["X-Signature"]; !ok {
return false, nil
}
- if _, ok := r.Headers["Date"]; !ok {
+ if _, ok := headers["Date"]; !ok {
return false, nil
}
if signingKey == "" {
return false, errors.New("signature validation signing key can not be empty")
}
- providedSignature := r.Headers["X-Signature"].(string)
- dateHeader := r.Headers["Date"].(string)
+ providedSignature := headers["X-Signature"].(string)
+ dateHeader := headers["Date"].(string)
mac := hmac.New(sha1.New, []byte(signingKey))
- mac.Write(r.Body)
+ mac.Write(body)
mac.Write([]byte(dateHeader))
expectedSignature := hex.EncodeToString(mac.Sum(nil))
if !hmac.Equal([]byte(providedSignature), []byte(expectedSignature)) {
- return false, &SignatureError{Signature: providedSignature}
+ return false, &SignatureError{providedSignature}
}
if !checkDate {
@@ -273,14 +169,14 @@ func CheckScalrSignature(r *Request, signingKey string, checkDate bool) (bool, e
delta := math.Abs(now.Sub(date).Seconds())
if delta > 300 {
- return false, &SignatureError{Signature: "outdated"}
+ return false, &SignatureError{"outdated"}
}
return true, nil
}
// CheckIPWhitelist makes sure the provided remote address (of the form IP:port) falls within the provided IP range
// (in CIDR form or a single IP address).
-func CheckIPWhitelist(remoteAddr, ipRange string) (bool, error) {
+func CheckIPWhitelist(remoteAddr string, ipRange string) (bool, error) {
// Extract IP address from remote address.
// IPv6 addresses will likely be surrounded by [].
@@ -319,7 +215,7 @@ func CheckIPWhitelist(remoteAddr, ipRange string) (bool, error) {
// ReplaceParameter replaces parameter value with the passed value in the passed map
// (please note you should pass pointer to the map, because we're modifying it)
// based on the passed string
-func ReplaceParameter(s string, params, value interface{}) bool {
+func ReplaceParameter(s string, params interface{}, value interface{}) bool {
if params == nil {
return false
}
@@ -355,9 +251,9 @@ func ReplaceParameter(s string, params, value interface{}) bool {
}
// GetParameter extracts interface{} value based on the passed string
-func GetParameter(s string, params interface{}) (interface{}, error) {
+func GetParameter(s string, params interface{}) (interface{}, bool) {
if params == nil {
- return nil, errors.New("no parameters")
+ return nil, false
}
paramsValue := reflect.ValueOf(params)
@@ -371,7 +267,7 @@ func GetParameter(s string, params interface{}) (interface{}, error) {
index, err := strconv.ParseUint(p[0], 10, 64)
if err != nil || paramsValueSliceLength <= int(index) {
- return nil, &ParameterNodeError{s}
+ return nil, false
}
return GetParameter(p[1], params.([]interface{})[index])
@@ -380,18 +276,18 @@ func GetParameter(s string, params interface{}) (interface{}, error) {
index, err := strconv.ParseUint(s, 10, 64)
if err != nil || paramsValueSliceLength <= int(index) {
- return nil, &ParameterNodeError{s}
+ return nil, false
}
- return params.([]interface{})[index], nil
+ return params.([]interface{})[index], true
}
- return nil, &ParameterNodeError{s}
+ return nil, false
case reflect.Map:
// Check for raw key
if v, ok := params.(map[string]interface{})[s]; ok {
- return v, nil
+ return v, true
}
// Checked for dotted references
@@ -401,34 +297,19 @@ func GetParameter(s string, params interface{}) (interface{}, error) {
return GetParameter(p[1], pValue)
}
- return pValue, nil
+ return pValue, true
}
}
- return nil, &ParameterNodeError{s}
+ return nil, false
}
-// ExtractParameterAsString extracts value from interface{} as string based on
-// the passed string. Complex data types are rendered as JSON instead of the Go
-// Stringer format.
-func ExtractParameterAsString(s string, params interface{}) (string, error) {
- pValue, err := GetParameter(s, params)
- if err != nil {
- return "", err
- }
-
- switch v := reflect.ValueOf(pValue); v.Kind() {
- case reflect.Array, reflect.Map, reflect.Slice:
- r, err := json.Marshal(pValue)
- if err != nil {
- return "", err
- }
-
- return string(r), nil
-
- default:
- return fmt.Sprintf("%v", pValue), nil
+// ExtractParameterAsString extracts value from interface{} as string based on the passed string
+func ExtractParameterAsString(s string, params interface{}) (string, bool) {
+ if pValue, ok := GetParameter(s, params); ok {
+ return fmt.Sprintf("%v", pValue), true
}
+ return "", false
}
// Argument type specifies the parameter key name and the source it should
@@ -442,71 +323,48 @@ type Argument struct {
// Get Argument method returns the value for the Argument's key name
// based on the Argument's source
-func (ha *Argument) Get(r *Request) (string, error) {
+func (ha *Argument) Get(headers, query, payload *map[string]interface{}) (string, bool) {
var source *map[string]interface{}
key := ha.Name
switch ha.Source {
case SourceHeader:
- source = &r.Headers
+ source = headers
key = textproto.CanonicalMIMEHeaderKey(ha.Name)
-
case SourceQuery, SourceQueryAlias:
- source = &r.Query
-
+ source = query
case SourcePayload:
- source = &r.Payload
-
+ source = payload
case SourceString:
- return ha.Name, nil
-
- case SourceRawRequestBody:
- return string(r.Body), nil
-
- case SourceRequest:
- if r == nil || r.RawRequest == nil {
- return "", errors.New("request is nil")
- }
-
- switch strings.ToLower(ha.Name) {
- case "remote-addr":
- return r.RawRequest.RemoteAddr, nil
- case "method":
- return r.RawRequest.Method, nil
- default:
- return "", fmt.Errorf("unsupported request key: %q", ha.Name)
- }
-
+ return ha.Name, true
case SourceEntirePayload:
- res, err := json.Marshal(&r.Payload)
+ r, err := json.Marshal(payload)
if err != nil {
- return "", err
+ return "", false
}
- return string(res), nil
-
+ return string(r), true
case SourceEntireHeaders:
- res, err := json.Marshal(&r.Headers)
+ r, err := json.Marshal(headers)
if err != nil {
- return "", err
+ return "", false
}
- return string(res), nil
-
+ return string(r), true
case SourceEntireQuery:
- res, err := json.Marshal(&r.Query)
+ r, err := json.Marshal(query)
if err != nil {
- return "", err
+ return "", false
}
- return string(res), nil
+ return string(r), true
}
if source != nil {
return ExtractParameterAsString(key, *source)
}
- return "", errors.New("no source for value retrieval")
+ return "", false
}
// Header is a structure containing header name and it's value
@@ -577,22 +435,17 @@ type Hook struct {
JSONStringParameters []Argument `json:"parse-parameters-as-json,omitempty"`
TriggerRule *Rules `json:"trigger-rule,omitempty"`
TriggerRuleMismatchHttpResponseCode int `json:"trigger-rule-mismatch-http-response-code,omitempty"`
- TriggerSignatureSoftFailures bool `json:"trigger-signature-soft-failures,omitempty"`
IncomingPayloadContentType string `json:"incoming-payload-content-type,omitempty"`
SuccessHttpResponseCode int `json:"success-http-response-code,omitempty"`
- HTTPMethods []string `json:"http-methods"`
}
// ParseJSONParameters decodes specified arguments to JSON objects and replaces the
// string with the newly created object
-func (h *Hook) ParseJSONParameters(r *Request) []error {
+func (h *Hook) ParseJSONParameters(headers, query, payload *map[string]interface{}) []error {
errors := make([]error, 0)
for i := range h.JSONStringParameters {
- arg, err := h.JSONStringParameters[i].Get(r)
- if err != nil {
- errors = append(errors, &ArgumentError{h.JSONStringParameters[i]})
- } else {
+ if arg, ok := h.JSONStringParameters[i].Get(headers, query, payload); ok {
var newArg map[string]interface{}
decoder := json.NewDecoder(strings.NewReader(string(arg)))
@@ -608,11 +461,11 @@ func (h *Hook) ParseJSONParameters(r *Request) []error {
switch h.JSONStringParameters[i].Source {
case SourceHeader:
- source = &r.Headers
+ source = headers
case SourcePayload:
- source = &r.Payload
+ source = payload
case SourceQuery, SourceQueryAlias:
- source = &r.Query
+ source = query
}
if source != nil {
@@ -626,6 +479,8 @@ func (h *Hook) ParseJSONParameters(r *Request) []error {
} else {
errors = append(errors, &SourceError{h.JSONStringParameters[i]})
}
+ } else {
+ errors = append(errors, &ArgumentError{h.JSONStringParameters[i]})
}
}
@@ -638,21 +493,19 @@ func (h *Hook) ParseJSONParameters(r *Request) []error {
// ExtractCommandArguments creates a list of arguments, based on the
// PassArgumentsToCommand property that is ready to be used with exec.Command()
-func (h *Hook) ExtractCommandArguments(r *Request) ([]string, []error) {
+func (h *Hook) ExtractCommandArguments(headers, query, payload *map[string]interface{}) ([]string, []error) {
args := make([]string, 0)
errors := make([]error, 0)
args = append(args, h.ExecuteCommand)
for i := range h.PassArgumentsToCommand {
- arg, err := h.PassArgumentsToCommand[i].Get(r)
- if err != nil {
+ if arg, ok := h.PassArgumentsToCommand[i].Get(headers, query, payload); ok {
+ args = append(args, arg)
+ } else {
args = append(args, "")
errors = append(errors, &ArgumentError{h.PassArgumentsToCommand[i]})
- continue
}
-
- args = append(args, arg)
}
if len(errors) > 0 {
@@ -665,22 +518,20 @@ func (h *Hook) ExtractCommandArguments(r *Request) ([]string, []error) {
// ExtractCommandArgumentsForEnv creates a list of arguments in key=value
// format, based on the PassEnvironmentToCommand property that is ready to be used
// with exec.Command().
-func (h *Hook) ExtractCommandArgumentsForEnv(r *Request) ([]string, []error) {
+func (h *Hook) ExtractCommandArgumentsForEnv(headers, query, payload *map[string]interface{}) ([]string, []error) {
args := make([]string, 0)
errors := make([]error, 0)
for i := range h.PassEnvironmentToCommand {
- arg, err := h.PassEnvironmentToCommand[i].Get(r)
- if err != nil {
- errors = append(errors, &ArgumentError{h.PassEnvironmentToCommand[i]})
- continue
- }
-
- if h.PassEnvironmentToCommand[i].EnvName != "" {
- // first try to use the EnvName if specified
- args = append(args, h.PassEnvironmentToCommand[i].EnvName+"="+arg)
+ if arg, ok := h.PassEnvironmentToCommand[i].Get(headers, query, payload); ok {
+ if h.PassEnvironmentToCommand[i].EnvName != "" {
+ // first try to use the EnvName if specified
+ args = append(args, h.PassEnvironmentToCommand[i].EnvName+"="+arg)
+ } else {
+ // then fallback on the name
+ args = append(args, EnvNamespace+h.PassEnvironmentToCommand[i].Name+"="+arg)
+ }
} else {
- // then fallback on the name
- args = append(args, EnvNamespace+h.PassEnvironmentToCommand[i].Name+"="+arg)
+ errors = append(errors, &ArgumentError{h.PassEnvironmentToCommand[i]})
}
}
@@ -701,34 +552,34 @@ type FileParameter struct {
// ExtractCommandArgumentsForFile creates a list of arguments in key=value
// format, based on the PassFileToCommand property that is ready to be used
// with exec.Command().
-func (h *Hook) ExtractCommandArgumentsForFile(r *Request) ([]FileParameter, []error) {
+func (h *Hook) ExtractCommandArgumentsForFile(headers, query, payload *map[string]interface{}) ([]FileParameter, []error) {
args := make([]FileParameter, 0)
errors := make([]error, 0)
for i := range h.PassFileToCommand {
- arg, err := h.PassFileToCommand[i].Get(r)
- if err != nil {
- errors = append(errors, &ArgumentError{h.PassFileToCommand[i]})
- continue
- }
+ if arg, ok := h.PassFileToCommand[i].Get(headers, query, payload); ok {
- if h.PassFileToCommand[i].EnvName == "" {
- // if no environment-variable name is set, fall-back on the name
- log.Printf("no ENVVAR name specified, falling back to [%s]", EnvNamespace+strings.ToUpper(h.PassFileToCommand[i].Name))
- h.PassFileToCommand[i].EnvName = EnvNamespace + strings.ToUpper(h.PassFileToCommand[i].Name)
- }
-
- var fileContent []byte
- if h.PassFileToCommand[i].Base64Decode {
- dec, err := base64.StdEncoding.DecodeString(arg)
- if err != nil {
- log.Printf("error decoding string [%s]", err)
+ if h.PassFileToCommand[i].EnvName == "" {
+ // if no environment-variable name is set, fall-back on the name
+ log.Printf("no ENVVAR name specified, falling back to [%s]", EnvNamespace+strings.ToUpper(h.PassFileToCommand[i].Name))
+ h.PassFileToCommand[i].EnvName = EnvNamespace + strings.ToUpper(h.PassFileToCommand[i].Name)
}
- fileContent = []byte(dec)
- } else {
- fileContent = []byte(arg)
- }
- args = append(args, FileParameter{EnvName: h.PassFileToCommand[i].EnvName, Data: fileContent})
+ var fileContent []byte
+ if h.PassFileToCommand[i].Base64Decode {
+ dec, err := base64.StdEncoding.DecodeString(arg)
+ if err != nil {
+ log.Printf("error decoding string [%s]", err)
+ }
+ fileContent = []byte(dec)
+ } else {
+ fileContent = []byte(arg)
+ }
+
+ args = append(args, FileParameter{EnvName: h.PassFileToCommand[i].EnvName, Data: fileContent})
+
+ } else {
+ errors = append(errors, &ArgumentError{h.PassFileToCommand[i]})
+ }
}
if len(errors) > 0 {
@@ -750,18 +601,14 @@ func (h *Hooks) LoadFromFile(path string, asTemplate bool) error {
}
// parse hook file for hooks
- file, e := os.ReadFile(path)
+ file, e := ioutil.ReadFile(path)
if e != nil {
return e
}
if asTemplate {
- funcMap := template.FuncMap{
- "cat": cat,
- "credential": credential,
- "getenv": getenv,
- }
+ funcMap := template.FuncMap{"getenv": getenv}
tmpl, err := template.New("hooks").Funcs(funcMap).Parse(string(file))
if err != nil {
@@ -778,7 +625,8 @@ func (h *Hooks) LoadFromFile(path string, asTemplate bool) error {
file = buf.Bytes()
}
- return yaml.Unmarshal(file, h)
+ e = yaml.Unmarshal(file, h)
+ return e
}
// Append appends hooks unless the new hooks contain a hook with an ID that already exists
@@ -816,16 +664,16 @@ type Rules struct {
// Evaluate finds the first rule property that is not nil and returns the value
// it evaluates to
-func (r Rules) Evaluate(req *Request) (bool, error) {
+func (r Rules) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte, remoteAddr string) (bool, error) {
switch {
case r.And != nil:
- return r.And.Evaluate(req)
+ return r.And.Evaluate(headers, query, payload, body, remoteAddr)
case r.Or != nil:
- return r.Or.Evaluate(req)
+ return r.Or.Evaluate(headers, query, payload, body, remoteAddr)
case r.Not != nil:
- return r.Not.Evaluate(req)
+ return r.Not.Evaluate(headers, query, payload, body, remoteAddr)
case r.Match != nil:
- return r.Match.Evaluate(req)
+ return r.Match.Evaluate(headers, query, payload, body, remoteAddr)
}
return false, nil
@@ -835,11 +683,11 @@ func (r Rules) Evaluate(req *Request) (bool, error) {
type AndRule []Rules
// Evaluate AndRule will return true if and only if all of ChildRules evaluate to true
-func (r AndRule) Evaluate(req *Request) (bool, error) {
+func (r AndRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte, remoteAddr string) (bool, error) {
res := true
for _, v := range r {
- rv, err := v.Evaluate(req)
+ rv, err := v.Evaluate(headers, query, payload, body, remoteAddr)
if err != nil {
return false, err
}
@@ -857,17 +705,13 @@ func (r AndRule) Evaluate(req *Request) (bool, error) {
type OrRule []Rules
// Evaluate OrRule will return true if any of ChildRules evaluate to true
-func (r OrRule) Evaluate(req *Request) (bool, error) {
+func (r OrRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte, remoteAddr string) (bool, error) {
res := false
for _, v := range r {
- rv, err := v.Evaluate(req)
+ rv, err := v.Evaluate(headers, query, payload, body, remoteAddr)
if err != nil {
- if !IsParameterNodeError(err) {
- if !req.AllowSignatureErrors || (req.AllowSignatureErrors && !IsSignatureError(err)) {
- return false, err
- }
- }
+ return false, err
}
res = res || rv
@@ -883,8 +727,8 @@ func (r OrRule) Evaluate(req *Request) (bool, error) {
type NotRule Rules
// Evaluate NotRule will return true if and only if ChildRule evaluates to false
-func (r NotRule) Evaluate(req *Request) (bool, error) {
- rv, err := Rules(r).Evaluate(req)
+func (r NotRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte, remoteAddr string) (bool, error) {
+ rv, err := Rules(r).Evaluate(headers, query, payload, body, remoteAddr)
return !rv, err
}
@@ -902,85 +746,39 @@ type MatchRule struct {
const (
MatchValue string = "value"
MatchRegex string = "regex"
- MatchHMACSHA1 string = "payload-hmac-sha1"
- MatchHMACSHA256 string = "payload-hmac-sha256"
- MatchHMACSHA512 string = "payload-hmac-sha512"
MatchHashSHA1 string = "payload-hash-sha1"
MatchHashSHA256 string = "payload-hash-sha256"
- MatchHashSHA512 string = "payload-hash-sha512"
IPWhitelist string = "ip-whitelist"
ScalrSignature string = "scalr-signature"
)
// Evaluate MatchRule will return based on the type
-func (r MatchRule) Evaluate(req *Request) (bool, error) {
+func (r MatchRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte, remoteAddr string) (bool, error) {
if r.Type == IPWhitelist {
- return CheckIPWhitelist(req.RawRequest.RemoteAddr, r.IPRange)
+ return CheckIPWhitelist(remoteAddr, r.IPRange)
}
if r.Type == ScalrSignature {
- return CheckScalrSignature(req, r.Secret, true)
+ return CheckScalrSignature(*headers, *body, r.Secret, true)
}
- arg, err := r.Parameter.Get(req)
- if err == nil {
+ if arg, ok := r.Parameter.Get(headers, query, payload); ok {
switch r.Type {
case MatchValue:
- return compare(arg, r.Value), nil
+ return arg == r.Value, nil
case MatchRegex:
return regexp.MatchString(r.Regex, arg)
case MatchHashSHA1:
- log.Print(`warn: use of deprecated option payload-hash-sha1; use payload-hmac-sha1 instead`)
- fallthrough
- case MatchHMACSHA1:
- _, err := CheckPayloadSignature(req.Body, r.Secret, arg)
+ _, err := CheckPayloadSignature(*body, r.Secret, arg)
return err == nil, err
case MatchHashSHA256:
- log.Print(`warn: use of deprecated option payload-hash-sha256: use payload-hmac-sha256 instead`)
- fallthrough
- case MatchHMACSHA256:
- _, err := CheckPayloadSignature256(req.Body, r.Secret, arg)
- return err == nil, err
- case MatchHashSHA512:
- log.Print(`warn: use of deprecated option payload-hash-sha512: use payload-hmac-sha512 instead`)
- fallthrough
- case MatchHMACSHA512:
- _, err := CheckPayloadSignature512(req.Body, r.Secret, arg)
+ _, err := CheckPayloadSignature256(*body, r.Secret, arg)
return err == nil, err
}
}
- return false, err
-}
-
-// compare is a helper function for constant time string comparisons.
-func compare(a, b string) bool {
- return subtle.ConstantTimeCompare([]byte(a), []byte(b)) == 1
+ return false, nil
}
// getenv provides a template function to retrieve OS environment variables.
func getenv(s string) string {
return os.Getenv(s)
}
-
-// cat provides a template function to retrieve content of files
-// Similarly to getenv, if no file is found, it returns the empty string
-func cat(s string) string {
- data, e := os.ReadFile(s)
-
- if e != nil {
- return ""
- }
-
- return strings.TrimSuffix(string(data), "\n")
-}
-
-// credential provides a template function to retreive secrets using systemd's LoadCredential mechanism
-func credential(s string) string {
- dir := getenv("CREDENTIALS_DIRECTORY")
-
- // If no credential directory is found, fallback to the env variable
- if dir == "" {
- return getenv(s)
- }
-
- return cat(path.Join(dir, s))
-}
diff --git a/internal/hook/hook_test.go b/hook/hook_test.go
similarity index 60%
rename from internal/hook/hook_test.go
rename to hook/hook_test.go
index cbc49f7..e8a98bb 100644
--- a/internal/hook/hook_test.go
+++ b/hook/hook_test.go
@@ -1,7 +1,6 @@
package hook
import (
- "net/http"
"os"
"reflect"
"strings"
@@ -29,9 +28,9 @@ func TestGetParameter(t *testing.T) {
{"z.b", map[string]interface{}{"a": map[string]interface{}{"z": 2}}, nil, false},
{"a.2", map[string]interface{}{"a": []interface{}{"a", "b"}}, nil, false},
} {
- res, err := GetParameter(test.key, test.val)
- if (err == nil) != test.ok {
- t.Errorf("unexpected result given {%q, %q}: %s\n", test.key, test.val, err)
+ res, ok := GetParameter(test.key, test.val)
+ if ok != test.ok {
+ t.Errorf("unexpected result given {%q, %q}: %t\n", test.key, test.val, ok)
}
if !reflect.DeepEqual(res, test.expect) {
@@ -49,15 +48,10 @@ var checkPayloadSignatureTests = []struct {
}{
{[]byte(`{"a": "z"}`), "secret", "b17e04cbb22afa8ffbff8796fc1894ed27badd9e", "b17e04cbb22afa8ffbff8796fc1894ed27badd9e", true},
{[]byte(`{"a": "z"}`), "secret", "sha1=b17e04cbb22afa8ffbff8796fc1894ed27badd9e", "b17e04cbb22afa8ffbff8796fc1894ed27badd9e", true},
- {[]byte(`{"a": "z"}`), "secret", "sha1=XXXe04cbb22afa8ffbff8796fc1894ed27badd9e,sha1=b17e04cbb22afa8ffbff8796fc1894ed27badd9e", "b17e04cbb22afa8ffbff8796fc1894ed27badd9e", true},
- {[]byte(``), "secret", "25af6174a0fcecc4d346680a72b7ce644b9a88e8", "25af6174a0fcecc4d346680a72b7ce644b9a88e8", true},
// failures
{[]byte(`{"a": "z"}`), "secret", "XXXe04cbb22afa8ffbff8796fc1894ed27badd9e", "b17e04cbb22afa8ffbff8796fc1894ed27badd9e", false},
- {[]byte(`{"a": "z"}`), "secret", "sha1=XXXe04cbb22afa8ffbff8796fc1894ed27badd9e", "b17e04cbb22afa8ffbff8796fc1894ed27badd9e", false},
- {[]byte(`{"a": "z"}`), "secret", "sha1=XXXe04cbb22afa8ffbff8796fc1894ed27badd9e,sha1=XXXe04cbb22afa8ffbff8796fc1894ed27badd9e", "b17e04cbb22afa8ffbff8796fc1894ed27badd9e", false},
{[]byte(`{"a": "z"}`), "secreX", "b17e04cbb22afa8ffbff8796fc1894ed27badd9e", "900225703e9342328db7307692736e2f7cc7b36e", false},
{[]byte(`{"a": "z"}`), "", "b17e04cbb22afa8ffbff8796fc1894ed27badd9e", "", false},
- {[]byte(``), "secret", "XXXf6174a0fcecc4d346680a72b7ce644b9a88e8", "25af6174a0fcecc4d346680a72b7ce644b9a88e8", false},
}
func TestCheckPayloadSignature(t *testing.T) {
@@ -82,14 +76,9 @@ var checkPayloadSignature256Tests = []struct {
}{
{[]byte(`{"a": "z"}`), "secret", "f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", "f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", true},
{[]byte(`{"a": "z"}`), "secret", "sha256=f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", "f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", true},
- {[]byte(`{"a": "z"}`), "secret", "sha256=XXX7af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89,sha256=f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", "f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", true},
- {[]byte(``), "secret", "f9e66e179b6747ae54108f82f8ade8b3c25d76fd30afde6c395822c530196169", "f9e66e179b6747ae54108f82f8ade8b3c25d76fd30afde6c395822c530196169", true},
// failures
{[]byte(`{"a": "z"}`), "secret", "XXX7af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", "f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", false},
- {[]byte(`{"a": "z"}`), "secret", "sha256=XXX7af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", "f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", false},
- {[]byte(`{"a": "z"}`), "secret", "sha256=XXX7af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89,sha256=XXX7af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", "f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", false},
{[]byte(`{"a": "z"}`), "", "XXX7af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", "", false},
- {[]byte(``), "secret", "XXX66e179b6747ae54108f82f8ade8b3c25d76fd30afde6c395822c530196169", "f9e66e179b6747ae54108f82f8ade8b3c25d76fd30afde6c395822c530196169", false},
}
func TestCheckPayloadSignature256(t *testing.T) {
@@ -105,39 +94,10 @@ func TestCheckPayloadSignature256(t *testing.T) {
}
}
-var checkPayloadSignature512Tests = []struct {
- payload []byte
- secret string
- signature string
- mac string
- ok bool
-}{
- {[]byte(`{"a": "z"}`), "secret", "4ab17cc8ec668ead8bf498f87f8f32848c04d5ca3c9bcfcd3db9363f0deb44e580b329502a7fdff633d4d8fca301cc5c94a55a2fec458c675fb0ff2655898324", "4ab17cc8ec668ead8bf498f87f8f32848c04d5ca3c9bcfcd3db9363f0deb44e580b329502a7fdff633d4d8fca301cc5c94a55a2fec458c675fb0ff2655898324", true},
- {[]byte(`{"a": "z"}`), "secret", "sha512=4ab17cc8ec668ead8bf498f87f8f32848c04d5ca3c9bcfcd3db9363f0deb44e580b329502a7fdff633d4d8fca301cc5c94a55a2fec458c675fb0ff2655898324", "4ab17cc8ec668ead8bf498f87f8f32848c04d5ca3c9bcfcd3db9363f0deb44e580b329502a7fdff633d4d8fca301cc5c94a55a2fec458c675fb0ff2655898324", true},
- {[]byte(``), "secret", "b0e9650c5faf9cd8ae02276671545424104589b3656731ec193b25d01b07561c27637c2d4d68389d6cf5007a8632c26ec89ba80a01c77a6cdd389ec28db43901", "b0e9650c5faf9cd8ae02276671545424104589b3656731ec193b25d01b07561c27637c2d4d68389d6cf5007a8632c26ec89ba80a01c77a6cdd389ec28db43901", true},
- // failures
- {[]byte(`{"a": "z"}`), "secret", "74a0081f5b5988f4f3e8b8dd34dadc6291611f2e6260635a7e1535f8e95edb97ff520ba8b152e8ca5760ac42639854f3242e29efc81be73a8bf52d474d31ffea", "4ab17cc8ec668ead8bf498f87f8f32848c04d5ca3c9bcfcd3db9363f0deb44e580b329502a7fdff633d4d8fca301cc5c94a55a2fec458c675fb0ff2655898324", false},
- {[]byte(`{"a": "z"}`), "", "74a0081f5b5988f4f3e8b8dd34dadc6291611f2e6260635a7e1535f8e95edb97ff520ba8b152e8ca5760ac42639854f3242e29efc81be73a8bf52d474d31ffea", "", false},
- {[]byte(``), "secret", "XXX9650c5faf9cd8ae02276671545424104589b3656731ec193b25d01b07561c27637c2d4d68389d6cf5007a8632c26ec89ba80a01c77a6cdd389ec28db43901", "b0e9650c5faf9cd8ae02276671545424104589b3656731ec193b25d01b07561c27637c2d4d68389d6cf5007a8632c26ec89ba80a01c77a6cdd389ec28db43901", false},
-}
-
-func TestCheckPayloadSignature512(t *testing.T) {
- for _, tt := range checkPayloadSignature512Tests {
- mac, err := CheckPayloadSignature512(tt.payload, tt.secret, tt.signature)
- if (err == nil) != tt.ok || mac != tt.mac {
- t.Errorf("failed to check payload signature {%q, %q, %q}:\nexpected {mac:%#v, ok:%#v},\ngot {mac:%#v, ok:%#v}", tt.payload, tt.secret, tt.signature, tt.mac, tt.ok, mac, (err == nil))
- }
-
- if err != nil && tt.mac != "" && strings.Contains(err.Error(), tt.mac) {
- t.Errorf("error message should not disclose expected mac: %s", err)
- }
- }
-}
-
var checkScalrSignatureTests = []struct {
description string
headers map[string]interface{}
- body []byte
+ payload []byte
secret string
expectedSignature string
ok bool
@@ -176,13 +136,9 @@ var checkScalrSignatureTests = []struct {
func TestCheckScalrSignature(t *testing.T) {
for _, testCase := range checkScalrSignatureTests {
- r := &Request{
- Headers: testCase.headers,
- Body: testCase.body,
- }
- valid, err := CheckScalrSignature(r, testCase.secret, false)
+ valid, err := CheckScalrSignature(testCase.headers, testCase.payload, testCase.secret, false)
if valid != testCase.ok {
- t.Errorf("failed to check scalr signature for test case: %s\nexpected ok:%#v, got ok:%#v}",
+ t.Errorf("failed to check scalr signature fot test case: %s\nexpected ok:%#v, got ok:%#v}",
testCase.description, testCase.ok, valid)
}
@@ -228,9 +184,6 @@ var extractParameterTests = []struct {
{"a.b.0", map[string]interface{}{"a": map[string]interface{}{"b": []interface{}{"x", "y", "z"}}}, "x", true},
{"a.1.b", map[string]interface{}{"a": []interface{}{map[string]interface{}{"b": "y"}, map[string]interface{}{"b": "z"}}}, "z", true},
{"a.1.b.c", map[string]interface{}{"a": []interface{}{map[string]interface{}{"b": map[string]interface{}{"c": "y"}}, map[string]interface{}{"b": map[string]interface{}{"c": "z"}}}}, "z", true},
- {"b", map[string]interface{}{"b": map[string]interface{}{"z": 1}}, `{"z":1}`, true},
- {"c", map[string]interface{}{"c": []interface{}{"y", "z"}}, `["y","z"]`, true},
- {"d", map[string]interface{}{"d": [2]interface{}{"y", "z"}}, `["y","z"]`, true},
// failures
{"check_nil", nil, "", false},
{"a.X", map[string]interface{}{"a": map[string]interface{}{"b": "z"}}, "", false}, // non-existent parameter reference
@@ -245,76 +198,62 @@ var extractParameterTests = []struct {
func TestExtractParameter(t *testing.T) {
for _, tt := range extractParameterTests {
- value, err := ExtractParameterAsString(tt.s, tt.params)
- if (err == nil) != tt.ok || value != tt.value {
- t.Errorf("failed to extract parameter %q:\nexpected {value:%#v, ok:%#v},\ngot {value:%#v, err:%v}", tt.s, tt.value, tt.ok, value, err)
+ value, ok := ExtractParameterAsString(tt.s, tt.params)
+ if ok != tt.ok || value != tt.value {
+ t.Errorf("failed to extract parameter %q:\nexpected {value:%#v, ok:%#v},\ngot {value:%#v, ok:%#v}", tt.s, tt.value, tt.ok, value, ok)
}
}
}
var argumentGetTests = []struct {
source, name string
- headers, query, payload map[string]interface{}
- request *http.Request
+ headers, query, payload *map[string]interface{}
value string
ok bool
}{
- {"header", "a", map[string]interface{}{"A": "z"}, nil, nil, nil, "z", true},
- {"url", "a", nil, map[string]interface{}{"a": "z"}, nil, nil, "z", true},
- {"payload", "a", nil, nil, map[string]interface{}{"a": "z"}, nil, "z", true},
- {"request", "METHOD", nil, nil, map[string]interface{}{"a": "z"}, &http.Request{Method: "POST", RemoteAddr: "127.0.0.1:1234"}, "POST", true},
- {"request", "remote-addr", nil, nil, map[string]interface{}{"a": "z"}, &http.Request{Method: "POST", RemoteAddr: "127.0.0.1:1234"}, "127.0.0.1:1234", true},
- {"string", "a", nil, nil, map[string]interface{}{"a": "z"}, nil, "a", true},
+ {"header", "a", &map[string]interface{}{"A": "z"}, nil, nil, "z", true},
+ {"url", "a", nil, &map[string]interface{}{"a": "z"}, nil, "z", true},
+ {"payload", "a", nil, nil, &map[string]interface{}{"a": "z"}, "z", true},
+ {"string", "a", nil, nil, &map[string]interface{}{"a": "z"}, "a", true},
// failures
- {"header", "a", nil, map[string]interface{}{"a": "z"}, map[string]interface{}{"a": "z"}, nil, "", false}, // nil headers
- {"url", "a", map[string]interface{}{"A": "z"}, nil, map[string]interface{}{"a": "z"}, nil, "", false}, // nil query
- {"payload", "a", map[string]interface{}{"A": "z"}, map[string]interface{}{"a": "z"}, nil, nil, "", false}, // nil payload
- {"foo", "a", map[string]interface{}{"A": "z"}, nil, nil, nil, "", false}, // invalid source
+ {"header", "a", nil, &map[string]interface{}{"a": "z"}, &map[string]interface{}{"a": "z"}, "", false}, // nil headers
+ {"url", "a", &map[string]interface{}{"A": "z"}, nil, &map[string]interface{}{"a": "z"}, "", false}, // nil query
+ {"payload", "a", &map[string]interface{}{"A": "z"}, &map[string]interface{}{"a": "z"}, nil, "", false}, // nil payload
+ {"foo", "a", &map[string]interface{}{"A": "z"}, nil, nil, "", false}, // invalid source
}
func TestArgumentGet(t *testing.T) {
for _, tt := range argumentGetTests {
a := Argument{tt.source, tt.name, "", false}
- r := &Request{
- Headers: tt.headers,
- Query: tt.query,
- Payload: tt.payload,
- RawRequest: tt.request,
- }
- value, err := a.Get(r)
- if (err == nil) != tt.ok || value != tt.value {
- t.Errorf("failed to get {%q, %q}:\nexpected {value:%#v, ok:%#v},\ngot {value:%#v, err:%v}", tt.source, tt.name, tt.value, tt.ok, value, err)
+ value, ok := a.Get(tt.headers, tt.query, tt.payload)
+ if ok != tt.ok || value != tt.value {
+ t.Errorf("failed to get {%q, %q}:\nexpected {value:%#v, ok:%#v},\ngot {value:%#v, ok:%#v}", tt.source, tt.name, tt.value, tt.ok, value, ok)
}
}
}
var hookParseJSONParametersTests = []struct {
params []Argument
- headers, query, payload map[string]interface{}
- rheaders, rquery, rpayload map[string]interface{}
+ headers, query, payload *map[string]interface{}
+ rheaders, rquery, rpayload *map[string]interface{}
ok bool
}{
- {[]Argument{Argument{"header", "a", "", false}}, map[string]interface{}{"A": `{"b": "y"}`}, nil, nil, map[string]interface{}{"A": map[string]interface{}{"b": "y"}}, nil, nil, true},
- {[]Argument{Argument{"url", "a", "", false}}, nil, map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, nil, true},
- {[]Argument{Argument{"payload", "a", "", false}}, nil, nil, map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, true},
- {[]Argument{Argument{"header", "z", "", false}}, map[string]interface{}{"Z": `{}`}, nil, nil, map[string]interface{}{"Z": map[string]interface{}{}}, nil, nil, true},
+ {[]Argument{Argument{"header", "a", "", false}}, &map[string]interface{}{"A": `{"b": "y"}`}, nil, nil, &map[string]interface{}{"A": map[string]interface{}{"b": "y"}}, nil, nil, true},
+ {[]Argument{Argument{"url", "a", "", false}}, nil, &map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, &map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, nil, true},
+ {[]Argument{Argument{"payload", "a", "", false}}, nil, nil, &map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, &map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, true},
+ {[]Argument{Argument{"header", "z", "", false}}, &map[string]interface{}{"Z": `{}`}, nil, nil, &map[string]interface{}{"Z": map[string]interface{}{}}, nil, nil, true},
// failures
- {[]Argument{Argument{"header", "z", "", false}}, map[string]interface{}{"Z": ``}, nil, nil, map[string]interface{}{"Z": ``}, nil, nil, false}, // empty string
- {[]Argument{Argument{"header", "y", "", false}}, map[string]interface{}{"X": `{}`}, nil, nil, map[string]interface{}{"X": `{}`}, nil, nil, false}, // missing parameter
- {[]Argument{Argument{"string", "z", "", false}}, map[string]interface{}{"Z": ``}, nil, nil, map[string]interface{}{"Z": ``}, nil, nil, false}, // invalid argument source
+ {[]Argument{Argument{"header", "z", "", false}}, &map[string]interface{}{"Z": ``}, nil, nil, &map[string]interface{}{"Z": ``}, nil, nil, false}, // empty string
+ {[]Argument{Argument{"header", "y", "", false}}, &map[string]interface{}{"X": `{}`}, nil, nil, &map[string]interface{}{"X": `{}`}, nil, nil, false}, // missing parameter
+ {[]Argument{Argument{"string", "z", "", false}}, &map[string]interface{}{"Z": ``}, nil, nil, &map[string]interface{}{"Z": ``}, nil, nil, false}, // invalid argument source
}
func TestHookParseJSONParameters(t *testing.T) {
for _, tt := range hookParseJSONParametersTests {
h := &Hook{JSONStringParameters: tt.params}
- r := &Request{
- Headers: tt.headers,
- Query: tt.query,
- Payload: tt.payload,
- }
- err := h.ParseJSONParameters(r)
+ err := h.ParseJSONParameters(tt.headers, tt.query, tt.payload)
if (err == nil) != tt.ok || !reflect.DeepEqual(tt.headers, tt.rheaders) {
- t.Errorf("failed to parse %v:\nexpected %#v, ok: %v\ngot %#v, ok: %v", tt.params, tt.rheaders, tt.ok, tt.headers, (err == nil))
+ t.Errorf("failed to parse %v:\nexpected %#v, ok: %v\ngot %#v, ok: %v", tt.params, *tt.rheaders, tt.ok, *tt.headers, (err == nil))
}
}
}
@@ -322,24 +261,19 @@ func TestHookParseJSONParameters(t *testing.T) {
var hookExtractCommandArgumentsTests = []struct {
exec string
args []Argument
- headers, query, payload map[string]interface{}
+ headers, query, payload *map[string]interface{}
value []string
ok bool
}{
- {"test", []Argument{Argument{"header", "a", "", false}}, map[string]interface{}{"A": "z"}, nil, nil, []string{"test", "z"}, true},
+ {"test", []Argument{Argument{"header", "a", "", false}}, &map[string]interface{}{"A": "z"}, nil, nil, []string{"test", "z"}, true},
// failures
- {"fail", []Argument{Argument{"payload", "a", "", false}}, map[string]interface{}{"A": "z"}, nil, nil, []string{"fail", ""}, false},
+ {"fail", []Argument{Argument{"payload", "a", "", false}}, &map[string]interface{}{"A": "z"}, nil, nil, []string{"fail", ""}, false},
}
func TestHookExtractCommandArguments(t *testing.T) {
for _, tt := range hookExtractCommandArgumentsTests {
h := &Hook{ExecuteCommand: tt.exec, PassArgumentsToCommand: tt.args}
- r := &Request{
- Headers: tt.headers,
- Query: tt.query,
- Payload: tt.payload,
- }
- value, err := h.ExtractCommandArguments(r)
+ value, err := h.ExtractCommandArguments(tt.headers, tt.query, tt.payload)
if (err == nil) != tt.ok || !reflect.DeepEqual(value, tt.value) {
t.Errorf("failed to extract args {cmd=%q, args=%v}:\nexpected %#v, ok: %v\ngot %#v, ok: %v", tt.exec, tt.args, tt.value, tt.ok, value, (err == nil))
}
@@ -368,7 +302,7 @@ func TestHookExtractCommandArguments(t *testing.T) {
var hookExtractCommandArgumentsForEnvTests = []struct {
exec string
args []Argument
- headers, query, payload map[string]interface{}
+ headers, query, payload *map[string]interface{}
value []string
ok bool
}{
@@ -376,14 +310,14 @@ var hookExtractCommandArgumentsForEnvTests = []struct {
{
"test",
[]Argument{Argument{"header", "a", "", false}},
- map[string]interface{}{"A": "z"}, nil, nil,
+ &map[string]interface{}{"A": "z"}, nil, nil,
[]string{"HOOK_a=z"},
true,
},
{
"test",
[]Argument{Argument{"header", "a", "MYKEY", false}},
- map[string]interface{}{"A": "z"}, nil, nil,
+ &map[string]interface{}{"A": "z"}, nil, nil,
[]string{"MYKEY=z"},
true,
},
@@ -391,7 +325,7 @@ var hookExtractCommandArgumentsForEnvTests = []struct {
{
"fail",
[]Argument{Argument{"payload", "a", "", false}},
- map[string]interface{}{"A": "z"}, nil, nil,
+ &map[string]interface{}{"A": "z"}, nil, nil,
[]string{},
false,
},
@@ -400,12 +334,7 @@ var hookExtractCommandArgumentsForEnvTests = []struct {
func TestHookExtractCommandArgumentsForEnv(t *testing.T) {
for _, tt := range hookExtractCommandArgumentsForEnvTests {
h := &Hook{ExecuteCommand: tt.exec, PassEnvironmentToCommand: tt.args}
- r := &Request{
- Headers: tt.headers,
- Query: tt.query,
- Payload: tt.payload,
- }
- value, err := h.ExtractCommandArgumentsForEnv(r)
+ value, err := h.ExtractCommandArgumentsForEnv(tt.headers, tt.query, tt.payload)
if (err == nil) != tt.ok || !reflect.DeepEqual(value, tt.value) {
t.Errorf("failed to extract args for env {cmd=%q, args=%v}:\nexpected %#v, ok: %v\ngot %#v, ok: %v", tt.exec, tt.args, tt.value, tt.ok, value, (err == nil))
}
@@ -417,10 +346,10 @@ var hooksLoadFromFileTests = []struct {
asTemplate bool
ok bool
}{
- {"../../hooks.json.example", false, true},
- {"../../hooks.yaml.example", false, true},
- {"../../hooks.json.tmpl.example", true, true},
- {"../../hooks.yaml.tmpl.example", true, true},
+ {"../hooks.json.example", false, true},
+ {"../hooks.yaml.example", false, true},
+ {"../hooks.json.tmpl.example", true, true},
+ {"../hooks.yaml.tmpl.example", true, true},
{"", false, true},
// failures
{"missing.json", false, false},
@@ -483,30 +412,24 @@ func TestHooksMatch(t *testing.T) {
var matchRuleTests = []struct {
typ, regex, secret, value, ipRange string
param Argument
- headers, query, payload map[string]interface{}
+ headers, query, payload *map[string]interface{}
body []byte
remoteAddr string
ok bool
err bool
}{
- {"value", "", "", "z", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": "z"}, nil, nil, []byte{}, "", true, false},
- {"regex", "^z", "", "z", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": "z"}, nil, nil, []byte{}, "", true, false},
- {"payload-hmac-sha1", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": "b17e04cbb22afa8ffbff8796fc1894ed27badd9e"}, nil, nil, []byte(`{"a": "z"}`), "", true, false},
- {"payload-hash-sha1", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": "b17e04cbb22afa8ffbff8796fc1894ed27badd9e"}, nil, nil, []byte(`{"a": "z"}`), "", true, false},
- {"payload-hmac-sha256", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": "f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89"}, nil, nil, []byte(`{"a": "z"}`), "", true, false},
- {"payload-hash-sha256", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": "f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89"}, nil, nil, []byte(`{"a": "z"}`), "", true, false},
+ {"value", "", "", "z", "", Argument{"header", "a", "", false}, &map[string]interface{}{"A": "z"}, nil, nil, []byte{}, "", true, false},
+ {"regex", "^z", "", "z", "", Argument{"header", "a", "", false}, &map[string]interface{}{"A": "z"}, nil, nil, []byte{}, "", true, false},
+ {"payload-hash-sha1", "", "secret", "", "", Argument{"header", "a", "", false}, &map[string]interface{}{"A": "b17e04cbb22afa8ffbff8796fc1894ed27badd9e"}, nil, nil, []byte(`{"a": "z"}`), "", true, false},
+ {"payload-hash-sha256", "", "secret", "", "", Argument{"header", "a", "", false}, &map[string]interface{}{"A": "f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89"}, nil, nil, []byte(`{"a": "z"}`), "", true, false},
// failures
- {"value", "", "", "X", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": "z"}, nil, nil, []byte{}, "", false, false},
- {"regex", "^X", "", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": "z"}, nil, nil, []byte{}, "", false, false},
- {"value", "", "2", "X", "", Argument{"header", "a", "", false}, map[string]interface{}{"Y": "z"}, nil, nil, []byte{}, "", false, true}, // reference invalid header
+ {"value", "", "", "X", "", Argument{"header", "a", "", false}, &map[string]interface{}{"A": "z"}, nil, nil, []byte{}, "", false, false},
+ {"regex", "^X", "", "", "", Argument{"header", "a", "", false}, &map[string]interface{}{"A": "z"}, nil, nil, []byte{}, "", false, false},
+ {"value", "", "2", "X", "", Argument{"header", "a", "", false}, &map[string]interface{}{"Y": "z"}, nil, nil, []byte{}, "", false, false}, // reference invalid header
// errors
- {"regex", "*", "", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": "z"}, nil, nil, []byte{}, "", false, true}, // invalid regex
- {"payload-hmac-sha1", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": ""}, nil, nil, []byte{}, "", false, true}, // invalid hmac
- {"payload-hash-sha1", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": ""}, nil, nil, []byte{}, "", false, true}, // invalid hmac
- {"payload-hmac-sha256", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": ""}, nil, nil, []byte{}, "", false, true}, // invalid hmac
- {"payload-hash-sha256", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": ""}, nil, nil, []byte{}, "", false, true}, // invalid hmac
- {"payload-hmac-sha512", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": ""}, nil, nil, []byte{}, "", false, true}, // invalid hmac
- {"payload-hash-sha512", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": ""}, nil, nil, []byte{}, "", false, true}, // invalid hmac
+ {"regex", "*", "", "", "", Argument{"header", "a", "", false}, &map[string]interface{}{"A": "z"}, nil, nil, []byte{}, "", false, true}, // invalid regex
+ {"payload-hash-sha1", "", "secret", "", "", Argument{"header", "a", "", false}, &map[string]interface{}{"A": ""}, nil, nil, []byte{}, "", false, true}, // invalid hmac
+ {"payload-hash-sha256", "", "secret", "", "", Argument{"header", "a", "", false}, &map[string]interface{}{"A": ""}, nil, nil, []byte{}, "", false, true}, // invalid hmac
// IP whitelisting, valid cases
{"ip-whitelist", "", "", "", "192.168.0.1/24", Argument{}, nil, nil, nil, []byte{}, "192.168.0.2:9000", true, false}, // valid IPv4, with range
{"ip-whitelist", "", "", "", "192.168.0.1/24", Argument{}, nil, nil, nil, []byte{}, "192.168.0.2:9000", true, false}, // valid IPv4, with range
@@ -525,18 +448,9 @@ var matchRuleTests = []struct {
func TestMatchRule(t *testing.T) {
for i, tt := range matchRuleTests {
r := MatchRule{tt.typ, tt.regex, tt.secret, tt.value, tt.param, tt.ipRange}
- req := &Request{
- Headers: tt.headers,
- Query: tt.query,
- Payload: tt.payload,
- Body: tt.body,
- RawRequest: &http.Request{
- RemoteAddr: tt.remoteAddr,
- },
- }
- ok, err := r.Evaluate(req)
+ ok, err := r.Evaluate(tt.headers, tt.query, tt.payload, &tt.body, tt.remoteAddr)
if ok != tt.ok || (err != nil) != tt.err {
- t.Errorf("%d failed to match %#v:\nexpected ok: %#v, err: %v\ngot ok: %#v, err: %v", i, r, tt.ok, tt.err, ok, err)
+ t.Errorf("%d failed to match %#v:\nexpected ok: %#v, err: %v\ngot ok: %#v, err: %v", i, r, tt.ok, tt.err, ok, (err != nil))
}
}
}
@@ -544,7 +458,7 @@ func TestMatchRule(t *testing.T) {
var andRuleTests = []struct {
desc string // description of the test case
rule AndRule
- headers, query, payload map[string]interface{}
+ headers, query, payload *map[string]interface{}
body []byte
ok bool
err bool
@@ -555,8 +469,7 @@ var andRuleTests = []struct {
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false}, ""}},
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", "", false}, ""}},
},
- map[string]interface{}{"A": "z", "B": "y"}, nil, nil,
- []byte{},
+ &map[string]interface{}{"A": "z", "B": "y"}, nil, nil, []byte{},
true, false,
},
{
@@ -565,8 +478,7 @@ var andRuleTests = []struct {
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false}, ""}},
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", "", false}, ""}},
},
- map[string]interface{}{"A": "z", "B": "Y"}, nil, nil,
- []byte{},
+ &map[string]interface{}{"A": "z", "B": "Y"}, nil, nil, []byte{},
false, false,
},
// Complex test to cover Rules.Evaluate
@@ -592,8 +504,7 @@ var andRuleTests = []struct {
},
},
},
- map[string]interface{}{"A": "z", "B": "y", "C": "x", "D": "w", "E": "X", "F": "X"}, nil, nil,
- []byte{},
+ &map[string]interface{}{"A": "z", "B": "y", "C": "x", "D": "w", "E": "X", "F": "X"}, nil, nil, []byte{},
true, false,
},
{"empty rule", AndRule{{}}, nil, nil, nil, nil, false, false},
@@ -601,20 +512,14 @@ var andRuleTests = []struct {
{
"invalid rule",
AndRule{{Match: &MatchRule{"value", "", "", "X", Argument{"header", "a", "", false}, ""}}},
- map[string]interface{}{"Y": "z"}, nil, nil, nil,
- false, true,
+ &map[string]interface{}{"Y": "z"}, nil, nil, nil,
+ false, false,
},
}
func TestAndRule(t *testing.T) {
for _, tt := range andRuleTests {
- r := &Request{
- Headers: tt.headers,
- Query: tt.query,
- Payload: tt.payload,
- Body: tt.body,
- }
- ok, err := tt.rule.Evaluate(r)
+ ok, err := tt.rule.Evaluate(tt.headers, tt.query, tt.payload, &tt.body, "")
if ok != tt.ok || (err != nil) != tt.err {
t.Errorf("failed to match %#v:\nexpected ok: %#v, err: %v\ngot ok: %#v, err: %v", tt.desc, tt.ok, tt.err, ok, err)
}
@@ -624,7 +529,7 @@ func TestAndRule(t *testing.T) {
var orRuleTests = []struct {
desc string // description of the test case
rule OrRule
- headers, query, payload map[string]interface{}
+ headers, query, payload *map[string]interface{}
body []byte
ok bool
err bool
@@ -635,8 +540,7 @@ var orRuleTests = []struct {
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false}, ""}},
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", "", false}, ""}},
},
- map[string]interface{}{"A": "z", "B": "X"}, nil, nil,
- []byte{},
+ &map[string]interface{}{"A": "z", "B": "X"}, nil, nil, []byte{},
true, false,
},
{
@@ -645,8 +549,7 @@ var orRuleTests = []struct {
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false}, ""}},
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", "", false}, ""}},
},
- map[string]interface{}{"A": "X", "B": "y"}, nil, nil,
- []byte{},
+ &map[string]interface{}{"A": "X", "B": "y"}, nil, nil, []byte{},
true, false,
},
{
@@ -655,31 +558,23 @@ var orRuleTests = []struct {
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false}, ""}},
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", "", false}, ""}},
},
- map[string]interface{}{"A": "Z", "B": "Y"}, nil, nil,
- []byte{},
+ &map[string]interface{}{"A": "Z", "B": "Y"}, nil, nil, []byte{},
false, false,
},
// failures
{
- "missing parameter node",
+ "invalid rule",
OrRule{
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false}, ""}},
},
- map[string]interface{}{"Y": "Z"}, nil, nil,
- []byte{},
+ &map[string]interface{}{"Y": "Z"}, nil, nil, []byte{},
false, false,
},
}
func TestOrRule(t *testing.T) {
for _, tt := range orRuleTests {
- r := &Request{
- Headers: tt.headers,
- Query: tt.query,
- Payload: tt.payload,
- Body: tt.body,
- }
- ok, err := tt.rule.Evaluate(r)
+ ok, err := tt.rule.Evaluate(tt.headers, tt.query, tt.payload, &tt.body, "")
if ok != tt.ok || (err != nil) != tt.err {
t.Errorf("%#v:\nexpected ok: %#v, err: %v\ngot ok: %#v err: %v", tt.desc, tt.ok, tt.err, ok, err)
}
@@ -689,40 +584,20 @@ func TestOrRule(t *testing.T) {
var notRuleTests = []struct {
desc string // description of the test case
rule NotRule
- headers, query, payload map[string]interface{}
+ headers, query, payload *map[string]interface{}
body []byte
ok bool
err bool
}{
- {"(a=z): !a=X", NotRule{Match: &MatchRule{"value", "", "", "X", Argument{"header", "a", "", false}, ""}}, map[string]interface{}{"A": "z"}, nil, nil, []byte{}, true, false},
- {"(a=z): !a=z", NotRule{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false}, ""}}, map[string]interface{}{"A": "z"}, nil, nil, []byte{}, false, false},
+ {"(a=z): !a=X", NotRule{Match: &MatchRule{"value", "", "", "X", Argument{"header", "a", "", false}, ""}}, &map[string]interface{}{"A": "z"}, nil, nil, []byte{}, true, false},
+ {"(a=z): !a=z", NotRule{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false}, ""}}, &map[string]interface{}{"A": "z"}, nil, nil, []byte{}, false, false},
}
func TestNotRule(t *testing.T) {
for _, tt := range notRuleTests {
- r := &Request{
- Headers: tt.headers,
- Query: tt.query,
- Payload: tt.payload,
- Body: tt.body,
- }
- ok, err := tt.rule.Evaluate(r)
+ ok, err := tt.rule.Evaluate(tt.headers, tt.query, tt.payload, &tt.body, "")
if ok != tt.ok || (err != nil) != tt.err {
t.Errorf("failed to match %#v:\nexpected ok: %#v, err: %v\ngot ok: %#v, err: %v", tt.rule, tt.ok, tt.err, ok, err)
}
}
}
-
-func TestCompare(t *testing.T) {
- for _, tt := range []struct {
- a, b string
- ok bool
- }{
- {"abcd", "abcd", true},
- {"zyxw", "abcd", false},
- } {
- if ok := compare(tt.a, tt.b); ok != tt.ok {
- t.Errorf("compare failed for %q and %q: got %v\n", tt.a, tt.b, ok)
- }
- }
-}
diff --git a/hooks.json.example b/hooks.json.example
index 90cd275..00f6ff3 100644
--- a/hooks.json.example
+++ b/hooks.json.example
@@ -33,7 +33,7 @@
{
"match":
{
- "type": "payload-hmac-sha1",
+ "type": "payload-hash-sha1",
"secret": "mysecret",
"parameter":
{
diff --git a/hooks.json.tmpl.example b/hooks.json.tmpl.example
index 5c00643..70e05c8 100644
--- a/hooks.json.tmpl.example
+++ b/hooks.json.tmpl.example
@@ -33,7 +33,7 @@
{
"match":
{
- "type": "payload-hmac-sha1",
+ "type": "payload-hash-sha1",
"secret": "{{ getenv "XXXTEST_SECRET" | js }}",
"parameter":
{
diff --git a/hooks.yaml.example b/hooks.yaml.example
index ba9d0b7..74713e0 100644
--- a/hooks.yaml.example
+++ b/hooks.yaml.example
@@ -15,7 +15,7 @@
trigger-rule:
and:
- match:
- type: payload-hmac-sha1
+ type: payload-hash-sha1
secret: mysecret
parameter:
source: header
diff --git a/hooks.yaml.tmpl.example b/hooks.yaml.tmpl.example
index 672c8d0..2bcfbd6 100644
--- a/hooks.yaml.tmpl.example
+++ b/hooks.yaml.tmpl.example
@@ -15,7 +15,7 @@
trigger-rule:
and:
- match:
- type: payload-hmac-sha1
+ type: payload-hash-sha1
secret: "{{ getenv "XXXTEST_SECRET" | js }}"
parameter:
source: header
diff --git a/images/hookdeck-black.svg b/images/hookdeck-black.svg
deleted file mode 100644
index 962b07d..0000000
--- a/images/hookdeck-black.svg
+++ /dev/null
@@ -1,11 +0,0 @@
-
diff --git a/images/hookdeck-white.svg b/images/hookdeck-white.svg
deleted file mode 100644
index 19afbbf..0000000
--- a/images/hookdeck-white.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/internal/hook/request.go b/internal/hook/request.go
deleted file mode 100644
index 6e0bd0c..0000000
--- a/internal/hook/request.go
+++ /dev/null
@@ -1,119 +0,0 @@
-package hook
-
-import (
- "bytes"
- "encoding/json"
- "fmt"
- "net/http"
- "net/url"
- "unicode"
-
- "github.com/clbanning/mxj/v2"
-)
-
-// Request represents a webhook request.
-type Request struct {
- // The request ID set by the RequestID middleware.
- ID string
-
- // The Content-Type of the request.
- ContentType string
-
- // The raw request body.
- Body []byte
-
- // Headers is a map of the parsed headers.
- Headers map[string]interface{}
-
- // Query is a map of the parsed URL query values.
- Query map[string]interface{}
-
- // Payload is a map of the parsed payload.
- Payload map[string]interface{}
-
- // The underlying HTTP request.
- RawRequest *http.Request
-
- // Treat signature errors as simple validate failures.
- AllowSignatureErrors bool
-}
-
-func (r *Request) ParseJSONPayload() error {
- decoder := json.NewDecoder(bytes.NewReader(r.Body))
- decoder.UseNumber()
-
- var firstChar byte
- for i := 0; i < len(r.Body); i++ {
- if unicode.IsSpace(rune(r.Body[i])) {
- continue
- }
- firstChar = r.Body[i]
- break
- }
-
- if firstChar == byte('[') {
- var arrayPayload interface{}
- err := decoder.Decode(&arrayPayload)
- if err != nil {
- return fmt.Errorf("error parsing JSON array payload %+v", err)
- }
-
- r.Payload = make(map[string]interface{}, 1)
- r.Payload["root"] = arrayPayload
- } else {
- err := decoder.Decode(&r.Payload)
- if err != nil {
- return fmt.Errorf("error parsing JSON payload %+v", err)
- }
- }
-
- return nil
-}
-
-func (r *Request) ParseHeaders(headers map[string][]string) {
- r.Headers = make(map[string]interface{}, len(headers))
-
- for k, v := range headers {
- if len(v) > 0 {
- r.Headers[k] = v[0]
- }
- }
-}
-
-func (r *Request) ParseQuery(query map[string][]string) {
- r.Query = make(map[string]interface{}, len(query))
-
- for k, v := range query {
- if len(v) > 0 {
- r.Query[k] = v[0]
- }
- }
-}
-
-func (r *Request) ParseFormPayload() error {
- fd, err := url.ParseQuery(string(r.Body))
- if err != nil {
- return fmt.Errorf("error parsing form payload %+v", err)
- }
-
- r.Payload = make(map[string]interface{}, len(fd))
-
- for k, v := range fd {
- if len(v) > 0 {
- r.Payload[k] = v[0]
- }
- }
-
- return nil
-}
-
-func (r *Request) ParseXMLPayload() error {
- var err error
-
- r.Payload, err = mxj.NewMapXmlReader(bytes.NewReader(r.Body))
- if err != nil {
- return fmt.Errorf("error parsing XML payload: %+v", err)
- }
-
- return nil
-}
diff --git a/internal/middleware/dumper.go b/internal/middleware/dumper.go
deleted file mode 100644
index f54609f..0000000
--- a/internal/middleware/dumper.go
+++ /dev/null
@@ -1,105 +0,0 @@
-package middleware
-
-// Derived from the Goa project, MIT Licensed
-// https://github.com/goadesign/goa/blob/v3/http/middleware/debug.go
-
-import (
- "bufio"
- "bytes"
- "fmt"
- "io"
- "net"
- "net/http"
- "net/http/httputil"
- "sort"
- "strings"
-)
-
-// responseDupper tees the response to a buffer and a response writer.
-type responseDupper struct {
- http.ResponseWriter
- Buffer *bytes.Buffer
- Status int
-}
-
-// Dumper returns a debug middleware which prints detailed information about
-// incoming requests and outgoing responses including all headers, parameters
-// and bodies.
-func Dumper(w io.Writer) func(http.Handler) http.Handler {
- return func(h http.Handler) http.Handler {
- return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
- buf := &bytes.Buffer{}
- // Request ID
- rid := r.Context().Value(RequestIDKey)
-
- // Dump request
-
- bd, err := httputil.DumpRequest(r, true)
- if err != nil {
- buf.WriteString(fmt.Sprintf("[%s] Error dumping request for debugging: %s\n", rid, err))
- }
-
- sc := bufio.NewScanner(bytes.NewBuffer(bd))
- sc.Split(bufio.ScanLines)
- for sc.Scan() {
- buf.WriteString(fmt.Sprintf("> [%s] ", rid))
- buf.WriteString(sc.Text() + "\n")
- }
-
- w.Write(buf.Bytes())
- buf.Reset()
-
- // Dump Response
-
- dupper := &responseDupper{ResponseWriter: rw, Buffer: &bytes.Buffer{}}
- h.ServeHTTP(dupper, r)
-
- // Response Status
- buf.WriteString(fmt.Sprintf("< [%s] %d %s\n", rid, dupper.Status, http.StatusText(dupper.Status)))
-
- // Response Headers
- keys := make([]string, len(dupper.Header()))
- i := 0
- for k := range dupper.Header() {
- keys[i] = k
- i++
- }
- sort.Strings(keys)
- for _, k := range keys {
- buf.WriteString(fmt.Sprintf("< [%s] %s: %s\n", rid, k, strings.Join(dupper.Header()[k], ", ")))
- }
-
- // Response Body
- if dupper.Buffer.Len() > 0 {
- buf.WriteString(fmt.Sprintf("< [%s]\n", rid))
- sc = bufio.NewScanner(dupper.Buffer)
- sc.Split(bufio.ScanLines)
- for sc.Scan() {
- buf.WriteString(fmt.Sprintf("< [%s] ", rid))
- buf.WriteString(sc.Text() + "\n")
- }
- }
- w.Write(buf.Bytes())
- })
- }
-}
-
-// Write writes the data to the buffer and connection as part of an HTTP reply.
-func (r *responseDupper) Write(b []byte) (int, error) {
- r.Buffer.Write(b)
- return r.ResponseWriter.Write(b)
-}
-
-// WriteHeader records the status and sends an HTTP response header with status code.
-func (r *responseDupper) WriteHeader(s int) {
- r.Status = s
- r.ResponseWriter.WriteHeader(s)
-}
-
-// Hijack supports the http.Hijacker interface.
-func (r *responseDupper) Hijack() (net.Conn, *bufio.ReadWriter, error) {
- if hijacker, ok := r.ResponseWriter.(http.Hijacker); ok {
- return hijacker.Hijack()
- }
- return nil, nil, fmt.Errorf("dumper middleware: inner ResponseWriter cannot be hijacked: %T", r.ResponseWriter)
-}
diff --git a/internal/middleware/logger.go b/internal/middleware/logger.go
deleted file mode 100644
index c4b5a8d..0000000
--- a/internal/middleware/logger.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package middleware
-
-import (
- "bytes"
- "fmt"
- "log"
- "net/http"
- "time"
-
- "github.com/dustin/go-humanize"
- "github.com/go-chi/chi/v5/middleware"
-)
-
-// Logger is a middleware that logs useful data about each HTTP request.
-type Logger struct {
- Logger middleware.LoggerInterface
-}
-
-// NewLogger creates a new RequestLogger Handler.
-func NewLogger() func(next http.Handler) http.Handler {
- return middleware.RequestLogger(&Logger{})
-}
-
-// NewLogEntry creates a new LogEntry for the request.
-func (l *Logger) NewLogEntry(r *http.Request) middleware.LogEntry {
- e := &LogEntry{
- req: r,
- buf: &bytes.Buffer{},
- }
-
- return e
-}
-
-// LogEntry represents an individual log entry.
-type LogEntry struct {
- *Logger
- req *http.Request
- buf *bytes.Buffer
-}
-
-// Write constructs and writes the final log entry.
-func (l *LogEntry) Write(status, totalBytes int, header http.Header, elapsed time.Duration, extra interface{}) {
- rid := GetReqID(l.req.Context())
- if rid != "" {
- fmt.Fprintf(l.buf, "[%s] ", rid)
- }
-
- fmt.Fprintf(l.buf, "%03d | %s | %s | ", status, humanize.IBytes(uint64(totalBytes)), elapsed)
- l.buf.WriteString(l.req.Host + " | " + l.req.Method + " " + l.req.RequestURI)
- log.Print(l.buf.String())
-}
-
-// Panic prints the call stack for a panic.
-func (l *LogEntry) Panic(v interface{}, stack []byte) {
- e := l.NewLogEntry(l.req).(*LogEntry)
- fmt.Fprintf(e.buf, "panic: %#v", v)
- log.Print(e.buf.String())
- log.Print(string(stack))
-}
diff --git a/internal/middleware/request_id.go b/internal/middleware/request_id.go
deleted file mode 100644
index 9d6cb1a..0000000
--- a/internal/middleware/request_id.go
+++ /dev/null
@@ -1,98 +0,0 @@
-package middleware
-
-// Derived from Goa project, MIT Licensed
-// https://github.com/goadesign/goa/blob/v3/http/middleware/requestid.go
-
-import (
- "context"
- "net/http"
-
- "github.com/gofrs/uuid/v5"
-)
-
-// Key to use when setting the request ID.
-type ctxKeyRequestID int
-
-// RequestIDKey is the key that holds the unique request ID in a request context.
-const RequestIDKey ctxKeyRequestID = 0
-
-// RequestID is a middleware that injects a request ID into the context of each
-// request.
-func RequestID(options ...RequestIDOption) func(http.Handler) http.Handler {
- o := newRequestIDOptions(options...)
-
- return func(next http.Handler) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- ctx := r.Context()
-
- var id string
-
- if o.UseRequestID() {
- id = r.Header.Get("X-Request-Id")
- if o.requestIDLimit > 0 && len(id) > o.requestIDLimit {
- id = id[:o.requestIDLimit]
- }
- }
-
- if id == "" {
- id = uuid.Must(uuid.NewV4()).String()[:6]
- }
-
- ctx = context.WithValue(ctx, RequestIDKey, id)
- next.ServeHTTP(w, r.WithContext(ctx))
- })
- }
-}
-
-// GetReqID returns a request ID from the given context if one is present.
-// Returns the empty string if a request ID cannot be found.
-func GetReqID(ctx context.Context) string {
- if ctx == nil {
- return ""
- }
- if reqID, ok := ctx.Value(RequestIDKey).(string); ok {
- return reqID
- }
- return ""
-}
-
-func UseXRequestIDHeaderOption(f bool) RequestIDOption {
- return func(o *RequestIDOptions) *RequestIDOptions {
- o.useXRequestID = f
- return o
- }
-}
-
-func XRequestIDLimitOption(limit int) RequestIDOption {
- return func(o *RequestIDOptions) *RequestIDOptions {
- o.requestIDLimit = limit
- return o
- }
-}
-
-type (
- RequestIDOption func(*RequestIDOptions) *RequestIDOptions
-
- RequestIDOptions struct {
- // useXRequestID enabled the use of the X-Request-Id request header as
- // the request ID.
- useXRequestID bool
-
- // requestIDLimit is the maximum length of the X-Request-Id header
- // allowed. Values longer than this value are truncated. Zero value
- // means no limit.
- requestIDLimit int
- }
-)
-
-func newRequestIDOptions(options ...RequestIDOption) *RequestIDOptions {
- o := new(RequestIDOptions)
- for _, opt := range options {
- o = opt(o)
- }
- return o
-}
-
-func (o *RequestIDOptions) UseRequestID() bool {
- return o.useXRequestID
-}
diff --git a/internal/pidfile/README.md b/internal/pidfile/README.md
deleted file mode 100644
index 67f762c..0000000
--- a/internal/pidfile/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-Package pidfile is derived from github.com/moby/moby/pkg/pidfile.
-
-Moby is licensed under the Apache License, Version 2.0.
-Copyright 2012-2017 Docker, Inc.
diff --git a/internal/pidfile/mkdirall.go b/internal/pidfile/mkdirall.go
deleted file mode 100644
index 1491366..0000000
--- a/internal/pidfile/mkdirall.go
+++ /dev/null
@@ -1,11 +0,0 @@
-// +build !windows
-
-package pidfile
-
-import "os"
-
-// MkdirAll creates a directory named path along with any necessary parents,
-// with permission specified by attribute perm for all dir created.
-func MkdirAll(path string, perm os.FileMode) error {
- return os.MkdirAll(path, perm)
-}
diff --git a/internal/pidfile/mkdirall_windows.go b/internal/pidfile/mkdirall_windows.go
deleted file mode 100644
index 0194de2..0000000
--- a/internal/pidfile/mkdirall_windows.go
+++ /dev/null
@@ -1,109 +0,0 @@
-// +build windows
-
-package pidfile
-
-import (
- "os"
- "regexp"
- "syscall"
- "unsafe"
-
- "golang.org/x/sys/windows"
-)
-
-// MkdirAll implementation that is volume path aware for Windows. It can be used
-// as a drop-in replacement for os.MkdirAll()
-func MkdirAll(path string, _ os.FileMode) error {
- return mkdirall(path, false, "")
-}
-
-// mkdirall is a custom version of os.MkdirAll modified for use on Windows
-// so that it is both volume path aware, and can create a directory with
-// a DACL.
-func mkdirall(path string, applyACL bool, sddl string) error {
- if re := regexp.MustCompile(`^\\\\\?\\Volume{[a-z0-9-]+}$`); re.MatchString(path) {
- return nil
- }
-
- // The rest of this method is largely copied from os.MkdirAll and should be kept
- // as-is to ensure compatibility.
-
- // Fast path: if we can tell whether path is a directory or file, stop with success or error.
- dir, err := os.Stat(path)
- if err == nil {
- if dir.IsDir() {
- return nil
- }
- return &os.PathError{
- Op: "mkdir",
- Path: path,
- Err: syscall.ENOTDIR,
- }
- }
-
- // Slow path: make sure parent exists and then call Mkdir for path.
- i := len(path)
- for i > 0 && os.IsPathSeparator(path[i-1]) { // Skip trailing path separator.
- i--
- }
-
- j := i
- for j > 0 && !os.IsPathSeparator(path[j-1]) { // Scan backward over element.
- j--
- }
-
- if j > 1 {
- // Create parent
- err = mkdirall(path[0:j-1], false, sddl)
- if err != nil {
- return err
- }
- }
-
- // Parent now exists; invoke os.Mkdir or mkdirWithACL and use its result.
- if applyACL {
- err = mkdirWithACL(path, sddl)
- } else {
- err = os.Mkdir(path, 0)
- }
-
- if err != nil {
- // Handle arguments like "foo/." by
- // double-checking that directory doesn't exist.
- dir, err1 := os.Lstat(path)
- if err1 == nil && dir.IsDir() {
- return nil
- }
- return err
- }
- return nil
-}
-
-// mkdirWithACL creates a new directory. If there is an error, it will be of
-// type *PathError. .
-//
-// This is a modified and combined version of os.Mkdir and windows.Mkdir
-// in golang to cater for creating a directory am ACL permitting full
-// access, with inheritance, to any subfolder/file for Built-in Administrators
-// and Local System.
-func mkdirWithACL(name string, sddl string) error {
- sa := windows.SecurityAttributes{Length: 0}
- sd, err := windows.SecurityDescriptorFromString(sddl)
- if err != nil {
- return &os.PathError{Op: "mkdir", Path: name, Err: err}
- }
- sa.Length = uint32(unsafe.Sizeof(sa))
- sa.InheritHandle = 1
- sa.SecurityDescriptor = sd
-
- namep, err := windows.UTF16PtrFromString(name)
- if err != nil {
- return &os.PathError{Op: "mkdir", Path: name, Err: err}
- }
-
- e := windows.CreateDirectory(namep, &sa)
- if e != nil {
- return &os.PathError{Op: "mkdir", Path: name, Err: e}
- }
- return nil
-}
diff --git a/internal/pidfile/pidfile.go b/internal/pidfile/pidfile.go
deleted file mode 100644
index e602dcb..0000000
--- a/internal/pidfile/pidfile.go
+++ /dev/null
@@ -1,51 +0,0 @@
-// Package pidfile provides structure and helper functions to create and remove
-// PID file. A PID file is usually a file used to store the process ID of a
-// running process.
-package pidfile
-
-import (
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- "strconv"
- "strings"
-)
-
-// PIDFile is a file used to store the process ID of a running process.
-type PIDFile struct {
- path string
-}
-
-func checkPIDFileAlreadyExists(path string) error {
- if pidByte, err := ioutil.ReadFile(path); err == nil {
- pidString := strings.TrimSpace(string(pidByte))
- if pid, err := strconv.Atoi(pidString); err == nil {
- if processExists(pid) {
- return fmt.Errorf("pid file found, ensure webhook is not running or delete %s", path)
- }
- }
- }
- return nil
-}
-
-// New creates a PIDfile using the specified path.
-func New(path string) (*PIDFile, error) {
- if err := checkPIDFileAlreadyExists(path); err != nil {
- return nil, err
- }
- // Note MkdirAll returns nil if a directory already exists
- if err := MkdirAll(filepath.Dir(path), os.FileMode(0o755)); err != nil {
- return nil, err
- }
- if err := ioutil.WriteFile(path, []byte(fmt.Sprintf("%d", os.Getpid())), 0o600); err != nil {
- return nil, err
- }
-
- return &PIDFile{path: path}, nil
-}
-
-// Remove removes the PIDFile.
-func (file PIDFile) Remove() error {
- return os.Remove(file.path)
-}
diff --git a/internal/pidfile/pidfile_darwin.go b/internal/pidfile/pidfile_darwin.go
deleted file mode 100644
index 2cd001a..0000000
--- a/internal/pidfile/pidfile_darwin.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// +build darwin
-
-package pidfile
-
-import (
- "golang.org/x/sys/unix"
-)
-
-func processExists(pid int) bool {
- // OS X does not have a proc filesystem.
- // Use kill -0 pid to judge if the process exists.
- err := unix.Kill(pid, 0)
- return err == nil
-}
diff --git a/internal/pidfile/pidfile_test.go b/internal/pidfile/pidfile_test.go
deleted file mode 100644
index 73e8af7..0000000
--- a/internal/pidfile/pidfile_test.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package pidfile
-
-import (
- "io/ioutil"
- "os"
- "path/filepath"
- "testing"
-)
-
-func TestNewAndRemove(t *testing.T) {
- dir, err := ioutil.TempDir(os.TempDir(), "test-pidfile")
- if err != nil {
- t.Fatal("Could not create test directory")
- }
-
- path := filepath.Join(dir, "testfile")
- file, err := New(path)
- if err != nil {
- t.Fatal("Could not create test file", err)
- }
-
- _, err = New(path)
- if err == nil {
- t.Fatal("Test file creation not blocked")
- }
-
- if err := file.Remove(); err != nil {
- t.Fatal("Could not delete created test file")
- }
-}
-
-func TestRemoveInvalidPath(t *testing.T) {
- file := PIDFile{path: filepath.Join("foo", "bar")}
-
- if err := file.Remove(); err == nil {
- t.Fatal("Non-existing file doesn't give an error on delete")
- }
-}
diff --git a/internal/pidfile/pidfile_unix.go b/internal/pidfile/pidfile_unix.go
deleted file mode 100644
index 1bf5221..0000000
--- a/internal/pidfile/pidfile_unix.go
+++ /dev/null
@@ -1,16 +0,0 @@
-// +build !windows,!darwin
-
-package pidfile
-
-import (
- "os"
- "path/filepath"
- "strconv"
-)
-
-func processExists(pid int) bool {
- if _, err := os.Stat(filepath.Join("/proc", strconv.Itoa(pid))); err == nil {
- return true
- }
- return false
-}
diff --git a/internal/pidfile/pidfile_windows.go b/internal/pidfile/pidfile_windows.go
deleted file mode 100644
index 86850d4..0000000
--- a/internal/pidfile/pidfile_windows.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package pidfile
-
-import (
- "golang.org/x/sys/windows"
-)
-
-const (
- processQueryLimitedInformation = 0x1000
-
- stillActive = 259
-)
-
-func processExists(pid int) bool {
- h, err := windows.OpenProcess(processQueryLimitedInformation, false, uint32(pid))
- if err != nil {
- return false
- }
- var c uint32
- err = windows.GetExitCodeProcess(h, &c)
- windows.Close(h)
- if err != nil {
- return c == stillActive
- }
- return true
-}
diff --git a/platform_unix.go b/platform_unix.go
deleted file mode 100644
index 5000e4a..0000000
--- a/platform_unix.go
+++ /dev/null
@@ -1,47 +0,0 @@
-//go:build !windows
-// +build !windows
-
-package main
-
-import (
- "flag"
- "fmt"
- "github.com/coreos/go-systemd/v22/activation"
- "net"
-)
-
-func platformFlags() {
- flag.StringVar(&socket, "socket", "", "path to a Unix socket (e.g. /tmp/webhook.sock) to use instead of listening on an ip and port; if specified, the ip and port options are ignored")
- flag.IntVar(&setGID, "setgid", 0, "set group ID after opening listening port; must be used with setuid, not permitted with -socket")
- flag.IntVar(&setUID, "setuid", 0, "set user ID after opening listening port; must be used with setgid, not permitted with -socket")
-}
-
-func trySocketListener() (net.Listener, error) {
- // first check whether we have any sockets from systemd
- listeners, err := activation.Listeners()
- if err != nil {
- return nil, fmt.Errorf("failed to retrieve sockets from systemd: %w", err)
- }
- numListeners := len(listeners)
- if numListeners > 1 {
- return nil, fmt.Errorf("received %d sockets from systemd, but only 1 is supported", numListeners)
- }
- if numListeners == 1 {
- sockAddr := listeners[0].Addr()
- if sockAddr.Network() == "tcp" {
- addr = sockAddr.String()
- } else {
- addr = fmt.Sprintf("{%s:%s}", sockAddr.Network(), sockAddr.String())
- }
- return listeners[0], nil
- }
- // if we get to here, we got no sockets from systemd, so check -socket flag
- if socket != "" {
- if setGID != 0 || setUID != 0 {
- return nil, fmt.Errorf("-setuid and -setgid options are not compatible with -socket. If you need to bind a socket as root but run webhook as a different user, consider using systemd activation")
- }
- addr = fmt.Sprintf("{unix:%s}", socket)
- return net.Listen("unix", socket)
- }
- return nil, nil
-}
diff --git a/platform_windows.go b/platform_windows.go
deleted file mode 100644
index de5ec6c..0000000
--- a/platform_windows.go
+++ /dev/null
@@ -1,23 +0,0 @@
-//go:build windows
-// +build windows
-
-package main
-
-import (
- "flag"
- "fmt"
- "github.com/Microsoft/go-winio"
- "net"
-)
-
-func platformFlags() {
- flag.StringVar(&socket, "socket", "", "path to a Windows named pipe (e.g. \\\\.\\pipe\\webhook) to use instead of listening on an ip and port; if specified, the ip and port options are ignored")
-}
-
-func trySocketListener() (net.Listener, error) {
- if socket != "" {
- addr = fmt.Sprintf("{pipe:%s}", socket)
- return winio.ListenPipe(socket, nil)
- }
- return nil, nil
-}
diff --git a/signals.go b/signals.go
index 9390513..d53a3f6 100644
--- a/signals.go
+++ b/signals.go
@@ -1,4 +1,3 @@
-//go:build !windows
// +build !windows
package main
@@ -7,7 +6,6 @@ import (
"log"
"os"
"os/signal"
- "strings"
"syscall"
)
@@ -16,9 +14,6 @@ func setupSignals() {
signals = make(chan os.Signal, 1)
signal.Notify(signals, syscall.SIGUSR1)
- signal.Notify(signals, syscall.SIGHUP)
- signal.Notify(signals, syscall.SIGTERM)
- signal.Notify(signals, os.Interrupt)
go watchForSignals()
}
@@ -28,35 +23,11 @@ func watchForSignals() {
for {
sig := <-signals
- switch sig {
- case syscall.SIGUSR1:
+ if sig == syscall.SIGUSR1 {
log.Println("caught USR1 signal")
+
reloadAllHooks()
-
- case syscall.SIGHUP:
- log.Println("caught HUP signal")
- reloadAllHooks()
-
- case os.Interrupt, syscall.SIGTERM:
- log.Printf("caught %s signal; exiting\n", sig)
- if pidFile != nil {
- err := pidFile.Remove()
- if err != nil {
- log.Print(err)
- }
- }
- if socket != "" && !strings.HasPrefix(socket, "@") {
- // we've been listening on a named Unix socket, delete it
- // before we exit so subsequent runs can re-bind the same
- // socket path
- err := os.Remove(socket)
- if err != nil {
- log.Printf("Failed to remove socket file %s: %v", socket, err)
- }
- }
- os.Exit(0)
-
- default:
+ } else {
log.Printf("caught unhandled signal %+v\n", sig)
}
}
diff --git a/test/hooks.json.tmpl b/test/hooks.json.tmpl
index 9cfe348..2fd90da 100644
--- a/test/hooks.json.tmpl
+++ b/test/hooks.json.tmpl
@@ -3,7 +3,6 @@
"id": "github",
"execute-command": "{{ .Hookecho }}",
"command-working-directory": "/",
- "http-methods": ["Post "],
"include-command-output-in-response": true,
"trigger-rule-mismatch-http-response-code": 400,
"pass-environment-to-command":
@@ -31,7 +30,7 @@
{
"match":
{
- "type": "payload-hmac-sha1",
+ "type": "payload-hash-sha1",
"secret": "mysecret",
"parameter":
{
@@ -55,149 +54,6 @@
]
}
},
- {
- "id": "github-multi-sig",
- "execute-command": "{{ .Hookecho }}",
- "command-working-directory": "/",
- "http-methods": ["Post "],
- "include-command-output-in-response": true,
- "trigger-rule-mismatch-http-response-code": 400,
- "trigger-signature-soft-failures": true,
- "pass-environment-to-command":
- [
- {
- "source": "payload",
- "name": "head_commit.timestamp"
- }
- ],
- "pass-arguments-to-command":
- [
- {
- "source": "payload",
- "name": "head_commit.id"
- },
- {
- "source": "payload",
- "name": "head_commit.author.email"
- }
- ],
- "trigger-rule":
- {
- "and":
- [
- "or":
- [
- {
- "match":
- {
- "type": "payload-hmac-sha1",
- "secret": "mysecretFAIL",
- "parameter":
- {
- "source": "header",
- "name": "X-Hub-Signature"
- }
- }
- },
- {
- "match":
- {
- "type": "payload-hmac-sha1",
- "secret": "mysecret",
- "parameter":
- {
- "source": "header",
- "name": "X-Hub-Signature"
- }
- }
- }
- ],
- {
- "match":
- {
- "type": "value",
- "value": "refs/heads/master",
- "parameter":
- {
- "source": "payload",
- "name": "ref"
- }
- }
- }
- ]
- }
- },
- {
- "id": "github-multi-sig-fail",
- "execute-command": "{{ .Hookecho }}",
- "command-working-directory": "/",
- "http-methods": ["Post "],
- "include-command-output-in-response": true,
- "trigger-rule-mismatch-http-response-code": 400,
- "pass-environment-to-command":
- [
- {
- "source": "payload",
- "name": "head_commit.timestamp"
- }
- ],
- "pass-arguments-to-command":
- [
- {
- "source": "payload",
- "name": "head_commit.id"
- },
- {
- "source": "payload",
- "name": "head_commit.author.email"
- }
- ],
- "trigger-rule":
- {
- "and":
- [
- "or":
- [
- {
- "match":
- {
- "type": "payload-hmac-sha1",
- "secret": "mysecretFAIL",
- "parameter":
- {
- "source": "header",
- "name": "X-Hub-Signature"
- }
- }
- },
- {
- "match":
- {
- "type": "payload-hmac-sha1",
- "secret": "mysecret",
- "parameter":
- {
- "source": "header",
- "name": "X-Hub-Signature"
- }
- }
- }
- ],
- {
- "match":
- {
- "type": "value",
- "value": "refs/heads/master",
- "parameter":
- {
- "source": "payload",
- "name": "ref"
- }
- }
- }
- ]
- }
- },
{
"id": "bitbucket",
"execute-command": "{{ .Hookecho }}",
@@ -281,103 +137,6 @@
}
}
},
- {
- "id": "xml",
- "execute-command": "{{ .Hookecho }}",
- "command-working-directory": "/",
- "response-message": "success",
- "trigger-rule": {
- "and": [
- {
- "match": {
- "type": "value",
- "parameter": {
- "source": "payload",
- "name": "app.users.user.0.-name"
- },
- "value": "Jeff"
- }
- },
- {
- "match": {
- "type": "value",
- "parameter": {
- "source": "payload",
- "name": "app.messages.message.#text"
- },
- "value": "Hello!!"
- }
- },
- ],
- }
- },
- {
- "id": "txt-raw",
- "execute-command": "{{ .Hookecho }}",
- "command-working-directory": "/",
- "include-command-output-in-response": true,
- "pass-arguments-to-command": [
- {
- "source": "raw-request-body"
- }
- ]
- },
- {
- "id": "sendgrid",
- "execute-command": "{{ .Hookecho }}",
- "command-working-directory": "/",
- "response-message": "success",
- "trigger-rule": {
- "match": {
- "type": "value",
- "parameter": {
- "source": "payload",
- "name": "root.0.event"
- },
- "value": "processed"
- }
- }
- },
- {
- "id": "sendgrid/dir",
- "execute-command": "{{ .Hookecho }}",
- "command-working-directory": "/",
- "response-message": "success",
- "trigger-rule": {
- "match": {
- "type": "value",
- "parameter": {
- "source": "payload",
- "name": "root.0.event"
- },
- "value": "it worked!"
- }
- }
- },
- {
- "id": "plex",
- "execute-command": "{{ .Hookecho }}",
- "command-working-directory": "/",
- "response-message": "success",
- "parse-parameters-as-json": [
- {
- "source": "payload",
- "name": "payload"
- }
- ],
- "trigger-rule":
- {
- "match":
- {
- "type": "value",
- "parameter": {
- "source": "payload",
- "name": "payload.event"
- },
- "value": "media.play"
- }
- }
- },
{
"id": "capture-command-output-on-success-not-by-default",
"pass-arguments-to-command": [
@@ -422,21 +181,6 @@
"include-command-output-in-response": true,
"include-command-output-in-response-on-error": true
},
- {
- "id": "request-source",
- "pass-arguments-to-command": [
- {
- "source": "request",
- "name": "method"
- },
- {
- "source": "request",
- "name": "remote-addr"
- }
- ],
- "execute-command": "{{ .Hookecho }}",
- "include-command-output-in-response": true
- },
{
"id": "static-params-ok",
"execute-command": "{{ .Hookecho }}",
@@ -460,99 +204,5 @@
"name": "passed"
}
],
- },
- {
- "id": "issue-471",
- "execute-command": "{{ .Hookecho }}",
- "response-message": "success",
- "trigger-rule":
- {
- "or":
- [
- {
- "match":
- {
- "parameter":
- {
- "source": "payload",
- "name": "foo"
- },
- "type": "value",
- "value": "bar"
- }
- },
- {
- "match":
- {
- "parameter":
- {
- "source": "payload",
- "name": "exists"
- },
- "type": "value",
- "value": 1
- }
- }
- ]
- }
- },
- {
- "id": "issue-471-and",
- "execute-command": "{{ .Hookecho }}",
- "response-message": "success",
- "trigger-rule":
- {
- "and":
- [
- {
- "match":
- {
- "parameter":
- {
- "source": "payload",
- "name": "foo"
- },
- "type": "value",
- "value": "bar"
- }
- },
- {
- "match":
- {
- "parameter":
- {
- "source": "payload",
- "name": "exists"
- },
- "type": "value",
- "value": 1
- }
- }
- ]
- }
- },
- {
- "id": "empty-payload-signature",
- "execute-command": "{{ .Hookecho }}",
- "command-working-directory": "/",
- "include-command-output-in-response": true,
- "trigger-rule":
- {
- "and":
- [
- {
- "match":
- {
- "type": "payload-hmac-sha1",
- "secret": "mysecret",
- "parameter":
- {
- "source": "header",
- "name": "X-Hub-Signature"
- }
- }
- }
- ]
- }
}
]
diff --git a/test/hooks.yaml.tmpl b/test/hooks.yaml.tmpl
index b18c191..c2fffcd 100644
--- a/test/hooks.yaml.tmpl
+++ b/test/hooks.yaml.tmpl
@@ -1,6 +1,4 @@
- id: github
- http-methods:
- - "Post "
trigger-rule:
and:
- match:
@@ -8,82 +6,7 @@
source: header
name: X-Hub-Signature
secret: mysecret
- type: payload-hmac-sha1
- - match:
- parameter:
- source: payload
- name: ref
- type: value
- value: refs/heads/master
- include-command-output-in-response: true
- trigger-rule-mismatch-http-response-code: 400
- execute-command: '{{ .Hookecho }}'
- pass-arguments-to-command:
- - source: payload
- name: head_commit.id
- - source: payload
- name: head_commit.author.email
- pass-environment-to-command:
- - source: payload
- name: head_commit.timestamp
- command-working-directory: /
-
-- id: github-multi-sig
- http-methods:
- - "Post "
- trigger-rule:
- and:
- - or:
- - match:
- parameter:
- source: header
- name: X-Hub-Signature
- secret: mysecretFAIL
- type: payload-hmac-sha1
- - match:
- parameter:
- source: header
- name: X-Hub-Signature
- secret: mysecret
- type: payload-hmac-sha1
- - match:
- parameter:
- source: payload
- name: ref
- type: value
- value: refs/heads/master
- include-command-output-in-response: true
- trigger-rule-mismatch-http-response-code: 400
- trigger-signature-soft-failures: true
- execute-command: '{{ .Hookecho }}'
- pass-arguments-to-command:
- - source: payload
- name: head_commit.id
- - source: payload
- name: head_commit.author.email
- pass-environment-to-command:
- - source: payload
- name: head_commit.timestamp
- command-working-directory: /
-
-- id: github-multi-sig-fail
- http-methods:
- - "Post "
- trigger-rule:
- and:
- - or:
- - match:
- parameter:
- source: header
- name: X-Hub-Signature
- secret: mysecretFAIL
- type: payload-hmac-sha1
- - match:
- parameter:
- source: header
- name: X-Hub-Signature
- secret: mysecret
- type: payload-hmac-sha1
+ type: payload-hash-sha1
- match:
parameter:
source: payload
@@ -153,71 +76,6 @@
include-command-output-in-response: true
command-working-directory: /
-- id: xml
- execute-command: '{{ .Hookecho }}'
- command-working-directory: /
- response-message: success
- trigger-rule:
- and:
- - match:
- type: value
- parameter:
- source: payload
- name: app.users.user.0.-name
- value: Jeff
- - match:
- type: value
- parameter:
- source: payload
- name: "app.messages.message.#text"
- value: "Hello!!"
-
-- id: txt-raw
- execute-command: '{{ .Hookecho }}'
- command-working-directory: /
- include-command-output-in-response: true
- pass-arguments-to-command:
- - source: raw-request-body
-
-- id: sendgrid
- execute-command: '{{ .Hookecho }}'
- command-working-directory: /
- response-message: success
- trigger-rule:
- match:
- type: value
- parameter:
- source: payload
- name: root.0.event
- value: processed
-
-- id: sendgrid/dir
- execute-command: '{{ .Hookecho }}'
- command-working-directory: /
- response-message: success
- trigger-rule:
- match:
- type: value
- parameter:
- source: payload
- name: root.0.event
- value: it worked!
-
-- id: plex
- trigger-rule:
- match:
- type: value
- parameter:
- source: payload
- name: payload.event
- value: media.play
- parse-parameters-as-json:
- - source: payload
- name: payload
- execute-command: '{{ .Hookecho }}'
- response-message: success
- command-working-directory: /
-
- id: capture-command-output-on-success-not-by-default
pass-arguments-to-command:
- source: string
@@ -246,15 +104,6 @@
include-command-output-in-response: true
include-command-output-in-response-on-error: true
-- id: request-source
- pass-arguments-to-command:
- - source: request
- name: method
- - source: request
- name: remote-addr
- execute-command: '{{ .Hookecho }}'
- include-command-output-in-response: true
-
- id: static-params-ok
execute-command: '{{ .Hookecho }}'
include-command-output-in-response: true
@@ -264,53 +113,4 @@
- id: warn-on-space
execute-command: '{{ .Hookecho }} foo'
- include-command-output-in-response: true
-
-- id: issue-471
- execute-command: '{{ .Hookecho }}'
- response-message: success
- trigger-rule:
- or:
- - match:
- parameter:
- source: payload
- name: foo
- type: value
- value: bar
- - match:
- parameter:
- source: payload
- name: exists
- type: value
- value: 1
-
-- id: issue-471-and
- execute-command: '{{ .Hookecho }}'
- response-message: success
- trigger-rule:
- and:
- - match:
- parameter:
- source: payload
- name: foo
- type: value
- value: bar
- - match:
- parameter:
- source: payload
- name: exists
- type: value
- value: 1
-
-- id: empty-payload-signature
- include-command-output-in-response: true
- execute-command: '{{ .Hookecho }}'
- command-working-directory: /
- trigger-rule:
- and:
- - match:
- parameter:
- source: header
- name: X-Hub-Signature
- secret: mysecret
- type: payload-hmac-sha1
+ include-command-output-in-response: true
\ No newline at end of file
diff --git a/testutils.go b/testutils.go
deleted file mode 100644
index 8253483..0000000
--- a/testutils.go
+++ /dev/null
@@ -1,30 +0,0 @@
-//go:build !windows
-// +build !windows
-
-package main
-
-import (
- "context"
- "io/ioutil"
- "net"
- "net/http"
- "os"
- "path"
-)
-
-func prepareTestSocket(_ string) (socketPath string, transport *http.Transport, cleanup func(), err error) {
- tmp, err := ioutil.TempDir("", "webhook-socket-")
- if err != nil {
- return "", nil, nil, err
- }
- cleanup = func() { os.RemoveAll(tmp) }
- socketPath = path.Join(tmp, "webhook.sock")
- socketDialer := &net.Dialer{}
- transport = &http.Transport{
- DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
- return socketDialer.DialContext(ctx, "unix", socketPath)
- },
- }
-
- return socketPath, transport, cleanup, nil
-}
diff --git a/testutils_windows.go b/testutils_windows.go
deleted file mode 100644
index a0aebf9..0000000
--- a/testutils_windows.go
+++ /dev/null
@@ -1,22 +0,0 @@
-//go:build windows
-// +build windows
-
-package main
-
-import (
- "context"
- "github.com/Microsoft/go-winio"
- "net"
- "net/http"
-)
-
-func prepareTestSocket(hookTmpl string) (socketPath string, transport *http.Transport, cleanup func(), err error) {
- socketPath = "\\\\.\\pipe\\webhook-" + hookTmpl
- transport = &http.Transport{
- DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
- return winio.DialPipeContext(ctx, socketPath)
- },
- }
-
- return socketPath, transport, nil, nil
-}
diff --git a/tls.go b/tls.go
deleted file mode 100644
index 526fd36..0000000
--- a/tls.go
+++ /dev/null
@@ -1,85 +0,0 @@
-package main
-
-import (
- "crypto/tls"
- "io"
- "log"
- "strings"
-)
-
-func writeTLSSupportedCipherStrings(w io.Writer, min uint16) error {
- for _, c := range tls.CipherSuites() {
- var found bool
-
- for _, v := range c.SupportedVersions {
- if v >= min {
- found = true
- }
- }
-
- if !found {
- continue
- }
-
- _, err := w.Write([]byte(c.Name + "\n"))
- if err != nil {
- return err
- }
- }
-
- return nil
-}
-
-// getTLSMinVersion converts a version string into a TLS version ID.
-func getTLSMinVersion(v string) uint16 {
- switch v {
- case "1.0":
- return tls.VersionTLS10
- case "1.1":
- return tls.VersionTLS11
- case "1.2", "":
- return tls.VersionTLS12
- case "1.3":
- return tls.VersionTLS13
- default:
- log.Fatalln("error: unknown minimum TLS version:", v)
- return 0
- }
-}
-
-// getTLSCipherSuites converts a comma separated list of cipher suites into a
-// slice of TLS cipher suite IDs.
-func getTLSCipherSuites(v string) []uint16 {
- supported := tls.CipherSuites()
-
- if v == "" {
- suites := make([]uint16, len(supported))
-
- for _, cs := range supported {
- suites = append(suites, cs.ID)
- }
-
- return suites
- }
-
- var found bool
- txts := strings.Split(v, ",")
- suites := make([]uint16, len(txts))
-
- for _, want := range txts {
- found = false
-
- for _, cs := range supported {
- if want == cs.Name {
- suites = append(suites, cs.ID)
- found = true
- }
- }
-
- if !found {
- log.Fatalln("error: unknown TLS cipher suite:", want)
- }
- }
-
- return suites
-}
diff --git a/vendor/github.com/Microsoft/go-winio/.gitattributes b/vendor/github.com/Microsoft/go-winio/.gitattributes
deleted file mode 100644
index 94f480d..0000000
--- a/vendor/github.com/Microsoft/go-winio/.gitattributes
+++ /dev/null
@@ -1 +0,0 @@
-* text=auto eol=lf
\ No newline at end of file
diff --git a/vendor/github.com/Microsoft/go-winio/.gitignore b/vendor/github.com/Microsoft/go-winio/.gitignore
deleted file mode 100644
index 815e206..0000000
--- a/vendor/github.com/Microsoft/go-winio/.gitignore
+++ /dev/null
@@ -1,10 +0,0 @@
-.vscode/
-
-*.exe
-
-# testing
-testdata
-
-# go workspaces
-go.work
-go.work.sum
diff --git a/vendor/github.com/Microsoft/go-winio/.golangci.yml b/vendor/github.com/Microsoft/go-winio/.golangci.yml
deleted file mode 100644
index faedfe9..0000000
--- a/vendor/github.com/Microsoft/go-winio/.golangci.yml
+++ /dev/null
@@ -1,147 +0,0 @@
-linters:
- enable:
- # style
- - containedctx # struct contains a context
- - dupl # duplicate code
- - errname # erorrs are named correctly
- - nolintlint # "//nolint" directives are properly explained
- - revive # golint replacement
- - unconvert # unnecessary conversions
- - wastedassign
-
- # bugs, performance, unused, etc ...
- - contextcheck # function uses a non-inherited context
- - errorlint # errors not wrapped for 1.13
- - exhaustive # check exhaustiveness of enum switch statements
- - gofmt # files are gofmt'ed
- - gosec # security
- - nilerr # returns nil even with non-nil error
- - thelper # test helpers without t.Helper()
- - unparam # unused function params
-
-issues:
- exclude-dirs:
- - pkg/etw/sample
-
- exclude-rules:
- # err is very often shadowed in nested scopes
- - linters:
- - govet
- text: '^shadow: declaration of "err" shadows declaration'
-
- # ignore long lines for skip autogen directives
- - linters:
- - revive
- text: "^line-length-limit: "
- source: "^//(go:generate|sys) "
-
- #TODO: remove after upgrading to go1.18
- # ignore comment spacing for nolint and sys directives
- - linters:
- - revive
- text: "^comment-spacings: no space between comment delimiter and comment text"
- source: "//(cspell:|nolint:|sys |todo)"
-
- # not on go 1.18 yet, so no any
- - linters:
- - revive
- text: "^use-any: since GO 1.18 'interface{}' can be replaced by 'any'"
-
- # allow unjustified ignores of error checks in defer statements
- - linters:
- - nolintlint
- text: "^directive `//nolint:errcheck` should provide explanation"
- source: '^\s*defer '
-
- # allow unjustified ignores of error lints for io.EOF
- - linters:
- - nolintlint
- text: "^directive `//nolint:errorlint` should provide explanation"
- source: '[=|!]= io.EOF'
-
-
-linters-settings:
- exhaustive:
- default-signifies-exhaustive: true
- govet:
- enable-all: true
- disable:
- # struct order is often for Win32 compat
- # also, ignore pointer bytes/GC issues for now until performance becomes an issue
- - fieldalignment
- nolintlint:
- require-explanation: true
- require-specific: true
- revive:
- # revive is more configurable than static check, so likely the preferred alternative to static-check
- # (once the perf issue is solved: https://github.com/golangci/golangci-lint/issues/2997)
- enable-all-rules:
- true
- # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md
- rules:
- # rules with required arguments
- - name: argument-limit
- disabled: true
- - name: banned-characters
- disabled: true
- - name: cognitive-complexity
- disabled: true
- - name: cyclomatic
- disabled: true
- - name: file-header
- disabled: true
- - name: function-length
- disabled: true
- - name: function-result-limit
- disabled: true
- - name: max-public-structs
- disabled: true
- # geneally annoying rules
- - name: add-constant # complains about any and all strings and integers
- disabled: true
- - name: confusing-naming # we frequently use "Foo()" and "foo()" together
- disabled: true
- - name: flag-parameter # excessive, and a common idiom we use
- disabled: true
- - name: unhandled-error # warns over common fmt.Print* and io.Close; rely on errcheck instead
- disabled: true
- # general config
- - name: line-length-limit
- arguments:
- - 140
- - name: var-naming
- arguments:
- - []
- - - CID
- - CRI
- - CTRD
- - DACL
- - DLL
- - DOS
- - ETW
- - FSCTL
- - GCS
- - GMSA
- - HCS
- - HV
- - IO
- - LCOW
- - LDAP
- - LPAC
- - LTSC
- - MMIO
- - NT
- - OCI
- - PMEM
- - PWSH
- - RX
- - SACl
- - SID
- - SMB
- - TX
- - VHD
- - VHDX
- - VMID
- - VPCI
- - WCOW
- - WIM
diff --git a/vendor/github.com/Microsoft/go-winio/CODEOWNERS b/vendor/github.com/Microsoft/go-winio/CODEOWNERS
deleted file mode 100644
index ae1b494..0000000
--- a/vendor/github.com/Microsoft/go-winio/CODEOWNERS
+++ /dev/null
@@ -1 +0,0 @@
- * @microsoft/containerplat
diff --git a/vendor/github.com/Microsoft/go-winio/README.md b/vendor/github.com/Microsoft/go-winio/README.md
deleted file mode 100644
index 7474b4f..0000000
--- a/vendor/github.com/Microsoft/go-winio/README.md
+++ /dev/null
@@ -1,89 +0,0 @@
-# go-winio [](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
-for using named pipes as a net transport.
-
-This code relies on IO completion ports to avoid blocking IO on system threads, allowing Go
-to reuse the thread to schedule another goroutine. This limits support to Windows Vista and
-newer operating systems. This is similar to the implementation of network sockets in Go's net
-package.
-
-Please see the LICENSE file for licensing information.
-
-## Contributing
-
-This project welcomes contributions and suggestions.
-Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that
-you have the right to, and actually do, grant us the rights to use your contribution.
-For details, visit [Microsoft CLA](https://cla.microsoft.com).
-
-When you submit a pull request, a CLA-bot will automatically determine whether you need to
-provide a CLA and decorate the PR appropriately (e.g., label, comment).
-Simply follow the instructions provided by the bot.
-You will only need to do this once across all repos using our CLA.
-
-Additionally, the pull request pipeline requires the following steps to be performed before
-mergining.
-
-### Code Sign-Off
-
-We require that contributors sign their commits using [`git commit --signoff`][git-commit-s]
-to certify they either authored the work themselves or otherwise have permission to use it in this project.
-
-A range of commits can be signed off using [`git rebase --signoff`][git-rebase-s].
-
-Please see [the developer certificate](https://developercertificate.org) for more info,
-as well as to make sure that you can attest to the rules listed.
-Our CI uses the DCO Github app to ensure that all commits in a given PR are signed-off.
-
-### Linting
-
-Code must pass a linting stage, which uses [`golangci-lint`][lint].
-The linting settings are stored in [`.golangci.yaml`](./.golangci.yaml), and can be run
-automatically with VSCode by adding the following to your workspace or folder settings:
-
-```json
- "go.lintTool": "golangci-lint",
- "go.lintOnSave": "package",
-```
-
-Additional editor [integrations options are also available][lint-ide].
-
-Alternatively, `golangci-lint` can be [installed locally][lint-install] and run from the repo root:
-
-```shell
-# use . or specify a path to only lint a package
-# to show all lint errors, use flags "--max-issues-per-linter=0 --max-same-issues=0"
-> golangci-lint run ./...
-```
-
-### Go Generate
-
-The pipeline checks that auto-generated code, via `go generate`, are up to date.
-
-This can be done for the entire repo:
-
-```shell
-> go generate ./...
-```
-
-## Code of Conduct
-
-This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
-For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
-contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
-
-## Special Thanks
-
-Thanks to [natefinch][natefinch] for the inspiration for this library.
-See [npipe](https://github.com/natefinch/npipe) for another named pipe implementation.
-
-[lint]: https://golangci-lint.run/
-[lint-ide]: https://golangci-lint.run/usage/integrations/#editor-integration
-[lint-install]: https://golangci-lint.run/usage/install/#local-installation
-
-[git-commit-s]: https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s
-[git-rebase-s]: https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---signoff
-
-[natefinch]: https://github.com/natefinch
diff --git a/vendor/github.com/Microsoft/go-winio/SECURITY.md b/vendor/github.com/Microsoft/go-winio/SECURITY.md
deleted file mode 100644
index 869fdfe..0000000
--- a/vendor/github.com/Microsoft/go-winio/SECURITY.md
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-## Security
-
-Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
-
-If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.
-
-## Reporting Security Issues
-
-**Please do not report security vulnerabilities through public GitHub issues.**
-
-Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).
-
-If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).
-
-You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc).
-
-Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
-
- * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
- * Full paths of source file(s) related to the manifestation of the issue
- * The location of the affected source code (tag/branch/commit or direct URL)
- * Any special configuration required to reproduce the issue
- * Step-by-step instructions to reproduce the issue
- * Proof-of-concept or exploit code (if possible)
- * Impact of the issue, including how an attacker might exploit the issue
-
-This information will help us triage your report more quickly.
-
-If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.
-
-## Preferred Languages
-
-We prefer all communications to be in English.
-
-## Policy
-
-Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).
-
-
diff --git a/vendor/github.com/Microsoft/go-winio/backup.go b/vendor/github.com/Microsoft/go-winio/backup.go
deleted file mode 100644
index b54341d..0000000
--- a/vendor/github.com/Microsoft/go-winio/backup.go
+++ /dev/null
@@ -1,287 +0,0 @@
-//go:build windows
-// +build windows
-
-package winio
-
-import (
- "encoding/binary"
- "errors"
- "fmt"
- "io"
- "os"
- "runtime"
- "unicode/utf16"
-
- "github.com/Microsoft/go-winio/internal/fs"
- "golang.org/x/sys/windows"
-)
-
-//sys backupRead(h windows.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead
-//sys backupWrite(h windows.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite
-
-const (
- BackupData = uint32(iota + 1)
- BackupEaData
- BackupSecurity
- BackupAlternateData
- BackupLink
- BackupPropertyData
- BackupObjectId //revive:disable-line:var-naming ID, not Id
- BackupReparseData
- BackupSparseBlock
- BackupTxfsData
-)
-
-const (
- StreamSparseAttributes = uint32(8)
-)
-
-//nolint:revive // var-naming: ALL_CAPS
-const (
- WRITE_DAC = windows.WRITE_DAC
- WRITE_OWNER = windows.WRITE_OWNER
- ACCESS_SYSTEM_SECURITY = windows.ACCESS_SYSTEM_SECURITY
-)
-
-// BackupHeader represents a backup stream of a file.
-type BackupHeader struct {
- //revive:disable-next-line:var-naming ID, not Id
- Id uint32 // The backup stream ID
- Attributes uint32 // Stream attributes
- Size int64 // The size of the stream in bytes
- Name string // The name of the stream (for BackupAlternateData only).
- Offset int64 // The offset of the stream in the file (for BackupSparseBlock only).
-}
-
-type win32StreamID struct {
- StreamID uint32
- Attributes uint32
- Size uint64
- NameSize uint32
-}
-
-// BackupStreamReader reads from a stream produced by the BackupRead Win32 API and produces a series
-// of BackupHeader values.
-type BackupStreamReader struct {
- r io.Reader
- bytesLeft int64
-}
-
-// NewBackupStreamReader produces a BackupStreamReader from any io.Reader.
-func NewBackupStreamReader(r io.Reader) *BackupStreamReader {
- return &BackupStreamReader{r, 0}
-}
-
-// Next returns the next backup stream and prepares for calls to Read(). It skips the remainder of the current stream if
-// it was not completely read.
-func (r *BackupStreamReader) Next() (*BackupHeader, error) {
- if r.bytesLeft > 0 { //nolint:nestif // todo: flatten this
- if s, ok := r.r.(io.Seeker); ok {
- // Make sure Seek on io.SeekCurrent sometimes succeeds
- // before trying the actual seek.
- if _, err := s.Seek(0, io.SeekCurrent); err == nil {
- if _, err = s.Seek(r.bytesLeft, io.SeekCurrent); err != nil {
- return nil, err
- }
- r.bytesLeft = 0
- }
- }
- if _, err := io.Copy(io.Discard, r); err != nil {
- return nil, err
- }
- }
- var wsi win32StreamID
- if err := binary.Read(r.r, binary.LittleEndian, &wsi); err != nil {
- return nil, err
- }
- hdr := &BackupHeader{
- Id: wsi.StreamID,
- Attributes: wsi.Attributes,
- Size: int64(wsi.Size),
- }
- if wsi.NameSize != 0 {
- name := make([]uint16, int(wsi.NameSize/2))
- if err := binary.Read(r.r, binary.LittleEndian, name); err != nil {
- return nil, err
- }
- hdr.Name = windows.UTF16ToString(name)
- }
- if wsi.StreamID == BackupSparseBlock {
- if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil {
- return nil, err
- }
- hdr.Size -= 8
- }
- r.bytesLeft = hdr.Size
- return hdr, nil
-}
-
-// Read reads from the current backup stream.
-func (r *BackupStreamReader) Read(b []byte) (int, error) {
- if r.bytesLeft == 0 {
- return 0, io.EOF
- }
- if int64(len(b)) > r.bytesLeft {
- b = b[:r.bytesLeft]
- }
- n, err := r.r.Read(b)
- r.bytesLeft -= int64(n)
- if err == io.EOF {
- err = io.ErrUnexpectedEOF
- } else if r.bytesLeft == 0 && err == nil {
- err = io.EOF
- }
- return n, err
-}
-
-// BackupStreamWriter writes a stream compatible with the BackupWrite Win32 API.
-type BackupStreamWriter struct {
- w io.Writer
- bytesLeft int64
-}
-
-// NewBackupStreamWriter produces a BackupStreamWriter on top of an io.Writer.
-func NewBackupStreamWriter(w io.Writer) *BackupStreamWriter {
- return &BackupStreamWriter{w, 0}
-}
-
-// WriteHeader writes the next backup stream header and prepares for calls to Write().
-func (w *BackupStreamWriter) WriteHeader(hdr *BackupHeader) error {
- if w.bytesLeft != 0 {
- return fmt.Errorf("missing %d bytes", w.bytesLeft)
- }
- name := utf16.Encode([]rune(hdr.Name))
- wsi := win32StreamID{
- StreamID: hdr.Id,
- Attributes: hdr.Attributes,
- Size: uint64(hdr.Size),
- NameSize: uint32(len(name) * 2),
- }
- if hdr.Id == BackupSparseBlock {
- // Include space for the int64 block offset
- wsi.Size += 8
- }
- if err := binary.Write(w.w, binary.LittleEndian, &wsi); err != nil {
- return err
- }
- if len(name) != 0 {
- if err := binary.Write(w.w, binary.LittleEndian, name); err != nil {
- return err
- }
- }
- if hdr.Id == BackupSparseBlock {
- if err := binary.Write(w.w, binary.LittleEndian, hdr.Offset); err != nil {
- return err
- }
- }
- w.bytesLeft = hdr.Size
- return nil
-}
-
-// Write writes to the current backup stream.
-func (w *BackupStreamWriter) Write(b []byte) (int, error) {
- if w.bytesLeft < int64(len(b)) {
- return 0, fmt.Errorf("too many bytes by %d", int64(len(b))-w.bytesLeft)
- }
- n, err := w.w.Write(b)
- w.bytesLeft -= int64(n)
- return n, err
-}
-
-// BackupFileReader provides an io.ReadCloser interface on top of the BackupRead Win32 API.
-type BackupFileReader struct {
- f *os.File
- includeSecurity bool
- ctx uintptr
-}
-
-// NewBackupFileReader returns a new BackupFileReader from a file handle. If includeSecurity is true,
-// Read will attempt to read the security descriptor of the file.
-func NewBackupFileReader(f *os.File, includeSecurity bool) *BackupFileReader {
- r := &BackupFileReader{f, includeSecurity, 0}
- return r
-}
-
-// Read reads a backup stream from the file by calling the Win32 API BackupRead().
-func (r *BackupFileReader) Read(b []byte) (int, error) {
- var bytesRead uint32
- err := backupRead(windows.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx)
- if err != nil {
- return 0, &os.PathError{Op: "BackupRead", Path: r.f.Name(), Err: err}
- }
- runtime.KeepAlive(r.f)
- if bytesRead == 0 {
- return 0, io.EOF
- }
- return int(bytesRead), nil
-}
-
-// Close frees Win32 resources associated with the BackupFileReader. It does not close
-// the underlying file.
-func (r *BackupFileReader) Close() error {
- if r.ctx != 0 {
- _ = backupRead(windows.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx)
- runtime.KeepAlive(r.f)
- r.ctx = 0
- }
- return nil
-}
-
-// BackupFileWriter provides an io.WriteCloser interface on top of the BackupWrite Win32 API.
-type BackupFileWriter struct {
- f *os.File
- includeSecurity bool
- ctx uintptr
-}
-
-// NewBackupFileWriter returns a new BackupFileWriter from a file handle. If includeSecurity is true,
-// Write() will attempt to restore the security descriptor from the stream.
-func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter {
- w := &BackupFileWriter{f, includeSecurity, 0}
- return w
-}
-
-// Write restores a portion of the file using the provided backup stream.
-func (w *BackupFileWriter) Write(b []byte) (int, error) {
- var bytesWritten uint32
- err := backupWrite(windows.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx)
- if err != nil {
- return 0, &os.PathError{Op: "BackupWrite", Path: w.f.Name(), Err: err}
- }
- runtime.KeepAlive(w.f)
- if int(bytesWritten) != len(b) {
- return int(bytesWritten), errors.New("not all bytes could be written")
- }
- return len(b), nil
-}
-
-// Close frees Win32 resources associated with the BackupFileWriter. It does not
-// close the underlying file.
-func (w *BackupFileWriter) Close() error {
- if w.ctx != 0 {
- _ = backupWrite(windows.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx)
- runtime.KeepAlive(w.f)
- w.ctx = 0
- }
- return nil
-}
-
-// OpenForBackup opens a file or directory, potentially skipping access checks if the backup
-// or restore privileges have been acquired.
-//
-// If the file opened was a directory, it cannot be used with Readdir().
-func OpenForBackup(path string, access uint32, share uint32, createmode uint32) (*os.File, error) {
- h, err := fs.CreateFile(path,
- fs.AccessMask(access),
- fs.FileShareMode(share),
- nil,
- fs.FileCreationDisposition(createmode),
- fs.FILE_FLAG_BACKUP_SEMANTICS|fs.FILE_FLAG_OPEN_REPARSE_POINT,
- 0,
- )
- if err != nil {
- err = &os.PathError{Op: "open", Path: path, Err: err}
- return nil, err
- }
- return os.NewFile(uintptr(h), path), nil
-}
diff --git a/vendor/github.com/Microsoft/go-winio/doc.go b/vendor/github.com/Microsoft/go-winio/doc.go
deleted file mode 100644
index 1f5bfe2..0000000
--- a/vendor/github.com/Microsoft/go-winio/doc.go
+++ /dev/null
@@ -1,22 +0,0 @@
-// This package provides utilities for efficiently performing Win32 IO operations in Go.
-// Currently, this package is provides support for genreal IO and management of
-// - named pipes
-// - files
-// - [Hyper-V sockets]
-//
-// This code is similar to Go's [net] package, and uses IO completion ports to avoid
-// blocking IO on system threads, allowing Go to reuse the thread to schedule other goroutines.
-//
-// This limits support to Windows Vista and newer operating systems.
-//
-// Additionally, this package provides support for:
-// - creating and managing GUIDs
-// - writing to [ETW]
-// - opening and manageing VHDs
-// - parsing [Windows Image files]
-// - auto-generating Win32 API code
-//
-// [Hyper-V sockets]: https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/make-integration-service
-// [ETW]: https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/event-tracing-for-windows--etw-
-// [Windows Image files]: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/work-with-windows-images
-package winio
diff --git a/vendor/github.com/Microsoft/go-winio/ea.go b/vendor/github.com/Microsoft/go-winio/ea.go
deleted file mode 100644
index e104dbd..0000000
--- a/vendor/github.com/Microsoft/go-winio/ea.go
+++ /dev/null
@@ -1,137 +0,0 @@
-package winio
-
-import (
- "bytes"
- "encoding/binary"
- "errors"
-)
-
-type fileFullEaInformation struct {
- NextEntryOffset uint32
- Flags uint8
- NameLength uint8
- ValueLength uint16
-}
-
-var (
- fileFullEaInformationSize = binary.Size(&fileFullEaInformation{})
-
- errInvalidEaBuffer = errors.New("invalid extended attribute buffer")
- errEaNameTooLarge = errors.New("extended attribute name too large")
- errEaValueTooLarge = errors.New("extended attribute value too large")
-)
-
-// ExtendedAttribute represents a single Windows EA.
-type ExtendedAttribute struct {
- Name string
- Value []byte
- Flags uint8
-}
-
-func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) {
- var info fileFullEaInformation
- err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info)
- if err != nil {
- err = errInvalidEaBuffer
- return ea, nb, err
- }
-
- nameOffset := fileFullEaInformationSize
- nameLen := int(info.NameLength)
- valueOffset := nameOffset + int(info.NameLength) + 1
- valueLen := int(info.ValueLength)
- nextOffset := int(info.NextEntryOffset)
- if valueLen+valueOffset > len(b) || nextOffset < 0 || nextOffset > len(b) {
- err = errInvalidEaBuffer
- return ea, nb, err
- }
-
- ea.Name = string(b[nameOffset : nameOffset+nameLen])
- ea.Value = b[valueOffset : valueOffset+valueLen]
- ea.Flags = info.Flags
- if info.NextEntryOffset != 0 {
- nb = b[info.NextEntryOffset:]
- }
- return ea, nb, err
-}
-
-// DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION
-// buffer retrieved from BackupRead, ZwQueryEaFile, etc.
-func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) {
- for len(b) != 0 {
- ea, nb, err := parseEa(b)
- if err != nil {
- return nil, err
- }
-
- eas = append(eas, ea)
- b = nb
- }
- return eas, err
-}
-
-func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error {
- if int(uint8(len(ea.Name))) != len(ea.Name) {
- return errEaNameTooLarge
- }
- if int(uint16(len(ea.Value))) != len(ea.Value) {
- return errEaValueTooLarge
- }
- entrySize := uint32(fileFullEaInformationSize + len(ea.Name) + 1 + len(ea.Value))
- withPadding := (entrySize + 3) &^ 3
- nextOffset := uint32(0)
- if !last {
- nextOffset = withPadding
- }
- info := fileFullEaInformation{
- NextEntryOffset: nextOffset,
- Flags: ea.Flags,
- NameLength: uint8(len(ea.Name)),
- ValueLength: uint16(len(ea.Value)),
- }
-
- err := binary.Write(buf, binary.LittleEndian, &info)
- if err != nil {
- return err
- }
-
- _, err = buf.Write([]byte(ea.Name))
- if err != nil {
- return err
- }
-
- err = buf.WriteByte(0)
- if err != nil {
- return err
- }
-
- _, err = buf.Write(ea.Value)
- if err != nil {
- return err
- }
-
- _, err = buf.Write([]byte{0, 0, 0}[0 : withPadding-entrySize])
- if err != nil {
- return err
- }
-
- return nil
-}
-
-// EncodeExtendedAttributes encodes a list of EAs into a FILE_FULL_EA_INFORMATION
-// buffer for use with BackupWrite, ZwSetEaFile, etc.
-func EncodeExtendedAttributes(eas []ExtendedAttribute) ([]byte, error) {
- var buf bytes.Buffer
- for i := range eas {
- last := false
- if i == len(eas)-1 {
- last = true
- }
-
- err := writeEa(&buf, &eas[i], last)
- if err != nil {
- return nil, err
- }
- }
- return buf.Bytes(), nil
-}
diff --git a/vendor/github.com/Microsoft/go-winio/file.go b/vendor/github.com/Microsoft/go-winio/file.go
deleted file mode 100644
index fe82a18..0000000
--- a/vendor/github.com/Microsoft/go-winio/file.go
+++ /dev/null
@@ -1,320 +0,0 @@
-//go:build windows
-// +build windows
-
-package winio
-
-import (
- "errors"
- "io"
- "runtime"
- "sync"
- "sync/atomic"
- "syscall"
- "time"
-
- "golang.org/x/sys/windows"
-)
-
-//sys cancelIoEx(file windows.Handle, o *windows.Overlapped) (err error) = CancelIoEx
-//sys createIoCompletionPort(file windows.Handle, port windows.Handle, key uintptr, threadCount uint32) (newport windows.Handle, err error) = CreateIoCompletionPort
-//sys getQueuedCompletionStatus(port windows.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus
-//sys setFileCompletionNotificationModes(h windows.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
-//sys wsaGetOverlappedResult(h windows.Handle, o *windows.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) = ws2_32.WSAGetOverlappedResult
-
-var (
- ErrFileClosed = errors.New("file has already been closed")
- ErrTimeout = &timeoutError{}
-)
-
-type timeoutError struct{}
-
-func (*timeoutError) Error() string { return "i/o timeout" }
-func (*timeoutError) Timeout() bool { return true }
-func (*timeoutError) Temporary() bool { return true }
-
-type timeoutChan chan struct{}
-
-var ioInitOnce sync.Once
-var ioCompletionPort windows.Handle
-
-// ioResult contains the result of an asynchronous IO operation.
-type ioResult struct {
- bytes uint32
- err error
-}
-
-// ioOperation represents an outstanding asynchronous Win32 IO.
-type ioOperation struct {
- o windows.Overlapped
- ch chan ioResult
-}
-
-func initIO() {
- h, err := createIoCompletionPort(windows.InvalidHandle, 0, 0, 0xffffffff)
- if err != nil {
- panic(err)
- }
- ioCompletionPort = h
- go ioCompletionProcessor(h)
-}
-
-// win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall.
-// It takes ownership of this handle and will close it if it is garbage collected.
-type win32File struct {
- handle windows.Handle
- wg sync.WaitGroup
- wgLock sync.RWMutex
- closing atomic.Bool
- socket bool
- readDeadline deadlineHandler
- writeDeadline deadlineHandler
-}
-
-type deadlineHandler struct {
- setLock sync.Mutex
- channel timeoutChan
- channelLock sync.RWMutex
- timer *time.Timer
- timedout atomic.Bool
-}
-
-// makeWin32File makes a new win32File from an existing file handle.
-func makeWin32File(h windows.Handle) (*win32File, error) {
- f := &win32File{handle: h}
- ioInitOnce.Do(initIO)
- _, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff)
- if err != nil {
- return nil, err
- }
- err = setFileCompletionNotificationModes(h, windows.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS|windows.FILE_SKIP_SET_EVENT_ON_HANDLE)
- if err != nil {
- return nil, err
- }
- f.readDeadline.channel = make(timeoutChan)
- f.writeDeadline.channel = make(timeoutChan)
- return f, nil
-}
-
-// Deprecated: use NewOpenFile instead.
-func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
- return NewOpenFile(windows.Handle(h))
-}
-
-func NewOpenFile(h windows.Handle) (io.ReadWriteCloser, error) {
- // If we return the result of makeWin32File directly, it can result in an
- // interface-wrapped nil, rather than a nil interface value.
- f, err := makeWin32File(h)
- if err != nil {
- return nil, err
- }
- return f, nil
-}
-
-// closeHandle closes the resources associated with a Win32 handle.
-func (f *win32File) closeHandle() {
- f.wgLock.Lock()
- // Atomically set that we are closing, releasing the resources only once.
- if !f.closing.Swap(true) {
- f.wgLock.Unlock()
- // cancel all IO and wait for it to complete
- _ = cancelIoEx(f.handle, nil)
- f.wg.Wait()
- // at this point, no new IO can start
- windows.Close(f.handle)
- f.handle = 0
- } else {
- f.wgLock.Unlock()
- }
-}
-
-// Close closes a win32File.
-func (f *win32File) Close() error {
- f.closeHandle()
- return nil
-}
-
-// IsClosed checks if the file has been closed.
-func (f *win32File) IsClosed() bool {
- return f.closing.Load()
-}
-
-// prepareIO prepares for a new IO operation.
-// The caller must call f.wg.Done() when the IO is finished, prior to Close() returning.
-func (f *win32File) prepareIO() (*ioOperation, error) {
- f.wgLock.RLock()
- if f.closing.Load() {
- f.wgLock.RUnlock()
- return nil, ErrFileClosed
- }
- f.wg.Add(1)
- f.wgLock.RUnlock()
- c := &ioOperation{}
- c.ch = make(chan ioResult)
- return c, nil
-}
-
-// ioCompletionProcessor processes completed async IOs forever.
-func ioCompletionProcessor(h windows.Handle) {
- for {
- var bytes uint32
- var key uintptr
- var op *ioOperation
- err := getQueuedCompletionStatus(h, &bytes, &key, &op, windows.INFINITE)
- if op == nil {
- panic(err)
- }
- op.ch <- ioResult{bytes, err}
- }
-}
-
-// todo: helsaawy - create an asyncIO version that takes a context
-
-// asyncIO processes the return value from ReadFile or WriteFile, blocking until
-// the operation has actually completed.
-func (f *win32File) asyncIO(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) {
- if err != windows.ERROR_IO_PENDING { //nolint:errorlint // err is Errno
- return int(bytes), err
- }
-
- if f.closing.Load() {
- _ = cancelIoEx(f.handle, &c.o)
- }
-
- var timeout timeoutChan
- if d != nil {
- d.channelLock.Lock()
- timeout = d.channel
- d.channelLock.Unlock()
- }
-
- var r ioResult
- select {
- case r = <-c.ch:
- err = r.err
- if err == windows.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno
- if f.closing.Load() {
- err = ErrFileClosed
- }
- } else if err != nil && f.socket {
- // err is from Win32. Query the overlapped structure to get the winsock error.
- var bytes, flags uint32
- err = wsaGetOverlappedResult(f.handle, &c.o, &bytes, false, &flags)
- }
- case <-timeout:
- _ = cancelIoEx(f.handle, &c.o)
- r = <-c.ch
- err = r.err
- if err == windows.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno
- err = ErrTimeout
- }
- }
-
- // runtime.KeepAlive is needed, as c is passed via native
- // code to ioCompletionProcessor, c must remain alive
- // until the channel read is complete.
- // todo: (de)allocate *ioOperation via win32 heap functions, instead of needing to KeepAlive?
- runtime.KeepAlive(c)
- return int(r.bytes), err
-}
-
-// Read reads from a file handle.
-func (f *win32File) Read(b []byte) (int, error) {
- c, err := f.prepareIO()
- if err != nil {
- return 0, err
- }
- defer f.wg.Done()
-
- if f.readDeadline.timedout.Load() {
- return 0, ErrTimeout
- }
-
- var bytes uint32
- err = windows.ReadFile(f.handle, b, &bytes, &c.o)
- n, err := f.asyncIO(c, &f.readDeadline, bytes, err)
- runtime.KeepAlive(b)
-
- // Handle EOF conditions.
- if err == nil && n == 0 && len(b) != 0 {
- return 0, io.EOF
- } else if err == windows.ERROR_BROKEN_PIPE { //nolint:errorlint // err is Errno
- return 0, io.EOF
- }
- return n, err
-}
-
-// Write writes to a file handle.
-func (f *win32File) Write(b []byte) (int, error) {
- c, err := f.prepareIO()
- if err != nil {
- return 0, err
- }
- defer f.wg.Done()
-
- if f.writeDeadline.timedout.Load() {
- return 0, ErrTimeout
- }
-
- var bytes uint32
- err = windows.WriteFile(f.handle, b, &bytes, &c.o)
- n, err := f.asyncIO(c, &f.writeDeadline, bytes, err)
- runtime.KeepAlive(b)
- return n, err
-}
-
-func (f *win32File) SetReadDeadline(deadline time.Time) error {
- return f.readDeadline.set(deadline)
-}
-
-func (f *win32File) SetWriteDeadline(deadline time.Time) error {
- return f.writeDeadline.set(deadline)
-}
-
-func (f *win32File) Flush() error {
- return windows.FlushFileBuffers(f.handle)
-}
-
-func (f *win32File) Fd() uintptr {
- return uintptr(f.handle)
-}
-
-func (d *deadlineHandler) set(deadline time.Time) error {
- d.setLock.Lock()
- defer d.setLock.Unlock()
-
- if d.timer != nil {
- if !d.timer.Stop() {
- <-d.channel
- }
- d.timer = nil
- }
- d.timedout.Store(false)
-
- select {
- case <-d.channel:
- d.channelLock.Lock()
- d.channel = make(chan struct{})
- d.channelLock.Unlock()
- default:
- }
-
- if deadline.IsZero() {
- return nil
- }
-
- timeoutIO := func() {
- d.timedout.Store(true)
- close(d.channel)
- }
-
- now := time.Now()
- duration := deadline.Sub(now)
- if deadline.After(now) {
- // Deadline is in the future, set a timer to wait
- d.timer = time.AfterFunc(duration, timeoutIO)
- } else {
- // Deadline is in the past. Cancel all pending IO now.
- timeoutIO()
- }
- return nil
-}
diff --git a/vendor/github.com/Microsoft/go-winio/fileinfo.go b/vendor/github.com/Microsoft/go-winio/fileinfo.go
deleted file mode 100644
index c860eb9..0000000
--- a/vendor/github.com/Microsoft/go-winio/fileinfo.go
+++ /dev/null
@@ -1,106 +0,0 @@
-//go:build windows
-// +build windows
-
-package winio
-
-import (
- "os"
- "runtime"
- "unsafe"
-
- "golang.org/x/sys/windows"
-)
-
-// FileBasicInfo contains file access time and file attributes information.
-type FileBasicInfo struct {
- CreationTime, LastAccessTime, LastWriteTime, ChangeTime windows.Filetime
- FileAttributes uint32
- _ uint32 // padding
-}
-
-// alignedFileBasicInfo is a FileBasicInfo, but aligned to uint64 by containing
-// uint64 rather than windows.Filetime. Filetime contains two uint32s. uint64
-// alignment is necessary to pass this as FILE_BASIC_INFO.
-type alignedFileBasicInfo struct {
- CreationTime, LastAccessTime, LastWriteTime, ChangeTime uint64
- FileAttributes uint32
- _ uint32 // padding
-}
-
-// GetFileBasicInfo retrieves times and attributes for a file.
-func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
- bi := &alignedFileBasicInfo{}
- if err := windows.GetFileInformationByHandleEx(
- windows.Handle(f.Fd()),
- windows.FileBasicInfo,
- (*byte)(unsafe.Pointer(bi)),
- uint32(unsafe.Sizeof(*bi)),
- ); err != nil {
- return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
- }
- runtime.KeepAlive(f)
- // Reinterpret the alignedFileBasicInfo as a FileBasicInfo so it matches the
- // public API of this module. The data may be unnecessarily aligned.
- return (*FileBasicInfo)(unsafe.Pointer(bi)), nil
-}
-
-// SetFileBasicInfo sets times and attributes for a file.
-func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error {
- // Create an alignedFileBasicInfo based on a FileBasicInfo. The copy is
- // suitable to pass to GetFileInformationByHandleEx.
- biAligned := *(*alignedFileBasicInfo)(unsafe.Pointer(bi))
- if err := windows.SetFileInformationByHandle(
- windows.Handle(f.Fd()),
- windows.FileBasicInfo,
- (*byte)(unsafe.Pointer(&biAligned)),
- uint32(unsafe.Sizeof(biAligned)),
- ); err != nil {
- return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err}
- }
- runtime.KeepAlive(f)
- return nil
-}
-
-// FileStandardInfo contains extended information for the file.
-// FILE_STANDARD_INFO in WinBase.h
-// https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_standard_info
-type FileStandardInfo struct {
- AllocationSize, EndOfFile int64
- NumberOfLinks uint32
- DeletePending, Directory bool
-}
-
-// GetFileStandardInfo retrieves ended information for the file.
-func GetFileStandardInfo(f *os.File) (*FileStandardInfo, error) {
- si := &FileStandardInfo{}
- if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()),
- windows.FileStandardInfo,
- (*byte)(unsafe.Pointer(si)),
- uint32(unsafe.Sizeof(*si))); err != nil {
- return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
- }
- runtime.KeepAlive(f)
- return si, nil
-}
-
-// FileIDInfo contains the volume serial number and file ID for a file. This pair should be
-// unique on a system.
-type FileIDInfo struct {
- VolumeSerialNumber uint64
- FileID [16]byte
-}
-
-// GetFileID retrieves the unique (volume, file ID) pair for a file.
-func GetFileID(f *os.File) (*FileIDInfo, error) {
- fileID := &FileIDInfo{}
- if err := windows.GetFileInformationByHandleEx(
- windows.Handle(f.Fd()),
- windows.FileIdInfo,
- (*byte)(unsafe.Pointer(fileID)),
- uint32(unsafe.Sizeof(*fileID)),
- ); err != nil {
- return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
- }
- runtime.KeepAlive(f)
- return fileID, nil
-}
diff --git a/vendor/github.com/Microsoft/go-winio/hvsock.go b/vendor/github.com/Microsoft/go-winio/hvsock.go
deleted file mode 100644
index c4fdd9d..0000000
--- a/vendor/github.com/Microsoft/go-winio/hvsock.go
+++ /dev/null
@@ -1,582 +0,0 @@
-//go:build windows
-// +build windows
-
-package winio
-
-import (
- "context"
- "errors"
- "fmt"
- "io"
- "net"
- "os"
- "time"
- "unsafe"
-
- "golang.org/x/sys/windows"
-
- "github.com/Microsoft/go-winio/internal/socket"
- "github.com/Microsoft/go-winio/pkg/guid"
-)
-
-const afHVSock = 34 // AF_HYPERV
-
-// Well known Service and VM IDs
-// https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/make-integration-service#vmid-wildcards
-
-// HvsockGUIDWildcard is the wildcard VmId for accepting connections from all partitions.
-func HvsockGUIDWildcard() guid.GUID { // 00000000-0000-0000-0000-000000000000
- return guid.GUID{}
-}
-
-// HvsockGUIDBroadcast is the wildcard VmId for broadcasting sends to all partitions.
-func HvsockGUIDBroadcast() guid.GUID { // ffffffff-ffff-ffff-ffff-ffffffffffff
- return guid.GUID{
- Data1: 0xffffffff,
- Data2: 0xffff,
- Data3: 0xffff,
- Data4: [8]uint8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
- }
-}
-
-// HvsockGUIDLoopback is the Loopback VmId for accepting connections to the same partition as the connector.
-func HvsockGUIDLoopback() guid.GUID { // e0e16197-dd56-4a10-9195-5ee7a155a838
- return guid.GUID{
- Data1: 0xe0e16197,
- Data2: 0xdd56,
- Data3: 0x4a10,
- Data4: [8]uint8{0x91, 0x95, 0x5e, 0xe7, 0xa1, 0x55, 0xa8, 0x38},
- }
-}
-
-// HvsockGUIDSiloHost is the address of a silo's host partition:
-// - The silo host of a hosted silo is the utility VM.
-// - The silo host of a silo on a physical host is the physical host.
-func HvsockGUIDSiloHost() guid.GUID { // 36bd0c5c-7276-4223-88ba-7d03b654c568
- return guid.GUID{
- Data1: 0x36bd0c5c,
- Data2: 0x7276,
- Data3: 0x4223,
- Data4: [8]byte{0x88, 0xba, 0x7d, 0x03, 0xb6, 0x54, 0xc5, 0x68},
- }
-}
-
-// HvsockGUIDChildren is the wildcard VmId for accepting connections from the connector's child partitions.
-func HvsockGUIDChildren() guid.GUID { // 90db8b89-0d35-4f79-8ce9-49ea0ac8b7cd
- return guid.GUID{
- Data1: 0x90db8b89,
- Data2: 0xd35,
- Data3: 0x4f79,
- Data4: [8]uint8{0x8c, 0xe9, 0x49, 0xea, 0xa, 0xc8, 0xb7, 0xcd},
- }
-}
-
-// HvsockGUIDParent is the wildcard VmId for accepting connections from the connector's parent partition.
-// Listening on this VmId accepts connection from:
-// - Inside silos: silo host partition.
-// - Inside hosted silo: host of the VM.
-// - Inside VM: VM host.
-// - Physical host: Not supported.
-func HvsockGUIDParent() guid.GUID { // a42e7cda-d03f-480c-9cc2-a4de20abb878
- return guid.GUID{
- Data1: 0xa42e7cda,
- Data2: 0xd03f,
- Data3: 0x480c,
- Data4: [8]uint8{0x9c, 0xc2, 0xa4, 0xde, 0x20, 0xab, 0xb8, 0x78},
- }
-}
-
-// hvsockVsockServiceTemplate is the Service GUID used for the VSOCK protocol.
-func hvsockVsockServiceTemplate() guid.GUID { // 00000000-facb-11e6-bd58-64006a7986d3
- return guid.GUID{
- Data2: 0xfacb,
- Data3: 0x11e6,
- Data4: [8]uint8{0xbd, 0x58, 0x64, 0x00, 0x6a, 0x79, 0x86, 0xd3},
- }
-}
-
-// An HvsockAddr is an address for a AF_HYPERV socket.
-type HvsockAddr struct {
- VMID guid.GUID
- ServiceID guid.GUID
-}
-
-type rawHvsockAddr struct {
- Family uint16
- _ uint16
- VMID guid.GUID
- ServiceID guid.GUID
-}
-
-var _ socket.RawSockaddr = &rawHvsockAddr{}
-
-// Network returns the address's network name, "hvsock".
-func (*HvsockAddr) Network() string {
- return "hvsock"
-}
-
-func (addr *HvsockAddr) String() string {
- return fmt.Sprintf("%s:%s", &addr.VMID, &addr.ServiceID)
-}
-
-// VsockServiceID returns an hvsock service ID corresponding to the specified AF_VSOCK port.
-func VsockServiceID(port uint32) guid.GUID {
- g := hvsockVsockServiceTemplate() // make a copy
- g.Data1 = port
- return g
-}
-
-func (addr *HvsockAddr) raw() rawHvsockAddr {
- return rawHvsockAddr{
- Family: afHVSock,
- VMID: addr.VMID,
- ServiceID: addr.ServiceID,
- }
-}
-
-func (addr *HvsockAddr) fromRaw(raw *rawHvsockAddr) {
- addr.VMID = raw.VMID
- addr.ServiceID = raw.ServiceID
-}
-
-// Sockaddr returns a pointer to and the size of this struct.
-//
-// Implements the [socket.RawSockaddr] interface, and allows use in
-// [socket.Bind] and [socket.ConnectEx].
-func (r *rawHvsockAddr) Sockaddr() (unsafe.Pointer, int32, error) {
- return unsafe.Pointer(r), int32(unsafe.Sizeof(rawHvsockAddr{})), nil
-}
-
-// Sockaddr interface allows use with `sockets.Bind()` and `.ConnectEx()`.
-func (r *rawHvsockAddr) FromBytes(b []byte) error {
- n := int(unsafe.Sizeof(rawHvsockAddr{}))
-
- if len(b) < n {
- return fmt.Errorf("got %d, want %d: %w", len(b), n, socket.ErrBufferSize)
- }
-
- copy(unsafe.Slice((*byte)(unsafe.Pointer(r)), n), b[:n])
- if r.Family != afHVSock {
- return fmt.Errorf("got %d, want %d: %w", r.Family, afHVSock, socket.ErrAddrFamily)
- }
-
- return nil
-}
-
-// HvsockListener is a socket listener for the AF_HYPERV address family.
-type HvsockListener struct {
- sock *win32File
- addr HvsockAddr
-}
-
-var _ net.Listener = &HvsockListener{}
-
-// HvsockConn is a connected socket of the AF_HYPERV address family.
-type HvsockConn struct {
- sock *win32File
- local, remote HvsockAddr
-}
-
-var _ net.Conn = &HvsockConn{}
-
-func newHVSocket() (*win32File, error) {
- fd, err := windows.Socket(afHVSock, windows.SOCK_STREAM, 1)
- if err != nil {
- return nil, os.NewSyscallError("socket", err)
- }
- f, err := makeWin32File(fd)
- if err != nil {
- windows.Close(fd)
- return nil, err
- }
- f.socket = true
- return f, nil
-}
-
-// ListenHvsock listens for connections on the specified hvsock address.
-func ListenHvsock(addr *HvsockAddr) (_ *HvsockListener, err error) {
- l := &HvsockListener{addr: *addr}
-
- var sock *win32File
- sock, err = newHVSocket()
- if err != nil {
- return nil, l.opErr("listen", err)
- }
- defer func() {
- if err != nil {
- _ = sock.Close()
- }
- }()
-
- sa := addr.raw()
- err = socket.Bind(sock.handle, &sa)
- if err != nil {
- return nil, l.opErr("listen", os.NewSyscallError("socket", err))
- }
- err = windows.Listen(sock.handle, 16)
- if err != nil {
- return nil, l.opErr("listen", os.NewSyscallError("listen", err))
- }
- return &HvsockListener{sock: sock, addr: *addr}, nil
-}
-
-func (l *HvsockListener) opErr(op string, err error) error {
- return &net.OpError{Op: op, Net: "hvsock", Addr: &l.addr, Err: err}
-}
-
-// Addr returns the listener's network address.
-func (l *HvsockListener) Addr() net.Addr {
- return &l.addr
-}
-
-// Accept waits for the next connection and returns it.
-func (l *HvsockListener) Accept() (_ net.Conn, err error) {
- sock, err := newHVSocket()
- if err != nil {
- return nil, l.opErr("accept", err)
- }
- defer func() {
- if sock != nil {
- sock.Close()
- }
- }()
- c, err := l.sock.prepareIO()
- if err != nil {
- return nil, l.opErr("accept", err)
- }
- defer l.sock.wg.Done()
-
- // AcceptEx, per documentation, requires an extra 16 bytes per address.
- //
- // https://docs.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-acceptex
- const addrlen = uint32(16 + unsafe.Sizeof(rawHvsockAddr{}))
- var addrbuf [addrlen * 2]byte
-
- var bytes uint32
- err = windows.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0 /* rxdatalen */, addrlen, addrlen, &bytes, &c.o)
- if _, err = l.sock.asyncIO(c, nil, bytes, err); err != nil {
- return nil, l.opErr("accept", os.NewSyscallError("acceptex", err))
- }
-
- conn := &HvsockConn{
- sock: sock,
- }
- // The local address returned in the AcceptEx buffer is the same as the Listener socket's
- // address. However, the service GUID reported by GetSockName is different from the Listeners
- // socket, and is sometimes the same as the local address of the socket that dialed the
- // address, with the service GUID.Data1 incremented, but othertimes is different.
- // todo: does the local address matter? is the listener's address or the actual address appropriate?
- conn.local.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[0])))
- conn.remote.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[addrlen])))
-
- // initialize the accepted socket and update its properties with those of the listening socket
- if err = windows.Setsockopt(sock.handle,
- windows.SOL_SOCKET, windows.SO_UPDATE_ACCEPT_CONTEXT,
- (*byte)(unsafe.Pointer(&l.sock.handle)), int32(unsafe.Sizeof(l.sock.handle))); err != nil {
- return nil, conn.opErr("accept", os.NewSyscallError("setsockopt", err))
- }
-
- sock = nil
- return conn, nil
-}
-
-// Close closes the listener, causing any pending Accept calls to fail.
-func (l *HvsockListener) Close() error {
- return l.sock.Close()
-}
-
-// HvsockDialer configures and dials a Hyper-V Socket (ie, [HvsockConn]).
-type HvsockDialer struct {
- // Deadline is the time the Dial operation must connect before erroring.
- Deadline time.Time
-
- // Retries is the number of additional connects to try if the connection times out, is refused,
- // or the host is unreachable
- Retries uint
-
- // RetryWait is the time to wait after a connection error to retry
- RetryWait time.Duration
-
- rt *time.Timer // redial wait timer
-}
-
-// Dial the Hyper-V socket at addr.
-//
-// See [HvsockDialer.Dial] for more information.
-func Dial(ctx context.Context, addr *HvsockAddr) (conn *HvsockConn, err error) {
- return (&HvsockDialer{}).Dial(ctx, addr)
-}
-
-// Dial attempts to connect to the Hyper-V socket at addr, and returns a connection if successful.
-// Will attempt (HvsockDialer).Retries if dialing fails, waiting (HvsockDialer).RetryWait between
-// retries.
-//
-// Dialing can be cancelled either by providing (HvsockDialer).Deadline, or cancelling ctx.
-func (d *HvsockDialer) Dial(ctx context.Context, addr *HvsockAddr) (conn *HvsockConn, err error) {
- op := "dial"
- // create the conn early to use opErr()
- conn = &HvsockConn{
- remote: *addr,
- }
-
- if !d.Deadline.IsZero() {
- var cancel context.CancelFunc
- ctx, cancel = context.WithDeadline(ctx, d.Deadline)
- defer cancel()
- }
-
- // preemptive timeout/cancellation check
- if err = ctx.Err(); err != nil {
- return nil, conn.opErr(op, err)
- }
-
- sock, err := newHVSocket()
- if err != nil {
- return nil, conn.opErr(op, err)
- }
- defer func() {
- if sock != nil {
- sock.Close()
- }
- }()
-
- sa := addr.raw()
- err = socket.Bind(sock.handle, &sa)
- if err != nil {
- return nil, conn.opErr(op, os.NewSyscallError("bind", err))
- }
-
- c, err := sock.prepareIO()
- if err != nil {
- return nil, conn.opErr(op, err)
- }
- defer sock.wg.Done()
- var bytes uint32
- for i := uint(0); i <= d.Retries; i++ {
- err = socket.ConnectEx(
- sock.handle,
- &sa,
- nil, // sendBuf
- 0, // sendDataLen
- &bytes,
- (*windows.Overlapped)(unsafe.Pointer(&c.o)))
- _, err = sock.asyncIO(c, nil, bytes, err)
- if i < d.Retries && canRedial(err) {
- if err = d.redialWait(ctx); err == nil {
- continue
- }
- }
- break
- }
- if err != nil {
- return nil, conn.opErr(op, os.NewSyscallError("connectex", err))
- }
-
- // update the connection properties, so shutdown can be used
- if err = windows.Setsockopt(
- sock.handle,
- windows.SOL_SOCKET,
- windows.SO_UPDATE_CONNECT_CONTEXT,
- nil, // optvalue
- 0, // optlen
- ); err != nil {
- return nil, conn.opErr(op, os.NewSyscallError("setsockopt", err))
- }
-
- // get the local name
- var sal rawHvsockAddr
- err = socket.GetSockName(sock.handle, &sal)
- if err != nil {
- return nil, conn.opErr(op, os.NewSyscallError("getsockname", err))
- }
- conn.local.fromRaw(&sal)
-
- // one last check for timeout, since asyncIO doesn't check the context
- if err = ctx.Err(); err != nil {
- return nil, conn.opErr(op, err)
- }
-
- conn.sock = sock
- sock = nil
-
- return conn, nil
-}
-
-// redialWait waits before attempting to redial, resetting the timer as appropriate.
-func (d *HvsockDialer) redialWait(ctx context.Context) (err error) {
- if d.RetryWait == 0 {
- return nil
- }
-
- if d.rt == nil {
- d.rt = time.NewTimer(d.RetryWait)
- } else {
- // should already be stopped and drained
- d.rt.Reset(d.RetryWait)
- }
-
- select {
- case <-ctx.Done():
- case <-d.rt.C:
- return nil
- }
-
- // stop and drain the timer
- if !d.rt.Stop() {
- <-d.rt.C
- }
- return ctx.Err()
-}
-
-// assumes error is a plain, unwrapped windows.Errno provided by direct syscall.
-func canRedial(err error) bool {
- //nolint:errorlint // guaranteed to be an Errno
- switch err {
- case windows.WSAECONNREFUSED, windows.WSAENETUNREACH, windows.WSAETIMEDOUT,
- windows.ERROR_CONNECTION_REFUSED, windows.ERROR_CONNECTION_UNAVAIL:
- return true
- default:
- return false
- }
-}
-
-func (conn *HvsockConn) opErr(op string, err error) error {
- // translate from "file closed" to "socket closed"
- if errors.Is(err, ErrFileClosed) {
- err = socket.ErrSocketClosed
- }
- return &net.OpError{Op: op, Net: "hvsock", Source: &conn.local, Addr: &conn.remote, Err: err}
-}
-
-func (conn *HvsockConn) Read(b []byte) (int, error) {
- c, err := conn.sock.prepareIO()
- if err != nil {
- return 0, conn.opErr("read", err)
- }
- defer conn.sock.wg.Done()
- buf := windows.WSABuf{Buf: &b[0], Len: uint32(len(b))}
- var flags, bytes uint32
- err = windows.WSARecv(conn.sock.handle, &buf, 1, &bytes, &flags, &c.o, nil)
- n, err := conn.sock.asyncIO(c, &conn.sock.readDeadline, bytes, err)
- if err != nil {
- var eno windows.Errno
- if errors.As(err, &eno) {
- err = os.NewSyscallError("wsarecv", eno)
- }
- return 0, conn.opErr("read", err)
- } else if n == 0 {
- err = io.EOF
- }
- return n, err
-}
-
-func (conn *HvsockConn) Write(b []byte) (int, error) {
- t := 0
- for len(b) != 0 {
- n, err := conn.write(b)
- if err != nil {
- return t + n, err
- }
- t += n
- b = b[n:]
- }
- return t, nil
-}
-
-func (conn *HvsockConn) write(b []byte) (int, error) {
- c, err := conn.sock.prepareIO()
- if err != nil {
- return 0, conn.opErr("write", err)
- }
- defer conn.sock.wg.Done()
- buf := windows.WSABuf{Buf: &b[0], Len: uint32(len(b))}
- var bytes uint32
- err = windows.WSASend(conn.sock.handle, &buf, 1, &bytes, 0, &c.o, nil)
- n, err := conn.sock.asyncIO(c, &conn.sock.writeDeadline, bytes, err)
- if err != nil {
- var eno windows.Errno
- if errors.As(err, &eno) {
- err = os.NewSyscallError("wsasend", eno)
- }
- return 0, conn.opErr("write", err)
- }
- return n, err
-}
-
-// Close closes the socket connection, failing any pending read or write calls.
-func (conn *HvsockConn) Close() error {
- return conn.sock.Close()
-}
-
-func (conn *HvsockConn) IsClosed() bool {
- return conn.sock.IsClosed()
-}
-
-// shutdown disables sending or receiving on a socket.
-func (conn *HvsockConn) shutdown(how int) error {
- if conn.IsClosed() {
- return socket.ErrSocketClosed
- }
-
- err := windows.Shutdown(conn.sock.handle, how)
- if err != nil {
- // If the connection was closed, shutdowns fail with "not connected"
- if errors.Is(err, windows.WSAENOTCONN) ||
- errors.Is(err, windows.WSAESHUTDOWN) {
- err = socket.ErrSocketClosed
- }
- return os.NewSyscallError("shutdown", err)
- }
- return nil
-}
-
-// CloseRead shuts down the read end of the socket, preventing future read operations.
-func (conn *HvsockConn) CloseRead() error {
- err := conn.shutdown(windows.SHUT_RD)
- if err != nil {
- return conn.opErr("closeread", err)
- }
- return nil
-}
-
-// CloseWrite shuts down the write end of the socket, preventing future write operations and
-// notifying the other endpoint that no more data will be written.
-func (conn *HvsockConn) CloseWrite() error {
- err := conn.shutdown(windows.SHUT_WR)
- if err != nil {
- return conn.opErr("closewrite", err)
- }
- return nil
-}
-
-// LocalAddr returns the local address of the connection.
-func (conn *HvsockConn) LocalAddr() net.Addr {
- return &conn.local
-}
-
-// RemoteAddr returns the remote address of the connection.
-func (conn *HvsockConn) RemoteAddr() net.Addr {
- return &conn.remote
-}
-
-// SetDeadline implements the net.Conn SetDeadline method.
-func (conn *HvsockConn) SetDeadline(t time.Time) error {
- // todo: implement `SetDeadline` for `win32File`
- if err := conn.SetReadDeadline(t); err != nil {
- return fmt.Errorf("set read deadline: %w", err)
- }
- if err := conn.SetWriteDeadline(t); err != nil {
- return fmt.Errorf("set write deadline: %w", err)
- }
- return nil
-}
-
-// SetReadDeadline implements the net.Conn SetReadDeadline method.
-func (conn *HvsockConn) SetReadDeadline(t time.Time) error {
- return conn.sock.SetReadDeadline(t)
-}
-
-// SetWriteDeadline implements the net.Conn SetWriteDeadline method.
-func (conn *HvsockConn) SetWriteDeadline(t time.Time) error {
- return conn.sock.SetWriteDeadline(t)
-}
diff --git a/vendor/github.com/Microsoft/go-winio/internal/fs/doc.go b/vendor/github.com/Microsoft/go-winio/internal/fs/doc.go
deleted file mode 100644
index 1f65388..0000000
--- a/vendor/github.com/Microsoft/go-winio/internal/fs/doc.go
+++ /dev/null
@@ -1,2 +0,0 @@
-// This package contains Win32 filesystem functionality.
-package fs
diff --git a/vendor/github.com/Microsoft/go-winio/internal/fs/fs.go b/vendor/github.com/Microsoft/go-winio/internal/fs/fs.go
deleted file mode 100644
index 0cd9621..0000000
--- a/vendor/github.com/Microsoft/go-winio/internal/fs/fs.go
+++ /dev/null
@@ -1,262 +0,0 @@
-//go:build windows
-
-package fs
-
-import (
- "golang.org/x/sys/windows"
-
- "github.com/Microsoft/go-winio/internal/stringbuffer"
-)
-
-//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go fs.go
-
-// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
-//sys CreateFile(name string, access AccessMask, mode FileShareMode, sa *windows.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) [failretval==windows.InvalidHandle] = CreateFileW
-
-const NullHandle windows.Handle = 0
-
-// AccessMask defines standard, specific, and generic rights.
-//
-// Used with CreateFile and NtCreateFile (and co.).
-//
-// Bitmask:
-// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
-// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
-// +---------------+---------------+-------------------------------+
-// |G|G|G|G|Resvd|A| StandardRights| SpecificRights |
-// |R|W|E|A| |S| | |
-// +-+-------------+---------------+-------------------------------+
-//
-// GR Generic Read
-// GW Generic Write
-// GE Generic Exectue
-// GA Generic All
-// Resvd Reserved
-// AS Access Security System
-//
-// https://learn.microsoft.com/en-us/windows/win32/secauthz/access-mask
-//
-// https://learn.microsoft.com/en-us/windows/win32/secauthz/generic-access-rights
-//
-// https://learn.microsoft.com/en-us/windows/win32/fileio/file-access-rights-constants
-type AccessMask = windows.ACCESS_MASK
-
-//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
-const (
- // Not actually any.
- //
- // For CreateFile: "query certain metadata such as file, directory, or device attributes without accessing that file or device"
- // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew#parameters
- FILE_ANY_ACCESS AccessMask = 0
-
- GENERIC_READ AccessMask = 0x8000_0000
- GENERIC_WRITE AccessMask = 0x4000_0000
- GENERIC_EXECUTE AccessMask = 0x2000_0000
- GENERIC_ALL AccessMask = 0x1000_0000
- ACCESS_SYSTEM_SECURITY AccessMask = 0x0100_0000
-
- // Specific Object Access
- // from ntioapi.h
-
- FILE_READ_DATA AccessMask = (0x0001) // file & pipe
- FILE_LIST_DIRECTORY AccessMask = (0x0001) // directory
-
- FILE_WRITE_DATA AccessMask = (0x0002) // file & pipe
- FILE_ADD_FILE AccessMask = (0x0002) // directory
-
- FILE_APPEND_DATA AccessMask = (0x0004) // file
- FILE_ADD_SUBDIRECTORY AccessMask = (0x0004) // directory
- FILE_CREATE_PIPE_INSTANCE AccessMask = (0x0004) // named pipe
-
- FILE_READ_EA AccessMask = (0x0008) // file & directory
- FILE_READ_PROPERTIES AccessMask = FILE_READ_EA
-
- FILE_WRITE_EA AccessMask = (0x0010) // file & directory
- FILE_WRITE_PROPERTIES AccessMask = FILE_WRITE_EA
-
- FILE_EXECUTE AccessMask = (0x0020) // file
- FILE_TRAVERSE AccessMask = (0x0020) // directory
-
- FILE_DELETE_CHILD AccessMask = (0x0040) // directory
-
- FILE_READ_ATTRIBUTES AccessMask = (0x0080) // all
-
- FILE_WRITE_ATTRIBUTES AccessMask = (0x0100) // all
-
- FILE_ALL_ACCESS AccessMask = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF)
- FILE_GENERIC_READ AccessMask = (STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE)
- FILE_GENERIC_WRITE AccessMask = (STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE)
- FILE_GENERIC_EXECUTE AccessMask = (STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE)
-
- SPECIFIC_RIGHTS_ALL AccessMask = 0x0000FFFF
-
- // Standard Access
- // from ntseapi.h
-
- DELETE AccessMask = 0x0001_0000
- READ_CONTROL AccessMask = 0x0002_0000
- WRITE_DAC AccessMask = 0x0004_0000
- WRITE_OWNER AccessMask = 0x0008_0000
- SYNCHRONIZE AccessMask = 0x0010_0000
-
- STANDARD_RIGHTS_REQUIRED AccessMask = 0x000F_0000
-
- STANDARD_RIGHTS_READ AccessMask = READ_CONTROL
- STANDARD_RIGHTS_WRITE AccessMask = READ_CONTROL
- STANDARD_RIGHTS_EXECUTE AccessMask = READ_CONTROL
-
- STANDARD_RIGHTS_ALL AccessMask = 0x001F_0000
-)
-
-type FileShareMode uint32
-
-//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
-const (
- FILE_SHARE_NONE FileShareMode = 0x00
- FILE_SHARE_READ FileShareMode = 0x01
- FILE_SHARE_WRITE FileShareMode = 0x02
- FILE_SHARE_DELETE FileShareMode = 0x04
- FILE_SHARE_VALID_FLAGS FileShareMode = 0x07
-)
-
-type FileCreationDisposition uint32
-
-//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
-const (
- // from winbase.h
-
- CREATE_NEW FileCreationDisposition = 0x01
- CREATE_ALWAYS FileCreationDisposition = 0x02
- OPEN_EXISTING FileCreationDisposition = 0x03
- OPEN_ALWAYS FileCreationDisposition = 0x04
- TRUNCATE_EXISTING FileCreationDisposition = 0x05
-)
-
-// Create disposition values for NtCreate*
-type NTFileCreationDisposition uint32
-
-//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
-const (
- // From ntioapi.h
-
- FILE_SUPERSEDE NTFileCreationDisposition = 0x00
- FILE_OPEN NTFileCreationDisposition = 0x01
- FILE_CREATE NTFileCreationDisposition = 0x02
- FILE_OPEN_IF NTFileCreationDisposition = 0x03
- FILE_OVERWRITE NTFileCreationDisposition = 0x04
- FILE_OVERWRITE_IF NTFileCreationDisposition = 0x05
- FILE_MAXIMUM_DISPOSITION NTFileCreationDisposition = 0x05
-)
-
-// CreateFile and co. take flags or attributes together as one parameter.
-// Define alias until we can use generics to allow both
-//
-// https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
-type FileFlagOrAttribute uint32
-
-//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
-const (
- // from winnt.h
-
- FILE_FLAG_WRITE_THROUGH FileFlagOrAttribute = 0x8000_0000
- FILE_FLAG_OVERLAPPED FileFlagOrAttribute = 0x4000_0000
- FILE_FLAG_NO_BUFFERING FileFlagOrAttribute = 0x2000_0000
- FILE_FLAG_RANDOM_ACCESS FileFlagOrAttribute = 0x1000_0000
- FILE_FLAG_SEQUENTIAL_SCAN FileFlagOrAttribute = 0x0800_0000
- FILE_FLAG_DELETE_ON_CLOSE FileFlagOrAttribute = 0x0400_0000
- FILE_FLAG_BACKUP_SEMANTICS FileFlagOrAttribute = 0x0200_0000
- FILE_FLAG_POSIX_SEMANTICS FileFlagOrAttribute = 0x0100_0000
- FILE_FLAG_OPEN_REPARSE_POINT FileFlagOrAttribute = 0x0020_0000
- FILE_FLAG_OPEN_NO_RECALL FileFlagOrAttribute = 0x0010_0000
- FILE_FLAG_FIRST_PIPE_INSTANCE FileFlagOrAttribute = 0x0008_0000
-)
-
-// NtCreate* functions take a dedicated CreateOptions parameter.
-//
-// https://learn.microsoft.com/en-us/windows/win32/api/Winternl/nf-winternl-ntcreatefile
-//
-// https://learn.microsoft.com/en-us/windows/win32/devnotes/nt-create-named-pipe-file
-type NTCreateOptions uint32
-
-//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
-const (
- // From ntioapi.h
-
- FILE_DIRECTORY_FILE NTCreateOptions = 0x0000_0001
- FILE_WRITE_THROUGH NTCreateOptions = 0x0000_0002
- FILE_SEQUENTIAL_ONLY NTCreateOptions = 0x0000_0004
- FILE_NO_INTERMEDIATE_BUFFERING NTCreateOptions = 0x0000_0008
-
- FILE_SYNCHRONOUS_IO_ALERT NTCreateOptions = 0x0000_0010
- FILE_SYNCHRONOUS_IO_NONALERT NTCreateOptions = 0x0000_0020
- FILE_NON_DIRECTORY_FILE NTCreateOptions = 0x0000_0040
- FILE_CREATE_TREE_CONNECTION NTCreateOptions = 0x0000_0080
-
- FILE_COMPLETE_IF_OPLOCKED NTCreateOptions = 0x0000_0100
- FILE_NO_EA_KNOWLEDGE NTCreateOptions = 0x0000_0200
- FILE_DISABLE_TUNNELING NTCreateOptions = 0x0000_0400
- FILE_RANDOM_ACCESS NTCreateOptions = 0x0000_0800
-
- FILE_DELETE_ON_CLOSE NTCreateOptions = 0x0000_1000
- FILE_OPEN_BY_FILE_ID NTCreateOptions = 0x0000_2000
- FILE_OPEN_FOR_BACKUP_INTENT NTCreateOptions = 0x0000_4000
- FILE_NO_COMPRESSION NTCreateOptions = 0x0000_8000
-)
-
-type FileSQSFlag = FileFlagOrAttribute
-
-//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
-const (
- // from winbase.h
-
- SECURITY_ANONYMOUS FileSQSFlag = FileSQSFlag(SecurityAnonymous << 16)
- SECURITY_IDENTIFICATION FileSQSFlag = FileSQSFlag(SecurityIdentification << 16)
- SECURITY_IMPERSONATION FileSQSFlag = FileSQSFlag(SecurityImpersonation << 16)
- SECURITY_DELEGATION FileSQSFlag = FileSQSFlag(SecurityDelegation << 16)
-
- SECURITY_SQOS_PRESENT FileSQSFlag = 0x0010_0000
- SECURITY_VALID_SQOS_FLAGS FileSQSFlag = 0x001F_0000
-)
-
-// GetFinalPathNameByHandle flags
-//
-// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew#parameters
-type GetFinalPathFlag uint32
-
-//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
-const (
- GetFinalPathDefaultFlag GetFinalPathFlag = 0x0
-
- FILE_NAME_NORMALIZED GetFinalPathFlag = 0x0
- FILE_NAME_OPENED GetFinalPathFlag = 0x8
-
- VOLUME_NAME_DOS GetFinalPathFlag = 0x0
- VOLUME_NAME_GUID GetFinalPathFlag = 0x1
- VOLUME_NAME_NT GetFinalPathFlag = 0x2
- VOLUME_NAME_NONE GetFinalPathFlag = 0x4
-)
-
-// getFinalPathNameByHandle facilitates calling the Windows API GetFinalPathNameByHandle
-// with the given handle and flags. It transparently takes care of creating a buffer of the
-// correct size for the call.
-//
-// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew
-func GetFinalPathNameByHandle(h windows.Handle, flags GetFinalPathFlag) (string, error) {
- b := stringbuffer.NewWString()
- //TODO: can loop infinitely if Win32 keeps returning the same (or a larger) n?
- for {
- n, err := windows.GetFinalPathNameByHandle(h, b.Pointer(), b.Cap(), uint32(flags))
- if err != nil {
- return "", err
- }
- // If the buffer wasn't large enough, n will be the total size needed (including null terminator).
- // Resize and try again.
- if n > b.Cap() {
- b.ResizeTo(n)
- continue
- }
- // If the buffer is large enough, n will be the size not including the null terminator.
- // Convert to a Go string and return.
- return b.String(), nil
- }
-}
diff --git a/vendor/github.com/Microsoft/go-winio/internal/fs/security.go b/vendor/github.com/Microsoft/go-winio/internal/fs/security.go
deleted file mode 100644
index 81760ac..0000000
--- a/vendor/github.com/Microsoft/go-winio/internal/fs/security.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package fs
-
-// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level
-type SecurityImpersonationLevel int32 // C default enums underlying type is `int`, which is Go `int32`
-
-// Impersonation levels
-const (
- SecurityAnonymous SecurityImpersonationLevel = 0
- SecurityIdentification SecurityImpersonationLevel = 1
- SecurityImpersonation SecurityImpersonationLevel = 2
- SecurityDelegation SecurityImpersonationLevel = 3
-)
diff --git a/vendor/github.com/Microsoft/go-winio/internal/fs/zsyscall_windows.go b/vendor/github.com/Microsoft/go-winio/internal/fs/zsyscall_windows.go
deleted file mode 100644
index a94e234..0000000
--- a/vendor/github.com/Microsoft/go-winio/internal/fs/zsyscall_windows.go
+++ /dev/null
@@ -1,61 +0,0 @@
-//go:build windows
-
-// Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT.
-
-package fs
-
-import (
- "syscall"
- "unsafe"
-
- "golang.org/x/sys/windows"
-)
-
-var _ unsafe.Pointer
-
-// Do the interface allocations only once for common
-// Errno values.
-const (
- errnoERROR_IO_PENDING = 997
-)
-
-var (
- errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
- errERROR_EINVAL error = syscall.EINVAL
-)
-
-// errnoErr returns common boxed Errno values, to prevent
-// allocations at runtime.
-func errnoErr(e syscall.Errno) error {
- switch e {
- case 0:
- return errERROR_EINVAL
- case errnoERROR_IO_PENDING:
- return errERROR_IO_PENDING
- }
- return e
-}
-
-var (
- modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
-
- procCreateFileW = modkernel32.NewProc("CreateFileW")
-)
-
-func CreateFile(name string, access AccessMask, mode FileShareMode, sa *windows.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) {
- var _p0 *uint16
- _p0, err = syscall.UTF16PtrFromString(name)
- if err != nil {
- return
- }
- return _CreateFile(_p0, access, mode, sa, createmode, attrs, templatefile)
-}
-
-func _CreateFile(name *uint16, access AccessMask, mode FileShareMode, sa *windows.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) {
- r0, _, e1 := syscall.SyscallN(procCreateFileW.Addr(), uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile))
- handle = windows.Handle(r0)
- if handle == windows.InvalidHandle {
- err = errnoErr(e1)
- }
- return
-}
diff --git a/vendor/github.com/Microsoft/go-winio/internal/socket/rawaddr.go b/vendor/github.com/Microsoft/go-winio/internal/socket/rawaddr.go
deleted file mode 100644
index 7e82f9a..0000000
--- a/vendor/github.com/Microsoft/go-winio/internal/socket/rawaddr.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package socket
-
-import (
- "unsafe"
-)
-
-// RawSockaddr allows structs to be used with [Bind] and [ConnectEx]. The
-// struct must meet the Win32 sockaddr requirements specified here:
-// https://docs.microsoft.com/en-us/windows/win32/winsock/sockaddr-2
-//
-// Specifically, the struct size must be least larger than an int16 (unsigned short)
-// for the address family.
-type RawSockaddr interface {
- // Sockaddr returns a pointer to the RawSockaddr and its struct size, allowing
- // for the RawSockaddr's data to be overwritten by syscalls (if necessary).
- //
- // It is the callers responsibility to validate that the values are valid; invalid
- // pointers or size can cause a panic.
- Sockaddr() (unsafe.Pointer, int32, error)
-}
diff --git a/vendor/github.com/Microsoft/go-winio/internal/socket/socket.go b/vendor/github.com/Microsoft/go-winio/internal/socket/socket.go
deleted file mode 100644
index 88580d9..0000000
--- a/vendor/github.com/Microsoft/go-winio/internal/socket/socket.go
+++ /dev/null
@@ -1,177 +0,0 @@
-//go:build windows
-
-package socket
-
-import (
- "errors"
- "fmt"
- "net"
- "sync"
- "syscall"
- "unsafe"
-
- "github.com/Microsoft/go-winio/pkg/guid"
- "golang.org/x/sys/windows"
-)
-
-//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go socket.go
-
-//sys getsockname(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) [failretval==socketError] = ws2_32.getsockname
-//sys getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) [failretval==socketError] = ws2_32.getpeername
-//sys bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) [failretval==socketError] = ws2_32.bind
-
-const socketError = uintptr(^uint32(0))
-
-var (
- // todo(helsaawy): create custom error types to store the desired vs actual size and addr family?
-
- ErrBufferSize = errors.New("buffer size")
- ErrAddrFamily = errors.New("address family")
- ErrInvalidPointer = errors.New("invalid pointer")
- ErrSocketClosed = fmt.Errorf("socket closed: %w", net.ErrClosed)
-)
-
-// todo(helsaawy): replace these with generics, ie: GetSockName[S RawSockaddr](s windows.Handle) (S, error)
-
-// GetSockName writes the local address of socket s to the [RawSockaddr] rsa.
-// If rsa is not large enough, the [windows.WSAEFAULT] is returned.
-func GetSockName(s windows.Handle, rsa RawSockaddr) error {
- ptr, l, err := rsa.Sockaddr()
- if err != nil {
- return fmt.Errorf("could not retrieve socket pointer and size: %w", err)
- }
-
- // although getsockname returns WSAEFAULT if the buffer is too small, it does not set
- // &l to the correct size, so--apart from doubling the buffer repeatedly--there is no remedy
- return getsockname(s, ptr, &l)
-}
-
-// GetPeerName returns the remote address the socket is connected to.
-//
-// See [GetSockName] for more information.
-func GetPeerName(s windows.Handle, rsa RawSockaddr) error {
- ptr, l, err := rsa.Sockaddr()
- if err != nil {
- return fmt.Errorf("could not retrieve socket pointer and size: %w", err)
- }
-
- return getpeername(s, ptr, &l)
-}
-
-func Bind(s windows.Handle, rsa RawSockaddr) (err error) {
- ptr, l, err := rsa.Sockaddr()
- if err != nil {
- return fmt.Errorf("could not retrieve socket pointer and size: %w", err)
- }
-
- return bind(s, ptr, l)
-}
-
-// "golang.org/x/sys/windows".ConnectEx and .Bind only accept internal implementations of the
-// their sockaddr interface, so they cannot be used with HvsockAddr
-// Replicate functionality here from
-// https://cs.opensource.google/go/x/sys/+/master:windows/syscall_windows.go
-
-// The function pointers to `AcceptEx`, `ConnectEx` and `GetAcceptExSockaddrs` must be loaded at
-// runtime via a WSAIoctl call:
-// https://docs.microsoft.com/en-us/windows/win32/api/Mswsock/nc-mswsock-lpfn_connectex#remarks
-
-type runtimeFunc struct {
- id guid.GUID
- once sync.Once
- addr uintptr
- err error
-}
-
-func (f *runtimeFunc) Load() error {
- f.once.Do(func() {
- var s windows.Handle
- s, f.err = windows.Socket(windows.AF_INET, windows.SOCK_STREAM, windows.IPPROTO_TCP)
- if f.err != nil {
- return
- }
- defer windows.CloseHandle(s) //nolint:errcheck
-
- var n uint32
- f.err = windows.WSAIoctl(s,
- windows.SIO_GET_EXTENSION_FUNCTION_POINTER,
- (*byte)(unsafe.Pointer(&f.id)),
- uint32(unsafe.Sizeof(f.id)),
- (*byte)(unsafe.Pointer(&f.addr)),
- uint32(unsafe.Sizeof(f.addr)),
- &n,
- nil, // overlapped
- 0, // completionRoutine
- )
- })
- return f.err
-}
-
-var (
- // todo: add `AcceptEx` and `GetAcceptExSockaddrs`
- WSAID_CONNECTEX = guid.GUID{ //revive:disable-line:var-naming ALL_CAPS
- Data1: 0x25a207b9,
- Data2: 0xddf3,
- Data3: 0x4660,
- Data4: [8]byte{0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e},
- }
-
- connectExFunc = runtimeFunc{id: WSAID_CONNECTEX}
-)
-
-func ConnectEx(
- fd windows.Handle,
- rsa RawSockaddr,
- sendBuf *byte,
- sendDataLen uint32,
- bytesSent *uint32,
- overlapped *windows.Overlapped,
-) error {
- if err := connectExFunc.Load(); err != nil {
- return fmt.Errorf("failed to load ConnectEx function pointer: %w", err)
- }
- ptr, n, err := rsa.Sockaddr()
- if err != nil {
- return err
- }
- return connectEx(fd, ptr, n, sendBuf, sendDataLen, bytesSent, overlapped)
-}
-
-// BOOL LpfnConnectex(
-// [in] SOCKET s,
-// [in] const sockaddr *name,
-// [in] int namelen,
-// [in, optional] PVOID lpSendBuffer,
-// [in] DWORD dwSendDataLength,
-// [out] LPDWORD lpdwBytesSent,
-// [in] LPOVERLAPPED lpOverlapped
-// )
-
-func connectEx(
- s windows.Handle,
- name unsafe.Pointer,
- namelen int32,
- sendBuf *byte,
- sendDataLen uint32,
- bytesSent *uint32,
- overlapped *windows.Overlapped,
-) (err error) {
- r1, _, e1 := syscall.SyscallN(connectExFunc.addr,
- uintptr(s),
- uintptr(name),
- uintptr(namelen),
- uintptr(unsafe.Pointer(sendBuf)),
- uintptr(sendDataLen),
- uintptr(unsafe.Pointer(bytesSent)),
- uintptr(unsafe.Pointer(overlapped)),
- )
-
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = syscall.EINVAL
- }
- }
- return err
-}
diff --git a/vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go b/vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go
deleted file mode 100644
index e150412..0000000
--- a/vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go
+++ /dev/null
@@ -1,69 +0,0 @@
-//go:build windows
-
-// Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT.
-
-package socket
-
-import (
- "syscall"
- "unsafe"
-
- "golang.org/x/sys/windows"
-)
-
-var _ unsafe.Pointer
-
-// Do the interface allocations only once for common
-// Errno values.
-const (
- errnoERROR_IO_PENDING = 997
-)
-
-var (
- errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
- errERROR_EINVAL error = syscall.EINVAL
-)
-
-// errnoErr returns common boxed Errno values, to prevent
-// allocations at runtime.
-func errnoErr(e syscall.Errno) error {
- switch e {
- case 0:
- return errERROR_EINVAL
- case errnoERROR_IO_PENDING:
- return errERROR_IO_PENDING
- }
- return e
-}
-
-var (
- modws2_32 = windows.NewLazySystemDLL("ws2_32.dll")
-
- procbind = modws2_32.NewProc("bind")
- procgetpeername = modws2_32.NewProc("getpeername")
- procgetsockname = modws2_32.NewProc("getsockname")
-)
-
-func bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) {
- r1, _, e1 := syscall.SyscallN(procbind.Addr(), uintptr(s), uintptr(name), uintptr(namelen))
- if r1 == socketError {
- err = errnoErr(e1)
- }
- return
-}
-
-func getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) {
- r1, _, e1 := syscall.SyscallN(procgetpeername.Addr(), uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen)))
- if r1 == socketError {
- err = errnoErr(e1)
- }
- return
-}
-
-func getsockname(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) {
- r1, _, e1 := syscall.SyscallN(procgetsockname.Addr(), uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen)))
- if r1 == socketError {
- err = errnoErr(e1)
- }
- return
-}
diff --git a/vendor/github.com/Microsoft/go-winio/internal/stringbuffer/wstring.go b/vendor/github.com/Microsoft/go-winio/internal/stringbuffer/wstring.go
deleted file mode 100644
index 42ebc01..0000000
--- a/vendor/github.com/Microsoft/go-winio/internal/stringbuffer/wstring.go
+++ /dev/null
@@ -1,132 +0,0 @@
-package stringbuffer
-
-import (
- "sync"
- "unicode/utf16"
-)
-
-// TODO: worth exporting and using in mkwinsyscall?
-
-// Uint16BufferSize is the buffer size in the pool, chosen somewhat arbitrarily to accommodate
-// large path strings:
-// MAX_PATH (260) + size of volume GUID prefix (49) + null terminator = 310.
-const MinWStringCap = 310
-
-// use *[]uint16 since []uint16 creates an extra allocation where the slice header
-// is copied to heap and then referenced via pointer in the interface header that sync.Pool
-// stores.
-var pathPool = sync.Pool{ // if go1.18+ adds Pool[T], use that to store []uint16 directly
- New: func() interface{} {
- b := make([]uint16, MinWStringCap)
- return &b
- },
-}
-
-func newBuffer() []uint16 { return *(pathPool.Get().(*[]uint16)) }
-
-// freeBuffer copies the slice header data, and puts a pointer to that in the pool.
-// This avoids taking a pointer to the slice header in WString, which can be set to nil.
-func freeBuffer(b []uint16) { pathPool.Put(&b) }
-
-// WString is a wide string buffer ([]uint16) meant for storing UTF-16 encoded strings
-// for interacting with Win32 APIs.
-// Sizes are specified as uint32 and not int.
-//
-// It is not thread safe.
-type WString struct {
- // type-def allows casting to []uint16 directly, use struct to prevent that and allow adding fields in the future.
-
- // raw buffer
- b []uint16
-}
-
-// NewWString returns a [WString] allocated from a shared pool with an
-// initial capacity of at least [MinWStringCap].
-// Since the buffer may have been previously used, its contents are not guaranteed to be empty.
-//
-// The buffer should be freed via [WString.Free]
-func NewWString() *WString {
- return &WString{
- b: newBuffer(),
- }
-}
-
-func (b *WString) Free() {
- if b.empty() {
- return
- }
- freeBuffer(b.b)
- b.b = nil
-}
-
-// ResizeTo grows the buffer to at least c and returns the new capacity, freeing the
-// previous buffer back into pool.
-func (b *WString) ResizeTo(c uint32) uint32 {
- // already sufficient (or n is 0)
- if c <= b.Cap() {
- return b.Cap()
- }
-
- if c <= MinWStringCap {
- c = MinWStringCap
- }
- // allocate at-least double buffer size, as is done in [bytes.Buffer] and other places
- if c <= 2*b.Cap() {
- c = 2 * b.Cap()
- }
-
- b2 := make([]uint16, c)
- if !b.empty() {
- copy(b2, b.b)
- freeBuffer(b.b)
- }
- b.b = b2
- return c
-}
-
-// Buffer returns the underlying []uint16 buffer.
-func (b *WString) Buffer() []uint16 {
- if b.empty() {
- return nil
- }
- return b.b
-}
-
-// Pointer returns a pointer to the first uint16 in the buffer.
-// If the [WString.Free] has already been called, the pointer will be nil.
-func (b *WString) Pointer() *uint16 {
- if b.empty() {
- return nil
- }
- return &b.b[0]
-}
-
-// String returns the returns the UTF-8 encoding of the UTF-16 string in the buffer.
-//
-// It assumes that the data is null-terminated.
-func (b *WString) String() string {
- // Using [windows.UTF16ToString] would require importing "golang.org/x/sys/windows"
- // and would make this code Windows-only, which makes no sense.
- // So copy UTF16ToString code into here.
- // If other windows-specific code is added, switch to [windows.UTF16ToString]
-
- s := b.b
- for i, v := range s {
- if v == 0 {
- s = s[:i]
- break
- }
- }
- return string(utf16.Decode(s))
-}
-
-// Cap returns the underlying buffer capacity.
-func (b *WString) Cap() uint32 {
- if b.empty() {
- return 0
- }
- return b.cap()
-}
-
-func (b *WString) cap() uint32 { return uint32(cap(b.b)) }
-func (b *WString) empty() bool { return b == nil || b.cap() == 0 }
diff --git a/vendor/github.com/Microsoft/go-winio/pipe.go b/vendor/github.com/Microsoft/go-winio/pipe.go
deleted file mode 100644
index a2da663..0000000
--- a/vendor/github.com/Microsoft/go-winio/pipe.go
+++ /dev/null
@@ -1,586 +0,0 @@
-//go:build windows
-// +build windows
-
-package winio
-
-import (
- "context"
- "errors"
- "fmt"
- "io"
- "net"
- "os"
- "runtime"
- "time"
- "unsafe"
-
- "golang.org/x/sys/windows"
-
- "github.com/Microsoft/go-winio/internal/fs"
-)
-
-//sys connectNamedPipe(pipe windows.Handle, o *windows.Overlapped) (err error) = ConnectNamedPipe
-//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *windows.SecurityAttributes) (handle windows.Handle, err error) [failretval==windows.InvalidHandle] = CreateNamedPipeW
-//sys disconnectNamedPipe(pipe windows.Handle) (err error) = DisconnectNamedPipe
-//sys getNamedPipeInfo(pipe windows.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo
-//sys getNamedPipeHandleState(pipe windows.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
-//sys ntCreateNamedPipeFile(pipe *windows.Handle, access ntAccessMask, oa *objectAttributes, iosb *ioStatusBlock, share ntFileShareMode, disposition ntFileCreationDisposition, options ntFileOptions, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntStatus) = ntdll.NtCreateNamedPipeFile
-//sys rtlNtStatusToDosError(status ntStatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb
-//sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntStatus) = ntdll.RtlDosPathNameToNtPathName_U
-//sys rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) = ntdll.RtlDefaultNpAcl
-
-type PipeConn interface {
- net.Conn
- Disconnect() error
- Flush() error
-}
-
-// type aliases for mkwinsyscall code
-type (
- ntAccessMask = fs.AccessMask
- ntFileShareMode = fs.FileShareMode
- ntFileCreationDisposition = fs.NTFileCreationDisposition
- ntFileOptions = fs.NTCreateOptions
-)
-
-type ioStatusBlock struct {
- Status, Information uintptr
-}
-
-// typedef struct _OBJECT_ATTRIBUTES {
-// ULONG Length;
-// HANDLE RootDirectory;
-// PUNICODE_STRING ObjectName;
-// ULONG Attributes;
-// PVOID SecurityDescriptor;
-// PVOID SecurityQualityOfService;
-// } OBJECT_ATTRIBUTES;
-//
-// https://learn.microsoft.com/en-us/windows/win32/api/ntdef/ns-ntdef-_object_attributes
-type objectAttributes struct {
- Length uintptr
- RootDirectory uintptr
- ObjectName *unicodeString
- Attributes uintptr
- SecurityDescriptor *securityDescriptor
- SecurityQoS uintptr
-}
-
-type unicodeString struct {
- Length uint16
- MaximumLength uint16
- Buffer uintptr
-}
-
-// typedef struct _SECURITY_DESCRIPTOR {
-// BYTE Revision;
-// BYTE Sbz1;
-// SECURITY_DESCRIPTOR_CONTROL Control;
-// PSID Owner;
-// PSID Group;
-// PACL Sacl;
-// PACL Dacl;
-// } SECURITY_DESCRIPTOR, *PISECURITY_DESCRIPTOR;
-//
-// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-security_descriptor
-type securityDescriptor struct {
- Revision byte
- Sbz1 byte
- Control uint16
- Owner uintptr
- Group uintptr
- Sacl uintptr //revive:disable-line:var-naming SACL, not Sacl
- Dacl uintptr //revive:disable-line:var-naming DACL, not Dacl
-}
-
-type ntStatus int32
-
-func (status ntStatus) Err() error {
- if status >= 0 {
- return nil
- }
- return rtlNtStatusToDosError(status)
-}
-
-var (
- // ErrPipeListenerClosed is returned for pipe operations on listeners that have been closed.
- ErrPipeListenerClosed = net.ErrClosed
-
- errPipeWriteClosed = errors.New("pipe has been closed for write")
-)
-
-type win32Pipe struct {
- *win32File
- path string
-}
-
-var _ PipeConn = (*win32Pipe)(nil)
-
-type win32MessageBytePipe struct {
- win32Pipe
- writeClosed bool
- readEOF bool
-}
-
-type pipeAddress string
-
-func (f *win32Pipe) LocalAddr() net.Addr {
- return pipeAddress(f.path)
-}
-
-func (f *win32Pipe) RemoteAddr() net.Addr {
- return pipeAddress(f.path)
-}
-
-func (f *win32Pipe) SetDeadline(t time.Time) error {
- if err := f.SetReadDeadline(t); err != nil {
- return err
- }
- return f.SetWriteDeadline(t)
-}
-
-func (f *win32Pipe) Disconnect() error {
- return disconnectNamedPipe(f.win32File.handle)
-}
-
-// CloseWrite closes the write side of a message pipe in byte mode.
-func (f *win32MessageBytePipe) CloseWrite() error {
- if f.writeClosed {
- return errPipeWriteClosed
- }
- err := f.win32File.Flush()
- if err != nil {
- return err
- }
- _, err = f.win32File.Write(nil)
- if err != nil {
- return err
- }
- f.writeClosed = true
- return nil
-}
-
-// Write writes bytes to a message pipe in byte mode. Zero-byte writes are ignored, since
-// they are used to implement CloseWrite().
-func (f *win32MessageBytePipe) Write(b []byte) (int, error) {
- if f.writeClosed {
- return 0, errPipeWriteClosed
- }
- if len(b) == 0 {
- return 0, nil
- }
- return f.win32File.Write(b)
-}
-
-// Read reads bytes from a message pipe in byte mode. A read of a zero-byte message on a message
-// mode pipe will return io.EOF, as will all subsequent reads.
-func (f *win32MessageBytePipe) Read(b []byte) (int, error) {
- if f.readEOF {
- return 0, io.EOF
- }
- n, err := f.win32File.Read(b)
- if err == io.EOF { //nolint:errorlint
- // If this was the result of a zero-byte read, then
- // it is possible that the read was due to a zero-size
- // message. Since we are simulating CloseWrite with a
- // zero-byte message, ensure that all future Read() calls
- // also return EOF.
- f.readEOF = true
- } else if err == windows.ERROR_MORE_DATA { //nolint:errorlint // err is Errno
- // ERROR_MORE_DATA indicates that the pipe's read mode is message mode
- // and the message still has more bytes. Treat this as a success, since
- // this package presents all named pipes as byte streams.
- err = nil
- }
- return n, err
-}
-
-func (pipeAddress) Network() string {
- return "pipe"
-}
-
-func (s pipeAddress) String() string {
- return string(s)
-}
-
-// tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout.
-func tryDialPipe(ctx context.Context, path *string, access fs.AccessMask, impLevel PipeImpLevel) (windows.Handle, error) {
- for {
- select {
- case <-ctx.Done():
- return windows.Handle(0), ctx.Err()
- default:
- h, err := fs.CreateFile(*path,
- access,
- 0, // mode
- nil, // security attributes
- fs.OPEN_EXISTING,
- fs.FILE_FLAG_OVERLAPPED|fs.SECURITY_SQOS_PRESENT|fs.FileSQSFlag(impLevel),
- 0, // template file handle
- )
- if err == nil {
- return h, nil
- }
- if err != windows.ERROR_PIPE_BUSY { //nolint:errorlint // err is Errno
- return h, &os.PathError{Err: err, Op: "open", Path: *path}
- }
- // Wait 10 msec and try again. This is a rather simplistic
- // view, as we always try each 10 milliseconds.
- time.Sleep(10 * time.Millisecond)
- }
- }
-}
-
-// DialPipe connects to a named pipe by path, timing out if the connection
-// takes longer than the specified duration. If timeout is nil, then we use
-// a default timeout of 2 seconds. (We do not use WaitNamedPipe.)
-func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
- var absTimeout time.Time
- if timeout != nil {
- absTimeout = time.Now().Add(*timeout)
- } else {
- absTimeout = time.Now().Add(2 * time.Second)
- }
- ctx, cancel := context.WithDeadline(context.Background(), absTimeout)
- defer cancel()
- conn, err := DialPipeContext(ctx, path)
- if errors.Is(err, context.DeadlineExceeded) {
- return nil, ErrTimeout
- }
- return conn, err
-}
-
-// DialPipeContext attempts to connect to a named pipe by `path` until `ctx`
-// cancellation or timeout.
-func DialPipeContext(ctx context.Context, path string) (net.Conn, error) {
- return DialPipeAccess(ctx, path, uint32(fs.GENERIC_READ|fs.GENERIC_WRITE))
-}
-
-// PipeImpLevel is an enumeration of impersonation levels that may be set
-// when calling DialPipeAccessImpersonation.
-type PipeImpLevel uint32
-
-const (
- PipeImpLevelAnonymous = PipeImpLevel(fs.SECURITY_ANONYMOUS)
- PipeImpLevelIdentification = PipeImpLevel(fs.SECURITY_IDENTIFICATION)
- PipeImpLevelImpersonation = PipeImpLevel(fs.SECURITY_IMPERSONATION)
- PipeImpLevelDelegation = PipeImpLevel(fs.SECURITY_DELEGATION)
-)
-
-// DialPipeAccess attempts to connect to a named pipe by `path` with `access` until `ctx`
-// cancellation or timeout.
-func DialPipeAccess(ctx context.Context, path string, access uint32) (net.Conn, error) {
- return DialPipeAccessImpLevel(ctx, path, access, PipeImpLevelAnonymous)
-}
-
-// DialPipeAccessImpLevel attempts to connect to a named pipe by `path` with
-// `access` at `impLevel` until `ctx` cancellation or timeout. The other
-// DialPipe* implementations use PipeImpLevelAnonymous.
-func DialPipeAccessImpLevel(ctx context.Context, path string, access uint32, impLevel PipeImpLevel) (net.Conn, error) {
- var err error
- var h windows.Handle
- h, err = tryDialPipe(ctx, &path, fs.AccessMask(access), impLevel)
- if err != nil {
- return nil, err
- }
-
- var flags uint32
- err = getNamedPipeInfo(h, &flags, nil, nil, nil)
- if err != nil {
- return nil, err
- }
-
- f, err := makeWin32File(h)
- if err != nil {
- windows.Close(h)
- return nil, err
- }
-
- // If the pipe is in message mode, return a message byte pipe, which
- // supports CloseWrite().
- if flags&windows.PIPE_TYPE_MESSAGE != 0 {
- return &win32MessageBytePipe{
- win32Pipe: win32Pipe{win32File: f, path: path},
- }, nil
- }
- return &win32Pipe{win32File: f, path: path}, nil
-}
-
-type acceptResponse struct {
- f *win32File
- err error
-}
-
-type win32PipeListener struct {
- firstHandle windows.Handle
- path string
- config PipeConfig
- acceptCh chan (chan acceptResponse)
- closeCh chan int
- doneCh chan int
-}
-
-func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (windows.Handle, error) {
- path16, err := windows.UTF16FromString(path)
- if err != nil {
- return 0, &os.PathError{Op: "open", Path: path, Err: err}
- }
-
- var oa objectAttributes
- oa.Length = unsafe.Sizeof(oa)
-
- var ntPath unicodeString
- if err := rtlDosPathNameToNtPathName(&path16[0],
- &ntPath,
- 0,
- 0,
- ).Err(); err != nil {
- return 0, &os.PathError{Op: "open", Path: path, Err: err}
- }
- defer windows.LocalFree(windows.Handle(ntPath.Buffer)) //nolint:errcheck
- oa.ObjectName = &ntPath
- oa.Attributes = windows.OBJ_CASE_INSENSITIVE
-
- // The security descriptor is only needed for the first pipe.
- if first {
- if sd != nil {
- //todo: does `sdb` need to be allocated on the heap, or can go allocate it?
- l := uint32(len(sd))
- sdb, err := windows.LocalAlloc(0, l)
- if err != nil {
- return 0, fmt.Errorf("LocalAlloc for security descriptor with of length %d: %w", l, err)
- }
- defer windows.LocalFree(windows.Handle(sdb)) //nolint:errcheck
- copy((*[0xffff]byte)(unsafe.Pointer(sdb))[:], sd)
- oa.SecurityDescriptor = (*securityDescriptor)(unsafe.Pointer(sdb))
- } else {
- // Construct the default named pipe security descriptor.
- var dacl uintptr
- if err := rtlDefaultNpAcl(&dacl).Err(); err != nil {
- return 0, fmt.Errorf("getting default named pipe ACL: %w", err)
- }
- defer windows.LocalFree(windows.Handle(dacl)) //nolint:errcheck
-
- sdb := &securityDescriptor{
- Revision: 1,
- Control: windows.SE_DACL_PRESENT,
- Dacl: dacl,
- }
- oa.SecurityDescriptor = sdb
- }
- }
-
- typ := uint32(windows.FILE_PIPE_REJECT_REMOTE_CLIENTS)
- if c.MessageMode {
- typ |= windows.FILE_PIPE_MESSAGE_TYPE
- }
-
- disposition := fs.FILE_OPEN
- access := fs.GENERIC_READ | fs.GENERIC_WRITE | fs.SYNCHRONIZE
- if first {
- disposition = fs.FILE_CREATE
- // By not asking for read or write access, the named pipe file system
- // will put this pipe into an initially disconnected state, blocking
- // client connections until the next call with first == false.
- access = fs.SYNCHRONIZE
- }
-
- timeout := int64(-50 * 10000) // 50ms
-
- var (
- h windows.Handle
- iosb ioStatusBlock
- )
- err = ntCreateNamedPipeFile(&h,
- access,
- &oa,
- &iosb,
- fs.FILE_SHARE_READ|fs.FILE_SHARE_WRITE,
- disposition,
- 0,
- typ,
- 0,
- 0,
- 0xffffffff,
- uint32(c.InputBufferSize),
- uint32(c.OutputBufferSize),
- &timeout).Err()
- if err != nil {
- return 0, &os.PathError{Op: "open", Path: path, Err: err}
- }
-
- runtime.KeepAlive(ntPath)
- return h, nil
-}
-
-func (l *win32PipeListener) makeServerPipe() (*win32File, error) {
- h, err := makeServerPipeHandle(l.path, nil, &l.config, false)
- if err != nil {
- return nil, err
- }
- f, err := makeWin32File(h)
- if err != nil {
- windows.Close(h)
- return nil, err
- }
- return f, nil
-}
-
-func (l *win32PipeListener) makeConnectedServerPipe() (*win32File, error) {
- p, err := l.makeServerPipe()
- if err != nil {
- return nil, err
- }
-
- // Wait for the client to connect.
- ch := make(chan error)
- go func(p *win32File) {
- ch <- connectPipe(p)
- }(p)
-
- select {
- case err = <-ch:
- if err != nil {
- p.Close()
- p = nil
- }
- case <-l.closeCh:
- // Abort the connect request by closing the handle.
- p.Close()
- p = nil
- err = <-ch
- if err == nil || err == ErrFileClosed { //nolint:errorlint // err is Errno
- err = ErrPipeListenerClosed
- }
- }
- return p, err
-}
-
-func (l *win32PipeListener) listenerRoutine() {
- closed := false
- for !closed {
- select {
- case <-l.closeCh:
- closed = true
- case responseCh := <-l.acceptCh:
- var (
- p *win32File
- err error
- )
- for {
- p, err = l.makeConnectedServerPipe()
- // If the connection was immediately closed by the client, try
- // again.
- if err != windows.ERROR_NO_DATA { //nolint:errorlint // err is Errno
- break
- }
- }
- responseCh <- acceptResponse{p, err}
- closed = err == ErrPipeListenerClosed //nolint:errorlint // err is Errno
- }
- }
- windows.Close(l.firstHandle)
- l.firstHandle = 0
- // Notify Close() and Accept() callers that the handle has been closed.
- close(l.doneCh)
-}
-
-// PipeConfig contain configuration for the pipe listener.
-type PipeConfig struct {
- // SecurityDescriptor contains a Windows security descriptor in SDDL format.
- SecurityDescriptor string
-
- // MessageMode determines whether the pipe is in byte or message mode. In either
- // case the pipe is read in byte mode by default. The only practical difference in
- // this implementation is that CloseWrite() is only supported for message mode pipes;
- // CloseWrite() is implemented as a zero-byte write, but zero-byte writes are only
- // transferred to the reader (and returned as io.EOF in this implementation)
- // when the pipe is in message mode.
- MessageMode bool
-
- // InputBufferSize specifies the size of the input buffer, in bytes.
- InputBufferSize int32
-
- // OutputBufferSize specifies the size of the output buffer, in bytes.
- OutputBufferSize int32
-}
-
-// ListenPipe creates a listener on a Windows named pipe path, e.g. \\.\pipe\mypipe.
-// The pipe must not already exist.
-func ListenPipe(path string, c *PipeConfig) (net.Listener, error) {
- var (
- sd []byte
- err error
- )
- if c == nil {
- c = &PipeConfig{}
- }
- if c.SecurityDescriptor != "" {
- sd, err = SddlToSecurityDescriptor(c.SecurityDescriptor)
- if err != nil {
- return nil, err
- }
- }
- h, err := makeServerPipeHandle(path, sd, c, true)
- if err != nil {
- return nil, err
- }
- l := &win32PipeListener{
- firstHandle: h,
- path: path,
- config: *c,
- acceptCh: make(chan (chan acceptResponse)),
- closeCh: make(chan int),
- doneCh: make(chan int),
- }
- go l.listenerRoutine()
- return l, nil
-}
-
-func connectPipe(p *win32File) error {
- c, err := p.prepareIO()
- if err != nil {
- return err
- }
- defer p.wg.Done()
-
- err = connectNamedPipe(p.handle, &c.o)
- _, err = p.asyncIO(c, nil, 0, err)
- if err != nil && err != windows.ERROR_PIPE_CONNECTED { //nolint:errorlint // err is Errno
- return err
- }
- return nil
-}
-
-func (l *win32PipeListener) Accept() (net.Conn, error) {
- ch := make(chan acceptResponse)
- select {
- case l.acceptCh <- ch:
- response := <-ch
- err := response.err
- if err != nil {
- return nil, err
- }
- if l.config.MessageMode {
- return &win32MessageBytePipe{
- win32Pipe: win32Pipe{win32File: response.f, path: l.path},
- }, nil
- }
- return &win32Pipe{win32File: response.f, path: l.path}, nil
- case <-l.doneCh:
- return nil, ErrPipeListenerClosed
- }
-}
-
-func (l *win32PipeListener) Close() error {
- select {
- case l.closeCh <- 1:
- <-l.doneCh
- case <-l.doneCh:
- }
- return nil
-}
-
-func (l *win32PipeListener) Addr() net.Addr {
- return pipeAddress(l.path)
-}
diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go
deleted file mode 100644
index 48ce4e9..0000000
--- a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go
+++ /dev/null
@@ -1,232 +0,0 @@
-// Package guid provides a GUID type. The backing structure for a GUID is
-// identical to that used by the golang.org/x/sys/windows GUID type.
-// There are two main binary encodings used for a GUID, the big-endian encoding,
-// and the Windows (mixed-endian) encoding. See here for details:
-// https://en.wikipedia.org/wiki/Universally_unique_identifier#Encoding
-package guid
-
-import (
- "crypto/rand"
- "crypto/sha1" //nolint:gosec // not used for secure application
- "encoding"
- "encoding/binary"
- "fmt"
- "strconv"
-)
-
-//go:generate go run golang.org/x/tools/cmd/stringer -type=Variant -trimprefix=Variant -linecomment
-
-// Variant specifies which GUID variant (or "type") of the GUID. It determines
-// how the entirety of the rest of the GUID is interpreted.
-type Variant uint8
-
-// The variants specified by RFC 4122 section 4.1.1.
-const (
- // VariantUnknown specifies a GUID variant which does not conform to one of
- // the variant encodings specified in RFC 4122.
- VariantUnknown Variant = iota
- VariantNCS
- VariantRFC4122 // RFC 4122
- VariantMicrosoft
- VariantFuture
-)
-
-// Version specifies how the bits in the GUID were generated. For instance, a
-// version 4 GUID is randomly generated, and a version 5 is generated from the
-// hash of an input string.
-type Version uint8
-
-func (v Version) String() string {
- return strconv.FormatUint(uint64(v), 10)
-}
-
-var _ = (encoding.TextMarshaler)(GUID{})
-var _ = (encoding.TextUnmarshaler)(&GUID{})
-
-// NewV4 returns a new version 4 (pseudorandom) GUID, as defined by RFC 4122.
-func NewV4() (GUID, error) {
- var b [16]byte
- if _, err := rand.Read(b[:]); err != nil {
- return GUID{}, err
- }
-
- g := FromArray(b)
- g.setVersion(4) // Version 4 means randomly generated.
- g.setVariant(VariantRFC4122)
-
- return g, nil
-}
-
-// NewV5 returns a new version 5 (generated from a string via SHA-1 hashing)
-// GUID, as defined by RFC 4122. The RFC is unclear on the encoding of the name,
-// and the sample code treats it as a series of bytes, so we do the same here.
-//
-// Some implementations, such as those found on Windows, treat the name as a
-// big-endian UTF16 stream of bytes. If that is desired, the string can be
-// encoded as such before being passed to this function.
-func NewV5(namespace GUID, name []byte) (GUID, error) {
- b := sha1.New() //nolint:gosec // not used for secure application
- namespaceBytes := namespace.ToArray()
- b.Write(namespaceBytes[:])
- b.Write(name)
-
- a := [16]byte{}
- copy(a[:], b.Sum(nil))
-
- g := FromArray(a)
- g.setVersion(5) // Version 5 means generated from a string.
- g.setVariant(VariantRFC4122)
-
- return g, nil
-}
-
-func fromArray(b [16]byte, order binary.ByteOrder) GUID {
- var g GUID
- g.Data1 = order.Uint32(b[0:4])
- g.Data2 = order.Uint16(b[4:6])
- g.Data3 = order.Uint16(b[6:8])
- copy(g.Data4[:], b[8:16])
- return g
-}
-
-func (g GUID) toArray(order binary.ByteOrder) [16]byte {
- b := [16]byte{}
- order.PutUint32(b[0:4], g.Data1)
- order.PutUint16(b[4:6], g.Data2)
- order.PutUint16(b[6:8], g.Data3)
- copy(b[8:16], g.Data4[:])
- return b
-}
-
-// FromArray constructs a GUID from a big-endian encoding array of 16 bytes.
-func FromArray(b [16]byte) GUID {
- return fromArray(b, binary.BigEndian)
-}
-
-// ToArray returns an array of 16 bytes representing the GUID in big-endian
-// encoding.
-func (g GUID) ToArray() [16]byte {
- return g.toArray(binary.BigEndian)
-}
-
-// FromWindowsArray constructs a GUID from a Windows encoding array of bytes.
-func FromWindowsArray(b [16]byte) GUID {
- return fromArray(b, binary.LittleEndian)
-}
-
-// ToWindowsArray returns an array of 16 bytes representing the GUID in Windows
-// encoding.
-func (g GUID) ToWindowsArray() [16]byte {
- return g.toArray(binary.LittleEndian)
-}
-
-func (g GUID) String() string {
- return fmt.Sprintf(
- "%08x-%04x-%04x-%04x-%012x",
- g.Data1,
- g.Data2,
- g.Data3,
- g.Data4[:2],
- g.Data4[2:])
-}
-
-// FromString parses a string containing a GUID and returns the GUID. The only
-// format currently supported is the `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
-// format.
-func FromString(s string) (GUID, error) {
- if len(s) != 36 {
- return GUID{}, fmt.Errorf("invalid GUID %q", s)
- }
- if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
- return GUID{}, fmt.Errorf("invalid GUID %q", s)
- }
-
- var g GUID
-
- data1, err := strconv.ParseUint(s[0:8], 16, 32)
- if err != nil {
- return GUID{}, fmt.Errorf("invalid GUID %q", s)
- }
- g.Data1 = uint32(data1)
-
- data2, err := strconv.ParseUint(s[9:13], 16, 16)
- if err != nil {
- return GUID{}, fmt.Errorf("invalid GUID %q", s)
- }
- g.Data2 = uint16(data2)
-
- data3, err := strconv.ParseUint(s[14:18], 16, 16)
- if err != nil {
- return GUID{}, fmt.Errorf("invalid GUID %q", s)
- }
- g.Data3 = uint16(data3)
-
- for i, x := range []int{19, 21, 24, 26, 28, 30, 32, 34} {
- v, err := strconv.ParseUint(s[x:x+2], 16, 8)
- if err != nil {
- return GUID{}, fmt.Errorf("invalid GUID %q", s)
- }
- g.Data4[i] = uint8(v)
- }
-
- return g, nil
-}
-
-func (g *GUID) setVariant(v Variant) {
- d := g.Data4[0]
- switch v {
- case VariantNCS:
- d = (d & 0x7f)
- case VariantRFC4122:
- d = (d & 0x3f) | 0x80
- case VariantMicrosoft:
- d = (d & 0x1f) | 0xc0
- case VariantFuture:
- d = (d & 0x0f) | 0xe0
- case VariantUnknown:
- fallthrough
- default:
- panic(fmt.Sprintf("invalid variant: %d", v))
- }
- g.Data4[0] = d
-}
-
-// Variant returns the GUID variant, as defined in RFC 4122.
-func (g GUID) Variant() Variant {
- b := g.Data4[0]
- if b&0x80 == 0 {
- return VariantNCS
- } else if b&0xc0 == 0x80 {
- return VariantRFC4122
- } else if b&0xe0 == 0xc0 {
- return VariantMicrosoft
- } else if b&0xe0 == 0xe0 {
- return VariantFuture
- }
- return VariantUnknown
-}
-
-func (g *GUID) setVersion(v Version) {
- g.Data3 = (g.Data3 & 0x0fff) | (uint16(v) << 12)
-}
-
-// Version returns the GUID version, as defined in RFC 4122.
-func (g GUID) Version() Version {
- return Version((g.Data3 & 0xF000) >> 12)
-}
-
-// MarshalText returns the textual representation of the GUID.
-func (g GUID) MarshalText() ([]byte, error) {
- return []byte(g.String()), nil
-}
-
-// UnmarshalText takes the textual representation of a GUID, and unmarhals it
-// into this GUID.
-func (g *GUID) UnmarshalText(text []byte) error {
- g2, err := FromString(string(text))
- if err != nil {
- return err
- }
- *g = g2
- return nil
-}
diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go
deleted file mode 100644
index 805bd35..0000000
--- a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go
+++ /dev/null
@@ -1,16 +0,0 @@
-//go:build !windows
-// +build !windows
-
-package guid
-
-// GUID represents a GUID/UUID. It has the same structure as
-// golang.org/x/sys/windows.GUID so that it can be used with functions expecting
-// that type. It is defined as its own type as that is only available to builds
-// targeted at `windows`. The representation matches that used by native Windows
-// code.
-type GUID struct {
- Data1 uint32
- Data2 uint16
- Data3 uint16
- Data4 [8]byte
-}
diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go
deleted file mode 100644
index 27e45ee..0000000
--- a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go
+++ /dev/null
@@ -1,13 +0,0 @@
-//go:build windows
-// +build windows
-
-package guid
-
-import "golang.org/x/sys/windows"
-
-// GUID represents a GUID/UUID. It has the same structure as
-// golang.org/x/sys/windows.GUID so that it can be used with functions expecting
-// that type. It is defined as its own type so that stringification and
-// marshaling can be supported. The representation matches that used by native
-// Windows code.
-type GUID windows.GUID
diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/variant_string.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/variant_string.go
deleted file mode 100644
index 4076d31..0000000
--- a/vendor/github.com/Microsoft/go-winio/pkg/guid/variant_string.go
+++ /dev/null
@@ -1,27 +0,0 @@
-// Code generated by "stringer -type=Variant -trimprefix=Variant -linecomment"; DO NOT EDIT.
-
-package guid
-
-import "strconv"
-
-func _() {
- // An "invalid array index" compiler error signifies that the constant values have changed.
- // Re-run the stringer command to generate them again.
- var x [1]struct{}
- _ = x[VariantUnknown-0]
- _ = x[VariantNCS-1]
- _ = x[VariantRFC4122-2]
- _ = x[VariantMicrosoft-3]
- _ = x[VariantFuture-4]
-}
-
-const _Variant_name = "UnknownNCSRFC 4122MicrosoftFuture"
-
-var _Variant_index = [...]uint8{0, 7, 10, 18, 27, 33}
-
-func (i Variant) String() string {
- if i >= Variant(len(_Variant_index)-1) {
- return "Variant(" + strconv.FormatInt(int64(i), 10) + ")"
- }
- return _Variant_name[_Variant_index[i]:_Variant_index[i+1]]
-}
diff --git a/vendor/github.com/Microsoft/go-winio/privilege.go b/vendor/github.com/Microsoft/go-winio/privilege.go
deleted file mode 100644
index d9b90b6..0000000
--- a/vendor/github.com/Microsoft/go-winio/privilege.go
+++ /dev/null
@@ -1,196 +0,0 @@
-//go:build windows
-// +build windows
-
-package winio
-
-import (
- "bytes"
- "encoding/binary"
- "fmt"
- "runtime"
- "sync"
- "unicode/utf16"
-
- "golang.org/x/sys/windows"
-)
-
-//sys adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges
-//sys impersonateSelf(level uint32) (err error) = advapi32.ImpersonateSelf
-//sys revertToSelf() (err error) = advapi32.RevertToSelf
-//sys openThreadToken(thread windows.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) = advapi32.OpenThreadToken
-//sys getCurrentThread() (h windows.Handle) = GetCurrentThread
-//sys lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) = advapi32.LookupPrivilegeValueW
-//sys lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW
-//sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW
-
-const (
- //revive:disable-next-line:var-naming ALL_CAPS
- SE_PRIVILEGE_ENABLED = windows.SE_PRIVILEGE_ENABLED
-
- //revive:disable-next-line:var-naming ALL_CAPS
- ERROR_NOT_ALL_ASSIGNED windows.Errno = windows.ERROR_NOT_ALL_ASSIGNED
-
- SeBackupPrivilege = "SeBackupPrivilege"
- SeRestorePrivilege = "SeRestorePrivilege"
- SeSecurityPrivilege = "SeSecurityPrivilege"
-)
-
-var (
- privNames = make(map[string]uint64)
- privNameMutex sync.Mutex
-)
-
-// PrivilegeError represents an error enabling privileges.
-type PrivilegeError struct {
- privileges []uint64
-}
-
-func (e *PrivilegeError) Error() string {
- s := "Could not enable privilege "
- if len(e.privileges) > 1 {
- s = "Could not enable privileges "
- }
- for i, p := range e.privileges {
- if i != 0 {
- s += ", "
- }
- s += `"`
- s += getPrivilegeName(p)
- s += `"`
- }
- return s
-}
-
-// RunWithPrivilege enables a single privilege for a function call.
-func RunWithPrivilege(name string, fn func() error) error {
- return RunWithPrivileges([]string{name}, fn)
-}
-
-// RunWithPrivileges enables privileges for a function call.
-func RunWithPrivileges(names []string, fn func() error) error {
- privileges, err := mapPrivileges(names)
- if err != nil {
- return err
- }
- runtime.LockOSThread()
- defer runtime.UnlockOSThread()
- token, err := newThreadToken()
- if err != nil {
- return err
- }
- defer releaseThreadToken(token)
- err = adjustPrivileges(token, privileges, SE_PRIVILEGE_ENABLED)
- if err != nil {
- return err
- }
- return fn()
-}
-
-func mapPrivileges(names []string) ([]uint64, error) {
- privileges := make([]uint64, 0, len(names))
- privNameMutex.Lock()
- defer privNameMutex.Unlock()
- for _, name := range names {
- p, ok := privNames[name]
- if !ok {
- err := lookupPrivilegeValue("", name, &p)
- if err != nil {
- return nil, err
- }
- privNames[name] = p
- }
- privileges = append(privileges, p)
- }
- return privileges, nil
-}
-
-// EnableProcessPrivileges enables privileges globally for the process.
-func EnableProcessPrivileges(names []string) error {
- return enableDisableProcessPrivilege(names, SE_PRIVILEGE_ENABLED)
-}
-
-// DisableProcessPrivileges disables privileges globally for the process.
-func DisableProcessPrivileges(names []string) error {
- return enableDisableProcessPrivilege(names, 0)
-}
-
-func enableDisableProcessPrivilege(names []string, action uint32) error {
- privileges, err := mapPrivileges(names)
- if err != nil {
- return err
- }
-
- p := windows.CurrentProcess()
- var token windows.Token
- err = windows.OpenProcessToken(p, windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, &token)
- if err != nil {
- return err
- }
-
- defer token.Close()
- return adjustPrivileges(token, privileges, action)
-}
-
-func adjustPrivileges(token windows.Token, privileges []uint64, action uint32) error {
- var b bytes.Buffer
- _ = binary.Write(&b, binary.LittleEndian, uint32(len(privileges)))
- for _, p := range privileges {
- _ = binary.Write(&b, binary.LittleEndian, p)
- _ = binary.Write(&b, binary.LittleEndian, action)
- }
- prevState := make([]byte, b.Len())
- reqSize := uint32(0)
- success, err := adjustTokenPrivileges(token, false, &b.Bytes()[0], uint32(len(prevState)), &prevState[0], &reqSize)
- if !success {
- return err
- }
- if err == ERROR_NOT_ALL_ASSIGNED { //nolint:errorlint // err is Errno
- return &PrivilegeError{privileges}
- }
- return nil
-}
-
-func getPrivilegeName(luid uint64) string {
- var nameBuffer [256]uint16
- bufSize := uint32(len(nameBuffer))
- err := lookupPrivilegeName("", &luid, &nameBuffer[0], &bufSize)
- if err != nil {
- return fmt.Sprintf("", luid)
- }
-
- var displayNameBuffer [256]uint16
- displayBufSize := uint32(len(displayNameBuffer))
- var langID uint32
- err = lookupPrivilegeDisplayName("", &nameBuffer[0], &displayNameBuffer[0], &displayBufSize, &langID)
- if err != nil {
- return fmt.Sprintf("", string(utf16.Decode(nameBuffer[:bufSize])))
- }
-
- return string(utf16.Decode(displayNameBuffer[:displayBufSize]))
-}
-
-func newThreadToken() (windows.Token, error) {
- err := impersonateSelf(windows.SecurityImpersonation)
- if err != nil {
- return 0, err
- }
-
- var token windows.Token
- err = openThreadToken(getCurrentThread(), windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, false, &token)
- if err != nil {
- rerr := revertToSelf()
- if rerr != nil {
- panic(rerr)
- }
- return 0, err
- }
- return token, nil
-}
-
-func releaseThreadToken(h windows.Token) {
- err := revertToSelf()
- if err != nil {
- panic(err)
- }
- h.Close()
-}
diff --git a/vendor/github.com/Microsoft/go-winio/reparse.go b/vendor/github.com/Microsoft/go-winio/reparse.go
deleted file mode 100644
index 67d1a10..0000000
--- a/vendor/github.com/Microsoft/go-winio/reparse.go
+++ /dev/null
@@ -1,131 +0,0 @@
-//go:build windows
-// +build windows
-
-package winio
-
-import (
- "bytes"
- "encoding/binary"
- "fmt"
- "strings"
- "unicode/utf16"
- "unsafe"
-)
-
-const (
- reparseTagMountPoint = 0xA0000003
- reparseTagSymlink = 0xA000000C
-)
-
-type reparseDataBuffer struct {
- ReparseTag uint32
- ReparseDataLength uint16
- Reserved uint16
- SubstituteNameOffset uint16
- SubstituteNameLength uint16
- PrintNameOffset uint16
- PrintNameLength uint16
-}
-
-// ReparsePoint describes a Win32 symlink or mount point.
-type ReparsePoint struct {
- Target string
- IsMountPoint bool
-}
-
-// UnsupportedReparsePointError is returned when trying to decode a non-symlink or
-// mount point reparse point.
-type UnsupportedReparsePointError struct {
- Tag uint32
-}
-
-func (e *UnsupportedReparsePointError) Error() string {
- return fmt.Sprintf("unsupported reparse point %x", e.Tag)
-}
-
-// DecodeReparsePoint decodes a Win32 REPARSE_DATA_BUFFER structure containing either a symlink
-// or a mount point.
-func DecodeReparsePoint(b []byte) (*ReparsePoint, error) {
- tag := binary.LittleEndian.Uint32(b[0:4])
- return DecodeReparsePointData(tag, b[8:])
-}
-
-func DecodeReparsePointData(tag uint32, b []byte) (*ReparsePoint, error) {
- isMountPoint := false
- switch tag {
- case reparseTagMountPoint:
- isMountPoint = true
- case reparseTagSymlink:
- default:
- return nil, &UnsupportedReparsePointError{tag}
- }
- nameOffset := 8 + binary.LittleEndian.Uint16(b[4:6])
- if !isMountPoint {
- nameOffset += 4
- }
- nameLength := binary.LittleEndian.Uint16(b[6:8])
- name := make([]uint16, nameLength/2)
- err := binary.Read(bytes.NewReader(b[nameOffset:nameOffset+nameLength]), binary.LittleEndian, &name)
- if err != nil {
- return nil, err
- }
- return &ReparsePoint{string(utf16.Decode(name)), isMountPoint}, nil
-}
-
-func isDriveLetter(c byte) bool {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
-}
-
-// EncodeReparsePoint encodes a Win32 REPARSE_DATA_BUFFER structure describing a symlink or
-// mount point.
-func EncodeReparsePoint(rp *ReparsePoint) []byte {
- // Generate an NT path and determine if this is a relative path.
- var ntTarget string
- relative := false
- if strings.HasPrefix(rp.Target, `\\?\`) {
- ntTarget = `\??\` + rp.Target[4:]
- } else if strings.HasPrefix(rp.Target, `\\`) {
- ntTarget = `\??\UNC\` + rp.Target[2:]
- } else if len(rp.Target) >= 2 && isDriveLetter(rp.Target[0]) && rp.Target[1] == ':' {
- ntTarget = `\??\` + rp.Target
- } else {
- ntTarget = rp.Target
- relative = true
- }
-
- // The paths must be NUL-terminated even though they are counted strings.
- target16 := utf16.Encode([]rune(rp.Target + "\x00"))
- ntTarget16 := utf16.Encode([]rune(ntTarget + "\x00"))
-
- size := int(unsafe.Sizeof(reparseDataBuffer{})) - 8
- size += len(ntTarget16)*2 + len(target16)*2
-
- tag := uint32(reparseTagMountPoint)
- if !rp.IsMountPoint {
- tag = reparseTagSymlink
- size += 4 // Add room for symlink flags
- }
-
- data := reparseDataBuffer{
- ReparseTag: tag,
- ReparseDataLength: uint16(size),
- SubstituteNameOffset: 0,
- SubstituteNameLength: uint16((len(ntTarget16) - 1) * 2),
- PrintNameOffset: uint16(len(ntTarget16) * 2),
- PrintNameLength: uint16((len(target16) - 1) * 2),
- }
-
- var b bytes.Buffer
- _ = binary.Write(&b, binary.LittleEndian, &data)
- if !rp.IsMountPoint {
- flags := uint32(0)
- if relative {
- flags |= 1
- }
- _ = binary.Write(&b, binary.LittleEndian, flags)
- }
-
- _ = binary.Write(&b, binary.LittleEndian, ntTarget16)
- _ = binary.Write(&b, binary.LittleEndian, target16)
- return b.Bytes()
-}
diff --git a/vendor/github.com/Microsoft/go-winio/sd.go b/vendor/github.com/Microsoft/go-winio/sd.go
deleted file mode 100644
index c3685e9..0000000
--- a/vendor/github.com/Microsoft/go-winio/sd.go
+++ /dev/null
@@ -1,133 +0,0 @@
-//go:build windows
-// +build windows
-
-package winio
-
-import (
- "errors"
- "fmt"
- "unsafe"
-
- "golang.org/x/sys/windows"
-)
-
-//sys lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountNameW
-//sys lookupAccountSid(systemName *uint16, sid *byte, name *uint16, nameSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountSidW
-//sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW
-//sys convertStringSidToSid(str *uint16, sid **byte) (err error) = advapi32.ConvertStringSidToSidW
-
-type AccountLookupError struct {
- Name string
- Err error
-}
-
-func (e *AccountLookupError) Error() string {
- if e.Name == "" {
- return "lookup account: empty account name specified"
- }
- var s string
- switch {
- case errors.Is(e.Err, windows.ERROR_INVALID_SID):
- s = "the security ID structure is invalid"
- case errors.Is(e.Err, windows.ERROR_NONE_MAPPED):
- s = "not found"
- default:
- s = e.Err.Error()
- }
- return "lookup account " + e.Name + ": " + s
-}
-
-func (e *AccountLookupError) Unwrap() error { return e.Err }
-
-type SddlConversionError struct {
- Sddl string
- Err error
-}
-
-func (e *SddlConversionError) Error() string {
- return "convert " + e.Sddl + ": " + e.Err.Error()
-}
-
-func (e *SddlConversionError) Unwrap() error { return e.Err }
-
-// LookupSidByName looks up the SID of an account by name
-//
-//revive:disable-next-line:var-naming SID, not Sid
-func LookupSidByName(name string) (sid string, err error) {
- if name == "" {
- return "", &AccountLookupError{name, windows.ERROR_NONE_MAPPED}
- }
-
- var sidSize, sidNameUse, refDomainSize uint32
- err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse)
- if err != nil && err != windows.ERROR_INSUFFICIENT_BUFFER { //nolint:errorlint // err is Errno
- return "", &AccountLookupError{name, err}
- }
- sidBuffer := make([]byte, sidSize)
- refDomainBuffer := make([]uint16, refDomainSize)
- err = lookupAccountName(nil, name, &sidBuffer[0], &sidSize, &refDomainBuffer[0], &refDomainSize, &sidNameUse)
- if err != nil {
- return "", &AccountLookupError{name, err}
- }
- var strBuffer *uint16
- err = convertSidToStringSid(&sidBuffer[0], &strBuffer)
- if err != nil {
- return "", &AccountLookupError{name, err}
- }
- sid = windows.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(strBuffer))[:])
- _, _ = windows.LocalFree(windows.Handle(unsafe.Pointer(strBuffer)))
- return sid, nil
-}
-
-// LookupNameBySid looks up the name of an account by SID
-//
-//revive:disable-next-line:var-naming SID, not Sid
-func LookupNameBySid(sid string) (name string, err error) {
- if sid == "" {
- return "", &AccountLookupError{sid, windows.ERROR_NONE_MAPPED}
- }
-
- sidBuffer, err := windows.UTF16PtrFromString(sid)
- if err != nil {
- return "", &AccountLookupError{sid, err}
- }
-
- var sidPtr *byte
- if err = convertStringSidToSid(sidBuffer, &sidPtr); err != nil {
- return "", &AccountLookupError{sid, err}
- }
- defer windows.LocalFree(windows.Handle(unsafe.Pointer(sidPtr))) //nolint:errcheck
-
- var nameSize, refDomainSize, sidNameUse uint32
- err = lookupAccountSid(nil, sidPtr, nil, &nameSize, nil, &refDomainSize, &sidNameUse)
- if err != nil && err != windows.ERROR_INSUFFICIENT_BUFFER { //nolint:errorlint // err is Errno
- return "", &AccountLookupError{sid, err}
- }
-
- nameBuffer := make([]uint16, nameSize)
- refDomainBuffer := make([]uint16, refDomainSize)
- err = lookupAccountSid(nil, sidPtr, &nameBuffer[0], &nameSize, &refDomainBuffer[0], &refDomainSize, &sidNameUse)
- if err != nil {
- return "", &AccountLookupError{sid, err}
- }
-
- name = windows.UTF16ToString(nameBuffer)
- return name, nil
-}
-
-func SddlToSecurityDescriptor(sddl string) ([]byte, error) {
- sd, err := windows.SecurityDescriptorFromString(sddl)
- if err != nil {
- return nil, &SddlConversionError{Sddl: sddl, Err: err}
- }
- b := unsafe.Slice((*byte)(unsafe.Pointer(sd)), sd.Length())
- return b, nil
-}
-
-func SecurityDescriptorToSddl(sd []byte) (string, error) {
- if l := int(unsafe.Sizeof(windows.SECURITY_DESCRIPTOR{})); len(sd) < l {
- return "", fmt.Errorf("SecurityDescriptor (%d) smaller than expected (%d): %w", len(sd), l, windows.ERROR_INCORRECT_SIZE)
- }
- s := (*windows.SECURITY_DESCRIPTOR)(unsafe.Pointer(&sd[0]))
- return s.String(), nil
-}
diff --git a/vendor/github.com/Microsoft/go-winio/syscall.go b/vendor/github.com/Microsoft/go-winio/syscall.go
deleted file mode 100644
index a6ca111..0000000
--- a/vendor/github.com/Microsoft/go-winio/syscall.go
+++ /dev/null
@@ -1,5 +0,0 @@
-//go:build windows
-
-package winio
-
-//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go ./*.go
diff --git a/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go b/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go
deleted file mode 100644
index 89b66ed..0000000
--- a/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go
+++ /dev/null
@@ -1,378 +0,0 @@
-//go:build windows
-
-// Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT.
-
-package winio
-
-import (
- "syscall"
- "unsafe"
-
- "golang.org/x/sys/windows"
-)
-
-var _ unsafe.Pointer
-
-// Do the interface allocations only once for common
-// Errno values.
-const (
- errnoERROR_IO_PENDING = 997
-)
-
-var (
- errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
- errERROR_EINVAL error = syscall.EINVAL
-)
-
-// errnoErr returns common boxed Errno values, to prevent
-// allocations at runtime.
-func errnoErr(e syscall.Errno) error {
- switch e {
- case 0:
- return errERROR_EINVAL
- case errnoERROR_IO_PENDING:
- return errERROR_IO_PENDING
- }
- return e
-}
-
-var (
- modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
- modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
- modntdll = windows.NewLazySystemDLL("ntdll.dll")
- modws2_32 = windows.NewLazySystemDLL("ws2_32.dll")
-
- procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
- procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW")
- procConvertStringSidToSidW = modadvapi32.NewProc("ConvertStringSidToSidW")
- procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf")
- procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW")
- procLookupAccountSidW = modadvapi32.NewProc("LookupAccountSidW")
- procLookupPrivilegeDisplayNameW = modadvapi32.NewProc("LookupPrivilegeDisplayNameW")
- procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW")
- procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW")
- procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken")
- procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
- procBackupRead = modkernel32.NewProc("BackupRead")
- procBackupWrite = modkernel32.NewProc("BackupWrite")
- procCancelIoEx = modkernel32.NewProc("CancelIoEx")
- procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
- procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
- procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW")
- procDisconnectNamedPipe = modkernel32.NewProc("DisconnectNamedPipe")
- procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
- procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW")
- procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo")
- procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
- procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
- procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile")
- procRtlDefaultNpAcl = modntdll.NewProc("RtlDefaultNpAcl")
- procRtlDosPathNameToNtPathName_U = modntdll.NewProc("RtlDosPathNameToNtPathName_U")
- procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb")
- procWSAGetOverlappedResult = modws2_32.NewProc("WSAGetOverlappedResult")
-)
-
-func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) {
- var _p0 uint32
- if releaseAll {
- _p0 = 1
- }
- r0, _, e1 := syscall.SyscallN(procAdjustTokenPrivileges.Addr(), uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize)))
- success = r0 != 0
- if true {
- err = errnoErr(e1)
- }
- return
-}
-
-func convertSidToStringSid(sid *byte, str **uint16) (err error) {
- r1, _, e1 := syscall.SyscallN(procConvertSidToStringSidW.Addr(), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func convertStringSidToSid(str *uint16, sid **byte) (err error) {
- r1, _, e1 := syscall.SyscallN(procConvertStringSidToSidW.Addr(), uintptr(unsafe.Pointer(str)), uintptr(unsafe.Pointer(sid)))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func impersonateSelf(level uint32) (err error) {
- r1, _, e1 := syscall.SyscallN(procImpersonateSelf.Addr(), uintptr(level))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
- var _p0 *uint16
- _p0, err = syscall.UTF16PtrFromString(accountName)
- if err != nil {
- return
- }
- return _lookupAccountName(systemName, _p0, sid, sidSize, refDomain, refDomainSize, sidNameUse)
-}
-
-func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
- r1, _, e1 := syscall.SyscallN(procLookupAccountNameW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func lookupAccountSid(systemName *uint16, sid *byte, name *uint16, nameSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
- r1, _, e1 := syscall.SyscallN(procLookupAccountSidW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
- var _p0 *uint16
- _p0, err = syscall.UTF16PtrFromString(systemName)
- if err != nil {
- return
- }
- return _lookupPrivilegeDisplayName(_p0, name, buffer, size, languageId)
-}
-
-func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
- r1, _, e1 := syscall.SyscallN(procLookupPrivilegeDisplayNameW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(languageId)))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) {
- var _p0 *uint16
- _p0, err = syscall.UTF16PtrFromString(systemName)
- if err != nil {
- return
- }
- return _lookupPrivilegeName(_p0, luid, buffer, size)
-}
-
-func _lookupPrivilegeName(systemName *uint16, luid *uint64, buffer *uint16, size *uint32) (err error) {
- r1, _, e1 := syscall.SyscallN(procLookupPrivilegeNameW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) {
- var _p0 *uint16
- _p0, err = syscall.UTF16PtrFromString(systemName)
- if err != nil {
- return
- }
- var _p1 *uint16
- _p1, err = syscall.UTF16PtrFromString(name)
- if err != nil {
- return
- }
- return _lookupPrivilegeValue(_p0, _p1, luid)
-}
-
-func _lookupPrivilegeValue(systemName *uint16, name *uint16, luid *uint64) (err error) {
- r1, _, e1 := syscall.SyscallN(procLookupPrivilegeValueW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid)))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func openThreadToken(thread windows.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) {
- var _p0 uint32
- if openAsSelf {
- _p0 = 1
- }
- r1, _, e1 := syscall.SyscallN(procOpenThreadToken.Addr(), uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token)))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func revertToSelf() (err error) {
- r1, _, e1 := syscall.SyscallN(procRevertToSelf.Addr())
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func backupRead(h windows.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
- var _p0 *byte
- if len(b) > 0 {
- _p0 = &b[0]
- }
- var _p1 uint32
- if abort {
- _p1 = 1
- }
- var _p2 uint32
- if processSecurity {
- _p2 = 1
- }
- r1, _, e1 := syscall.SyscallN(procBackupRead.Addr(), uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesRead)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func backupWrite(h windows.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
- var _p0 *byte
- if len(b) > 0 {
- _p0 = &b[0]
- }
- var _p1 uint32
- if abort {
- _p1 = 1
- }
- var _p2 uint32
- if processSecurity {
- _p2 = 1
- }
- r1, _, e1 := syscall.SyscallN(procBackupWrite.Addr(), uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesWritten)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func cancelIoEx(file windows.Handle, o *windows.Overlapped) (err error) {
- r1, _, e1 := syscall.SyscallN(procCancelIoEx.Addr(), uintptr(file), uintptr(unsafe.Pointer(o)))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func connectNamedPipe(pipe windows.Handle, o *windows.Overlapped) (err error) {
- r1, _, e1 := syscall.SyscallN(procConnectNamedPipe.Addr(), uintptr(pipe), uintptr(unsafe.Pointer(o)))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func createIoCompletionPort(file windows.Handle, port windows.Handle, key uintptr, threadCount uint32) (newport windows.Handle, err error) {
- r0, _, e1 := syscall.SyscallN(procCreateIoCompletionPort.Addr(), uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount))
- newport = windows.Handle(r0)
- if newport == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *windows.SecurityAttributes) (handle windows.Handle, err error) {
- var _p0 *uint16
- _p0, err = syscall.UTF16PtrFromString(name)
- if err != nil {
- return
- }
- return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa)
-}
-
-func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *windows.SecurityAttributes) (handle windows.Handle, err error) {
- r0, _, e1 := syscall.SyscallN(procCreateNamedPipeW.Addr(), uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)))
- handle = windows.Handle(r0)
- if handle == windows.InvalidHandle {
- err = errnoErr(e1)
- }
- return
-}
-
-func disconnectNamedPipe(pipe windows.Handle) (err error) {
- r1, _, e1 := syscall.SyscallN(procDisconnectNamedPipe.Addr(), uintptr(pipe))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func getCurrentThread() (h windows.Handle) {
- r0, _, _ := syscall.SyscallN(procGetCurrentThread.Addr())
- h = windows.Handle(r0)
- return
-}
-
-func getNamedPipeHandleState(pipe windows.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) {
- r1, _, e1 := syscall.SyscallN(procGetNamedPipeHandleStateW.Addr(), uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func getNamedPipeInfo(pipe windows.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) {
- r1, _, e1 := syscall.SyscallN(procGetNamedPipeInfo.Addr(), uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func getQueuedCompletionStatus(port windows.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) {
- r1, _, e1 := syscall.SyscallN(procGetQueuedCompletionStatus.Addr(), uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func setFileCompletionNotificationModes(h windows.Handle, flags uint8) (err error) {
- r1, _, e1 := syscall.SyscallN(procSetFileCompletionNotificationModes.Addr(), uintptr(h), uintptr(flags))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func ntCreateNamedPipeFile(pipe *windows.Handle, access ntAccessMask, oa *objectAttributes, iosb *ioStatusBlock, share ntFileShareMode, disposition ntFileCreationDisposition, options ntFileOptions, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntStatus) {
- r0, _, _ := syscall.SyscallN(procNtCreateNamedPipeFile.Addr(), uintptr(unsafe.Pointer(pipe)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(disposition), uintptr(options), uintptr(typ), uintptr(readMode), uintptr(completionMode), uintptr(maxInstances), uintptr(inboundQuota), uintptr(outputQuota), uintptr(unsafe.Pointer(timeout)))
- status = ntStatus(r0)
- return
-}
-
-func rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) {
- r0, _, _ := syscall.SyscallN(procRtlDefaultNpAcl.Addr(), uintptr(unsafe.Pointer(dacl)))
- status = ntStatus(r0)
- return
-}
-
-func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntStatus) {
- r0, _, _ := syscall.SyscallN(procRtlDosPathNameToNtPathName_U.Addr(), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(ntName)), uintptr(filePart), uintptr(reserved))
- status = ntStatus(r0)
- return
-}
-
-func rtlNtStatusToDosError(status ntStatus) (winerr error) {
- r0, _, _ := syscall.SyscallN(procRtlNtStatusToDosErrorNoTeb.Addr(), uintptr(status))
- if r0 != 0 {
- winerr = syscall.Errno(r0)
- }
- return
-}
-
-func wsaGetOverlappedResult(h windows.Handle, o *windows.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) {
- var _p0 uint32
- if wait {
- _p0 = 1
- }
- r1, _, e1 := syscall.SyscallN(procWSAGetOverlappedResult.Addr(), uintptr(h), uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(bytes)), uintptr(_p0), uintptr(unsafe.Pointer(flags)))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
diff --git a/vendor/github.com/clbanning/mxj/v2/.travis.yml b/vendor/github.com/clbanning/mxj/v2/.travis.yml
deleted file mode 100644
index 9c86115..0000000
--- a/vendor/github.com/clbanning/mxj/v2/.travis.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-language: go
-
-go:
-- 1.x
\ No newline at end of file
diff --git a/vendor/github.com/clbanning/mxj/v2/LICENSE b/vendor/github.com/clbanning/mxj/v2/LICENSE
deleted file mode 100644
index 1ada880..0000000
--- a/vendor/github.com/clbanning/mxj/v2/LICENSE
+++ /dev/null
@@ -1,22 +0,0 @@
-Copyright (c) 2012-2021 Charles Banning . All rights reserved.
-
-The MIT License (MIT)
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
diff --git a/vendor/github.com/clbanning/mxj/v2/anyxml.go b/vendor/github.com/clbanning/mxj/v2/anyxml.go
deleted file mode 100644
index 63970ee..0000000
--- a/vendor/github.com/clbanning/mxj/v2/anyxml.go
+++ /dev/null
@@ -1,201 +0,0 @@
-package mxj
-
-import (
- "bytes"
- "encoding/xml"
- "reflect"
-)
-
-const (
- DefaultElementTag = "element"
-)
-
-// Encode arbitrary value as XML.
-//
-// Note: unmarshaling the resultant
-// XML may not return the original value, since tag labels may have been injected
-// to create the XML representation of the value.
-/*
- Encode an arbitrary JSON object.
- package main
-
- import (
- "encoding/json"
- "fmt"
- "github.com/clbanning/mxj"
- )
-
- func main() {
- jsondata := []byte(`[
- { "somekey":"somevalue" },
- "string",
- 3.14159265,
- true
- ]`)
- var i interface{}
- err := json.Unmarshal(jsondata, &i)
- if err != nil {
- // do something
- }
- x, err := mxj.AnyXmlIndent(i, "", " ", "mydoc")
- if err != nil {
- // do something else
- }
- fmt.Println(string(x))
- }
-
- output:
-
- somevalue
- string
- 3.14159265
- true
-
-
-An extreme example is available in examples/goofy_map.go.
-*/
-// Alternative values for DefaultRootTag and DefaultElementTag can be set as:
-// AnyXml( v, myRootTag, myElementTag).
-func AnyXml(v interface{}, tags ...string) ([]byte, error) {
- var rt, et string
- if len(tags) == 1 || len(tags) == 2 {
- rt = tags[0]
- } else {
- rt = DefaultRootTag
- }
- if len(tags) == 2 {
- et = tags[1]
- } else {
- et = DefaultElementTag
- }
-
- if v == nil {
- if useGoXmlEmptyElemSyntax {
- return []byte("<" + rt + ">" + rt + ">"), nil
- }
- return []byte("<" + rt + "/>"), nil
- }
- if reflect.TypeOf(v).Kind() == reflect.Struct {
- return xml.Marshal(v)
- }
-
- var err error
- s := new(bytes.Buffer)
- p := new(pretty)
-
- var b []byte
- switch v.(type) {
- case []interface{}:
- if _, err = s.WriteString("<" + rt + ">"); err != nil {
- return nil, err
- }
- for _, vv := range v.([]interface{}) {
- switch vv.(type) {
- case map[string]interface{}:
- m := vv.(map[string]interface{})
- if len(m) == 1 {
- for tag, val := range m {
- err = marshalMapToXmlIndent(false, s, tag, val, p)
- }
- } else {
- err = marshalMapToXmlIndent(false, s, et, vv, p)
- }
- default:
- err = marshalMapToXmlIndent(false, s, et, vv, p)
- }
- if err != nil {
- break
- }
- }
- if _, err = s.WriteString("" + rt + ">"); err != nil {
- return nil, err
- }
- b = s.Bytes()
- case map[string]interface{}:
- m := Map(v.(map[string]interface{}))
- b, err = m.Xml(rt)
- default:
- err = marshalMapToXmlIndent(false, s, rt, v, p)
- b = s.Bytes()
- }
-
- return b, err
-}
-
-// Encode an arbitrary value as a pretty XML string.
-// Alternative values for DefaultRootTag and DefaultElementTag can be set as:
-// AnyXmlIndent( v, "", " ", myRootTag, myElementTag).
-func AnyXmlIndent(v interface{}, prefix, indent string, tags ...string) ([]byte, error) {
- var rt, et string
- if len(tags) == 1 || len(tags) == 2 {
- rt = tags[0]
- } else {
- rt = DefaultRootTag
- }
- if len(tags) == 2 {
- et = tags[1]
- } else {
- et = DefaultElementTag
- }
-
- if v == nil {
- if useGoXmlEmptyElemSyntax {
- return []byte(prefix + "<" + rt + ">" + rt + ">"), nil
- }
- return []byte(prefix + "<" + rt + "/>"), nil
- }
- if reflect.TypeOf(v).Kind() == reflect.Struct {
- return xml.MarshalIndent(v, prefix, indent)
- }
-
- var err error
- s := new(bytes.Buffer)
- p := new(pretty)
- p.indent = indent
- p.padding = prefix
-
- var b []byte
- switch v.(type) {
- case []interface{}:
- if _, err = s.WriteString("<" + rt + ">\n"); err != nil {
- return nil, err
- }
- p.Indent()
- for _, vv := range v.([]interface{}) {
- switch vv.(type) {
- case map[string]interface{}:
- m := vv.(map[string]interface{})
- if len(m) == 1 {
- for tag, val := range m {
- err = marshalMapToXmlIndent(true, s, tag, val, p)
- }
- } else {
- p.start = 1 // we 1 tag in
- err = marshalMapToXmlIndent(true, s, et, vv, p)
- // *s += "\n"
- if _, err = s.WriteString("\n"); err != nil {
- return nil, err
- }
- }
- default:
- p.start = 0 // in case trailing p.start = 1
- err = marshalMapToXmlIndent(true, s, et, vv, p)
- }
- if err != nil {
- break
- }
- }
- if _, err = s.WriteString(`` + rt + `>`); err != nil {
- return nil, err
- }
- b = s.Bytes()
- case map[string]interface{}:
- m := Map(v.(map[string]interface{}))
- b, err = m.XmlIndent(prefix, indent, rt)
- default:
- err = marshalMapToXmlIndent(true, s, rt, v, p)
- b = s.Bytes()
- }
-
- return b, err
-}
diff --git a/vendor/github.com/clbanning/mxj/v2/atomFeedString.xml b/vendor/github.com/clbanning/mxj/v2/atomFeedString.xml
deleted file mode 100644
index 474575a..0000000
--- a/vendor/github.com/clbanning/mxj/v2/atomFeedString.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-
-Code Review - My issueshttp://codereview.appspot.com/rietveld<>rietveld: an attempt at pubsubhubbub
-2009-10-04T01:35:58+00:00email-address-removedurn:md5:134d9179c41f806be79b3a5f7877d19a
- An attempt at adding pubsubhubbub support to Rietveld.
-http://code.google.com/p/pubsubhubbub
-http://code.google.com/p/rietveld/issues/detail?id=155
-
-The server side of the protocol is trivial:
- 1. add a <link rel="hub" href="hub-server"> tag to all
- feeds that will be pubsubhubbubbed.
- 2. every time one of those feeds changes, tell the hub
- with a simple POST request.
-
-I have tested this by adding debug prints to a local hub
-server and checking that the server got the right publish
-requests.
-
-I can't quite get the server to work, but I think the bug
-is not in my code. I think that the server expects to be
-able to grab the feed and see the feed's actual URL in
-the link rel="self", but the default value for that drops
-the :port from the URL, and I cannot for the life of me
-figure out how to get the Atom generator deep inside
-django not to do that, or even where it is doing that,
-or even what code is running to generate the Atom feed.
-(I thought I knew but I added some assert False statements
-and it kept running!)
-
-Ignoring that particular problem, I would appreciate
-feedback on the right way to get the two values at
-the top of feeds.py marked NOTE(rsc).
-
-
-rietveld: correct tab handling
-2009-10-03T23:02:17+00:00email-address-removedurn:md5:0a2a4f19bb815101f0ba2904aed7c35a
- This fixes the buggy tab rendering that can be seen at
-http://codereview.appspot.com/116075/diff/1/2
-
-The fundamental problem was that the tab code was
-not being told what column the text began in, so it
-didn't know where to put the tab stops. Another problem
-was that some of the code assumed that string byte
-offsets were the same as column offsets, which is only
-true if there are no tabs.
-
-In the process of fixing this, I cleaned up the arguments
-to Fold and ExpandTabs and renamed them Break and
-_ExpandTabs so that I could be sure that I found all the
-call sites. I also wanted to verify that ExpandTabs was
-not being used from outside intra_region_diff.py.
-
-
- `
-
diff --git a/vendor/github.com/clbanning/mxj/v2/doc.go b/vendor/github.com/clbanning/mxj/v2/doc.go
deleted file mode 100644
index 07ac098..0000000
--- a/vendor/github.com/clbanning/mxj/v2/doc.go
+++ /dev/null
@@ -1,143 +0,0 @@
-// mxj - A collection of map[string]interface{} and associated XML and JSON utilities.
-// Copyright 2012-2019, Charles Banning. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file
-
-/*
-Marshal/Unmarshal XML to/from map[string]interface{} values (and JSON); extract/modify values from maps by key or key-path, including wildcards.
-
-mxj supplants the legacy x2j and j2x packages. The subpackage x2j-wrapper is provided to facilitate migrating from the x2j package. The x2j and j2x subpackages provide similar functionality of the old packages but are not function-name compatible with them.
-
-Note: this library was designed for processing ad hoc anonymous messages. Bulk processing large data sets may be much more efficiently performed using the encoding/xml or encoding/json packages from Go's standard library directly.
-
-Related Packages:
- checkxml: github.com/clbanning/checkxml provides functions for validating XML data.
-
-Notes:
- 2022.11.28: v2.7 - add SetGlobalKeyMapPrefix to change default prefix, '#', for default keys
- 2022.11.20: v2.6 - add NewMapForattedXmlSeq for XML docs formatted with whitespace character
- 2021.02.02: v2.5 - add XmlCheckIsValid toggle to force checking that the encoded XML is valid
- 2020.12.14: v2.4 - add XMLEscapeCharsDecoder to preserve XML escaped characters in Map values
- 2020.10.28: v2.3 - add TrimWhiteSpace option
- 2020.05.01: v2.2 - optimize map to XML encoding for large XML docs.
- 2019.07.04: v2.0 - remove unnecessary methods - mv.XmlWriterRaw, mv.XmlIndentWriterRaw - for Map and MapSeq.
- 2019.07.04: Add MapSeq type and move associated functions and methods from Map to MapSeq.
- 2019.01.21: DecodeSimpleValuesAsMap - decode to map[:map["#text":]] rather than map[:].
- 2018.04.18: mv.Xml/mv.XmlIndent encodes non-map[string]interface{} map values - map[string]string, map[int]uint, etc.
- 2018.03.29: mv.Gob/NewMapGob support gob encoding/decoding of Maps.
- 2018.03.26: Added mxj/x2j-wrapper sub-package for migrating from legacy x2j package.
- 2017.02.22: LeafNode paths can use ".N" syntax rather than "[N]" for list member indexing.
- 2017.02.21: github.com/clbanning/checkxml provides functions for validating XML data.
- 2017.02.10: SetFieldSeparator changes field separator for args in UpdateValuesForPath, ValuesFor... methods.
- 2017.02.06: Support XMPP stream processing - HandleXMPPStreamTag().
- 2016.11.07: Preserve name space prefix syntax in XmlSeq parser - NewMapXmlSeq(), etc.
- 2016.06.25: Support overriding default XML attribute prefix, "-", in Map keys - SetAttrPrefix().
- 2016.05.26: Support customization of xml.Decoder by exposing CustomDecoder variable.
- 2016.03.19: Escape invalid chars when encoding XML attribute and element values - XMLEscapeChars().
- 2016.03.02: By default decoding XML with float64 and bool value casting will not cast "NaN", "Inf", and "-Inf".
- To cast them to float64, first set flag with CastNanInf(true).
- 2016.02.22: New mv.Root(), mv.Elements(), mv.Attributes methods let you examine XML document structure.
- 2016.02.16: Add CoerceKeysToLower() option to handle tags with mixed capitalization.
- 2016.02.12: Seek for first xml.StartElement token; only return error if io.EOF is reached first (handles BOM).
- 2015-12-02: NewMapXmlSeq() with mv.XmlSeq() & co. will try to preserve structure of XML doc when re-encoding.
- 2014-08-02: AnyXml() and AnyXmlIndent() will try to marshal arbitrary values to XML.
-
-SUMMARY
-
- type Map map[string]interface{}
-
- Create a Map value, 'mv', from any map[string]interface{} value, 'v':
- mv := Map(v)
-
- Unmarshal / marshal XML as a Map value, 'mv':
- mv, err := NewMapXml(xmlValue) // unmarshal
- xmlValue, err := mv.Xml() // marshal
-
- Unmarshal XML from an io.Reader as a Map value, 'mv':
- mv, err := NewMapXmlReader(xmlReader) // repeated calls, as with an os.File Reader, will process stream
- mv, raw, err := NewMapXmlReaderRaw(xmlReader) // 'raw' is the raw XML that was decoded
-
- Marshal Map value, 'mv', to an XML Writer (io.Writer):
- err := mv.XmlWriter(xmlWriter)
- raw, err := mv.XmlWriterRaw(xmlWriter) // 'raw' is the raw XML that was written on xmlWriter
-
- Also, for prettified output:
- xmlValue, err := mv.XmlIndent(prefix, indent, ...)
- err := mv.XmlIndentWriter(xmlWriter, prefix, indent, ...)
- raw, err := mv.XmlIndentWriterRaw(xmlWriter, prefix, indent, ...)
-
- Bulk process XML with error handling (note: handlers must return a boolean value):
- err := HandleXmlReader(xmlReader, mapHandler(Map), errHandler(error))
- err := HandleXmlReaderRaw(xmlReader, mapHandler(Map, []byte), errHandler(error, []byte))
-
- Converting XML to JSON: see Examples for NewMapXml and HandleXmlReader.
-
- There are comparable functions and methods for JSON processing.
-
- Arbitrary structure values can be decoded to / encoded from Map values:
- mv, err := NewMapStruct(structVal)
- err := mv.Struct(structPointer)
-
- To work with XML tag values, JSON or Map key values or structure field values, decode the XML, JSON
- or structure to a Map value, 'mv', or cast a map[string]interface{} value to a Map value, 'mv', then:
- paths := mv.PathsForKey(key)
- path := mv.PathForKeyShortest(key)
- values, err := mv.ValuesForKey(key, subkeys)
- values, err := mv.ValuesForPath(path, subkeys) // 'path' can be dot-notation with wildcards and indexed arrays.
- count, err := mv.UpdateValuesForPath(newVal, path, subkeys)
-
- Get everything at once, irrespective of path depth:
- leafnodes := mv.LeafNodes()
- leafvalues := mv.LeafValues()
-
- A new Map with whatever keys are desired can be created from the current Map and then encoded in XML
- or JSON. (Note: keys can use dot-notation. 'oldKey' can also use wildcards and indexed arrays.)
- newMap, err := mv.NewMap("oldKey_1:newKey_1", "oldKey_2:newKey_2", ..., "oldKey_N:newKey_N")
- newMap, err := mv.NewMap("oldKey1", "oldKey3", "oldKey5") // a subset of 'mv'; see "examples/partial.go"
- newXml, err := newMap.Xml() // for example
- newJson, err := newMap.Json() // ditto
-
-XML PARSING CONVENTIONS
-
- Using NewMapXml()
-
- - Attributes are parsed to `map[string]interface{}` values by prefixing a hyphen, `-`,
- to the attribute label. (Unless overridden by `PrependAttrWithHyphen(false)` or
- `SetAttrPrefix()`.)
- - If the element is a simple element and has attributes, the element value
- is given the key `#text` for its `map[string]interface{}` representation. (See
- the 'atomFeedString.xml' test data, below.)
- - XML comments, directives, and process instructions are ignored.
- - If CoerceKeysToLower() has been called, then the resultant keys will be lower case.
-
- Using NewMapXmlSeq()
-
- - Attributes are parsed to `map["#attr"]map[]map[string]interface{}`values
- where the `` value has "#text" and "#seq" keys - the "#text" key holds the
- value for ``.
- - All elements, except for the root, have a "#seq" key.
- - Comments, directives, and process instructions are unmarshalled into the Map using the
- keys "#comment", "#directive", and "#procinst", respectively. (See documentation for more
- specifics.)
- - Name space syntax is preserved:
- - something parses to map["ns:key"]interface{}{"something"}
- - xmlns:ns="http://myns.com/ns" parses to map["xmlns:ns"]interface{}{"http://myns.com/ns"}
-
- Both
-
- - By default, "Nan", "Inf", and "-Inf" values are not cast to float64. If you want them
- to be cast, set a flag to cast them using CastNanInf(true).
-
-XML ENCODING CONVENTIONS
-
- - 'nil' Map values, which may represent 'null' JSON values, are encoded as "".
- NOTE: the operation is not symmetric as "" elements are decoded as 'tag:""' Map values,
- which, then, encode in JSON as '"tag":""' values..
- - ALSO: there is no guarantee that the encoded XML doc will be the same as the decoded one. (Go
- randomizes the walk through map[string]interface{} values.) If you plan to re-encode the
- Map value to XML and want the same sequencing of elements look at NewMapXmlSeq() and
- mv.XmlSeq() - these try to preserve the element sequencing but with added complexity when
- working with the Map representation.
-
-*/
-package mxj
diff --git a/vendor/github.com/clbanning/mxj/v2/escapechars.go b/vendor/github.com/clbanning/mxj/v2/escapechars.go
deleted file mode 100644
index eeb3d25..0000000
--- a/vendor/github.com/clbanning/mxj/v2/escapechars.go
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2016 Charles Banning. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file
-
-package mxj
-
-import (
- "bytes"
-)
-
-var xmlEscapeChars bool
-
-// XMLEscapeChars(true) forces escaping invalid characters in attribute and element values.
-// NOTE: this is brute force with NO interrogation of '&' being escaped already; if it is
-// then '&' will be re-escaped as '&'.
-//
-/*
- The values are:
- " "
- ' '
- < <
- > >
- & &
-*/
-//
-// Note: if XMLEscapeCharsDecoder(true) has been called - or the default, 'false,' value
-// has been toggled to 'true' - then XMLEscapeChars(true) is ignored. If XMLEscapeChars(true)
-// has already been called before XMLEscapeCharsDecoder(true), XMLEscapeChars(false) is called
-// to turn escape encoding on mv.Xml, etc., to prevent double escaping ampersands, '&'.
-func XMLEscapeChars(b ...bool) {
- var bb bool
- if len(b) == 0 {
- bb = !xmlEscapeChars
- } else {
- bb = b[0]
- }
- if bb == true && xmlEscapeCharsDecoder == false {
- xmlEscapeChars = true
- } else {
- xmlEscapeChars = false
- }
-}
-
-// Scan for '&' first, since 's' may contain "&" that is parsed to "&"
-// - or "<" that is parsed to "<".
-var escapechars = [][2][]byte{
- {[]byte(`&`), []byte(`&`)},
- {[]byte(`<`), []byte(`<`)},
- {[]byte(`>`), []byte(`>`)},
- {[]byte(`"`), []byte(`"`)},
- {[]byte(`'`), []byte(`'`)},
-}
-
-func escapeChars(s string) string {
- if len(s) == 0 {
- return s
- }
-
- b := []byte(s)
- for _, v := range escapechars {
- n := bytes.Count(b, v[0])
- if n == 0 {
- continue
- }
- b = bytes.Replace(b, v[0], v[1], n)
- }
- return string(b)
-}
-
-// per issue #84, escape CharData values from xml.Decoder
-
-var xmlEscapeCharsDecoder bool
-
-// XMLEscapeCharsDecoder(b ...bool) escapes XML characters in xml.CharData values
-// returned by Decoder.Token. Thus, the internal Map values will contain escaped
-// values, and you do not need to set XMLEscapeChars for proper encoding.
-//
-// By default, the Map values have the non-escaped values returned by Decoder.Token.
-// XMLEscapeCharsDecoder(true) - or, XMLEscapeCharsDecoder() - will toggle escape
-// encoding 'on.'
-//
-// Note: if XMLEscapeCharDecoder(true) is call then XMLEscapeChars(false) is
-// called to prevent re-escaping the values on encoding using mv.Xml, etc.
-func XMLEscapeCharsDecoder(b ...bool) {
- if len(b) == 0 {
- xmlEscapeCharsDecoder = !xmlEscapeCharsDecoder
- } else {
- xmlEscapeCharsDecoder = b[0]
- }
- if xmlEscapeCharsDecoder == true && xmlEscapeChars == true {
- xmlEscapeChars = false
- }
-}
diff --git a/vendor/github.com/clbanning/mxj/v2/exists.go b/vendor/github.com/clbanning/mxj/v2/exists.go
deleted file mode 100644
index 07aeda4..0000000
--- a/vendor/github.com/clbanning/mxj/v2/exists.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package mxj
-
-// Checks whether the path exists. If err != nil then 'false' is returned
-// along with the error encountered parsing either the "path" or "subkeys"
-// argument.
-func (mv Map) Exists(path string, subkeys ...string) (bool, error) {
- v, err := mv.ValuesForPath(path, subkeys...)
- return (err == nil && len(v) > 0), err
-}
diff --git a/vendor/github.com/clbanning/mxj/v2/files.go b/vendor/github.com/clbanning/mxj/v2/files.go
deleted file mode 100644
index 27e06e1..0000000
--- a/vendor/github.com/clbanning/mxj/v2/files.go
+++ /dev/null
@@ -1,287 +0,0 @@
-package mxj
-
-import (
- "fmt"
- "io"
- "os"
-)
-
-type Maps []Map
-
-func NewMaps() Maps {
- return make(Maps, 0)
-}
-
-type MapRaw struct {
- M Map
- R []byte
-}
-
-// NewMapsFromXmlFile - creates an array from a file of JSON values.
-func NewMapsFromJsonFile(name string) (Maps, error) {
- fi, err := os.Stat(name)
- if err != nil {
- return nil, err
- }
- if !fi.Mode().IsRegular() {
- return nil, fmt.Errorf("file %s is not a regular file", name)
- }
-
- fh, err := os.Open(name)
- if err != nil {
- return nil, err
- }
- defer fh.Close()
-
- am := make([]Map, 0)
- for {
- m, raw, err := NewMapJsonReaderRaw(fh)
- if err != nil && err != io.EOF {
- return am, fmt.Errorf("error: %s - reading: %s", err.Error(), string(raw))
- }
- if len(m) > 0 {
- am = append(am, m)
- }
- if err == io.EOF {
- break
- }
- }
- return am, nil
-}
-
-// ReadMapsFromJsonFileRaw - creates an array of MapRaw from a file of JSON values.
-func NewMapsFromJsonFileRaw(name string) ([]MapRaw, error) {
- fi, err := os.Stat(name)
- if err != nil {
- return nil, err
- }
- if !fi.Mode().IsRegular() {
- return nil, fmt.Errorf("file %s is not a regular file", name)
- }
-
- fh, err := os.Open(name)
- if err != nil {
- return nil, err
- }
- defer fh.Close()
-
- am := make([]MapRaw, 0)
- for {
- mr := new(MapRaw)
- mr.M, mr.R, err = NewMapJsonReaderRaw(fh)
- if err != nil && err != io.EOF {
- return am, fmt.Errorf("error: %s - reading: %s", err.Error(), string(mr.R))
- }
- if len(mr.M) > 0 {
- am = append(am, *mr)
- }
- if err == io.EOF {
- break
- }
- }
- return am, nil
-}
-
-// NewMapsFromXmlFile - creates an array from a file of XML values.
-func NewMapsFromXmlFile(name string) (Maps, error) {
- fi, err := os.Stat(name)
- if err != nil {
- return nil, err
- }
- if !fi.Mode().IsRegular() {
- return nil, fmt.Errorf("file %s is not a regular file", name)
- }
-
- fh, err := os.Open(name)
- if err != nil {
- return nil, err
- }
- defer fh.Close()
-
- am := make([]Map, 0)
- for {
- m, raw, err := NewMapXmlReaderRaw(fh)
- if err != nil && err != io.EOF {
- return am, fmt.Errorf("error: %s - reading: %s", err.Error(), string(raw))
- }
- if len(m) > 0 {
- am = append(am, m)
- }
- if err == io.EOF {
- break
- }
- }
- return am, nil
-}
-
-// NewMapsFromXmlFileRaw - creates an array of MapRaw from a file of XML values.
-// NOTE: the slice with the raw XML is clean with no extra capacity - unlike NewMapXmlReaderRaw().
-// It is slow at parsing a file from disk and is intended for relatively small utility files.
-func NewMapsFromXmlFileRaw(name string) ([]MapRaw, error) {
- fi, err := os.Stat(name)
- if err != nil {
- return nil, err
- }
- if !fi.Mode().IsRegular() {
- return nil, fmt.Errorf("file %s is not a regular file", name)
- }
-
- fh, err := os.Open(name)
- if err != nil {
- return nil, err
- }
- defer fh.Close()
-
- am := make([]MapRaw, 0)
- for {
- mr := new(MapRaw)
- mr.M, mr.R, err = NewMapXmlReaderRaw(fh)
- if err != nil && err != io.EOF {
- return am, fmt.Errorf("error: %s - reading: %s", err.Error(), string(mr.R))
- }
- if len(mr.M) > 0 {
- am = append(am, *mr)
- }
- if err == io.EOF {
- break
- }
- }
- return am, nil
-}
-
-// ------------------------ Maps writing -------------------------
-// These are handy-dandy methods for dumping configuration data, etc.
-
-// JsonString - analogous to mv.Json()
-func (mvs Maps) JsonString(safeEncoding ...bool) (string, error) {
- var s string
- for _, v := range mvs {
- j, err := v.Json()
- if err != nil {
- return s, err
- }
- s += string(j)
- }
- return s, nil
-}
-
-// JsonStringIndent - analogous to mv.JsonIndent()
-func (mvs Maps) JsonStringIndent(prefix, indent string, safeEncoding ...bool) (string, error) {
- var s string
- var haveFirst bool
- for _, v := range mvs {
- j, err := v.JsonIndent(prefix, indent)
- if err != nil {
- return s, err
- }
- if haveFirst {
- s += "\n"
- } else {
- haveFirst = true
- }
- s += string(j)
- }
- return s, nil
-}
-
-// XmlString - analogous to mv.Xml()
-func (mvs Maps) XmlString() (string, error) {
- var s string
- for _, v := range mvs {
- x, err := v.Xml()
- if err != nil {
- return s, err
- }
- s += string(x)
- }
- return s, nil
-}
-
-// XmlStringIndent - analogous to mv.XmlIndent()
-func (mvs Maps) XmlStringIndent(prefix, indent string) (string, error) {
- var s string
- for _, v := range mvs {
- x, err := v.XmlIndent(prefix, indent)
- if err != nil {
- return s, err
- }
- s += string(x)
- }
- return s, nil
-}
-
-// JsonFile - write Maps to named file as JSON
-// Note: the file will be created, if necessary; if it exists it will be truncated.
-// If you need to append to a file, open it and use JsonWriter method.
-func (mvs Maps) JsonFile(file string, safeEncoding ...bool) error {
- var encoding bool
- if len(safeEncoding) == 1 {
- encoding = safeEncoding[0]
- }
- s, err := mvs.JsonString(encoding)
- if err != nil {
- return err
- }
- fh, err := os.Create(file)
- if err != nil {
- return err
- }
- defer fh.Close()
- fh.WriteString(s)
- return nil
-}
-
-// JsonFileIndent - write Maps to named file as pretty JSON
-// Note: the file will be created, if necessary; if it exists it will be truncated.
-// If you need to append to a file, open it and use JsonIndentWriter method.
-func (mvs Maps) JsonFileIndent(file, prefix, indent string, safeEncoding ...bool) error {
- var encoding bool
- if len(safeEncoding) == 1 {
- encoding = safeEncoding[0]
- }
- s, err := mvs.JsonStringIndent(prefix, indent, encoding)
- if err != nil {
- return err
- }
- fh, err := os.Create(file)
- if err != nil {
- return err
- }
- defer fh.Close()
- fh.WriteString(s)
- return nil
-}
-
-// XmlFile - write Maps to named file as XML
-// Note: the file will be created, if necessary; if it exists it will be truncated.
-// If you need to append to a file, open it and use XmlWriter method.
-func (mvs Maps) XmlFile(file string) error {
- s, err := mvs.XmlString()
- if err != nil {
- return err
- }
- fh, err := os.Create(file)
- if err != nil {
- return err
- }
- defer fh.Close()
- fh.WriteString(s)
- return nil
-}
-
-// XmlFileIndent - write Maps to named file as pretty XML
-// Note: the file will be created,if necessary; if it exists it will be truncated.
-// If you need to append to a file, open it and use XmlIndentWriter method.
-func (mvs Maps) XmlFileIndent(file, prefix, indent string) error {
- s, err := mvs.XmlStringIndent(prefix, indent)
- if err != nil {
- return err
- }
- fh, err := os.Create(file)
- if err != nil {
- return err
- }
- defer fh.Close()
- fh.WriteString(s)
- return nil
-}
diff --git a/vendor/github.com/clbanning/mxj/v2/files_test.badjson b/vendor/github.com/clbanning/mxj/v2/files_test.badjson
deleted file mode 100644
index d187200..0000000
--- a/vendor/github.com/clbanning/mxj/v2/files_test.badjson
+++ /dev/null
@@ -1,2 +0,0 @@
-{ "this":"is", "a":"test", "file":"for", "files_test.go":"case" }
-{ "with":"some", "bad":JSON, "in":"it" }
diff --git a/vendor/github.com/clbanning/mxj/v2/files_test.badxml b/vendor/github.com/clbanning/mxj/v2/files_test.badxml
deleted file mode 100644
index 4736ef9..0000000
--- a/vendor/github.com/clbanning/mxj/v2/files_test.badxml
+++ /dev/null
@@ -1,9 +0,0 @@
-
- test
- for files.go
-
-
- some
- doc
- test case
-
diff --git a/vendor/github.com/clbanning/mxj/v2/files_test.json b/vendor/github.com/clbanning/mxj/v2/files_test.json
deleted file mode 100644
index e9a3ddf..0000000
--- a/vendor/github.com/clbanning/mxj/v2/files_test.json
+++ /dev/null
@@ -1,2 +0,0 @@
-{ "this":"is", "a":"test", "file":"for", "files_test.go":"case" }
-{ "with":"just", "two":2, "JSON":"values", "true":true }
diff --git a/vendor/github.com/clbanning/mxj/v2/files_test.xml b/vendor/github.com/clbanning/mxj/v2/files_test.xml
deleted file mode 100644
index 65cf021..0000000
--- a/vendor/github.com/clbanning/mxj/v2/files_test.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
- test
- for files.go
-
-
- some
- doc
- test case
-
diff --git a/vendor/github.com/clbanning/mxj/v2/files_test_dup.json b/vendor/github.com/clbanning/mxj/v2/files_test_dup.json
deleted file mode 100644
index 2becb6a..0000000
--- a/vendor/github.com/clbanning/mxj/v2/files_test_dup.json
+++ /dev/null
@@ -1 +0,0 @@
-{"a":"test","file":"for","files_test.go":"case","this":"is"}{"JSON":"values","true":true,"two":2,"with":"just"}
\ No newline at end of file
diff --git a/vendor/github.com/clbanning/mxj/v2/files_test_dup.xml b/vendor/github.com/clbanning/mxj/v2/files_test_dup.xml
deleted file mode 100644
index f68d22e..0000000
--- a/vendor/github.com/clbanning/mxj/v2/files_test_dup.xml
+++ /dev/null
@@ -1 +0,0 @@
-for files.gotestdoctest casesome
\ No newline at end of file
diff --git a/vendor/github.com/clbanning/mxj/v2/files_test_indent.json b/vendor/github.com/clbanning/mxj/v2/files_test_indent.json
deleted file mode 100644
index 6fde156..0000000
--- a/vendor/github.com/clbanning/mxj/v2/files_test_indent.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "a": "test",
- "file": "for",
- "files_test.go": "case",
- "this": "is"
-}
-{
- "JSON": "values",
- "true": true,
- "two": 2,
- "with": "just"
-}
\ No newline at end of file
diff --git a/vendor/github.com/clbanning/mxj/v2/files_test_indent.xml b/vendor/github.com/clbanning/mxj/v2/files_test_indent.xml
deleted file mode 100644
index 8c91a1d..0000000
--- a/vendor/github.com/clbanning/mxj/v2/files_test_indent.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
- for files.go
- test
-
- doc
- test case
- some
-
\ No newline at end of file
diff --git a/vendor/github.com/clbanning/mxj/v2/gob.go b/vendor/github.com/clbanning/mxj/v2/gob.go
deleted file mode 100644
index d56c2fd..0000000
--- a/vendor/github.com/clbanning/mxj/v2/gob.go
+++ /dev/null
@@ -1,35 +0,0 @@
-// gob.go - Encode/Decode a Map into a gob object.
-
-package mxj
-
-import (
- "bytes"
- "encoding/gob"
-)
-
-// NewMapGob returns a Map value for a gob object that has been
-// encoded from a map[string]interface{} (or compatible type) value.
-// It is intended to provide symmetric handling of Maps that have
-// been encoded using mv.Gob.
-func NewMapGob(gobj []byte) (Map, error) {
- m := make(map[string]interface{}, 0)
- if len(gobj) == 0 {
- return m, nil
- }
- r := bytes.NewReader(gobj)
- dec := gob.NewDecoder(r)
- if err := dec.Decode(&m); err != nil {
- return m, err
- }
- return m, nil
-}
-
-// Gob returns a gob-encoded value for the Map 'mv'.
-func (mv Map) Gob() ([]byte, error) {
- var buf bytes.Buffer
- enc := gob.NewEncoder(&buf)
- if err := enc.Encode(map[string]interface{}(mv)); err != nil {
- return nil, err
- }
- return buf.Bytes(), nil
-}
diff --git a/vendor/github.com/clbanning/mxj/v2/json.go b/vendor/github.com/clbanning/mxj/v2/json.go
deleted file mode 100644
index eb2c05a..0000000
--- a/vendor/github.com/clbanning/mxj/v2/json.go
+++ /dev/null
@@ -1,323 +0,0 @@
-// Copyright 2012-2014 Charles Banning. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file
-
-package mxj
-
-import (
- "bytes"
- "encoding/json"
- "fmt"
- "io"
- "time"
-)
-
-// ------------------------------ write JSON -----------------------
-
-// Just a wrapper on json.Marshal.
-// If option safeEncoding is'true' then safe encoding of '<', '>' and '&'
-// is preserved. (see encoding/json#Marshal, encoding/json#Encode)
-func (mv Map) Json(safeEncoding ...bool) ([]byte, error) {
- var s bool
- if len(safeEncoding) == 1 {
- s = safeEncoding[0]
- }
-
- b, err := json.Marshal(mv)
-
- if !s {
- b = bytes.Replace(b, []byte("\\u003c"), []byte("<"), -1)
- b = bytes.Replace(b, []byte("\\u003e"), []byte(">"), -1)
- b = bytes.Replace(b, []byte("\\u0026"), []byte("&"), -1)
- }
- return b, err
-}
-
-// Just a wrapper on json.MarshalIndent.
-// If option safeEncoding is'true' then safe encoding of '<' , '>' and '&'
-// is preserved. (see encoding/json#Marshal, encoding/json#Encode)
-func (mv Map) JsonIndent(prefix, indent string, safeEncoding ...bool) ([]byte, error) {
- var s bool
- if len(safeEncoding) == 1 {
- s = safeEncoding[0]
- }
-
- b, err := json.MarshalIndent(mv, prefix, indent)
- if !s {
- b = bytes.Replace(b, []byte("\\u003c"), []byte("<"), -1)
- b = bytes.Replace(b, []byte("\\u003e"), []byte(">"), -1)
- b = bytes.Replace(b, []byte("\\u0026"), []byte("&"), -1)
- }
- return b, err
-}
-
-// The following implementation is provided for symmetry with NewMapJsonReader[Raw]
-// The names will also provide a key for the number of return arguments.
-
-// Writes the Map as JSON on the Writer.
-// If 'safeEncoding' is 'true', then "safe" encoding of '<', '>' and '&' is preserved.
-func (mv Map) JsonWriter(jsonWriter io.Writer, safeEncoding ...bool) error {
- b, err := mv.Json(safeEncoding...)
- if err != nil {
- return err
- }
-
- _, err = jsonWriter.Write(b)
- return err
-}
-
-// Writes the Map as JSON on the Writer. []byte is the raw JSON that was written.
-// If 'safeEncoding' is 'true', then "safe" encoding of '<', '>' and '&' is preserved.
-func (mv Map) JsonWriterRaw(jsonWriter io.Writer, safeEncoding ...bool) ([]byte, error) {
- b, err := mv.Json(safeEncoding...)
- if err != nil {
- return b, err
- }
-
- _, err = jsonWriter.Write(b)
- return b, err
-}
-
-// Writes the Map as pretty JSON on the Writer.
-// If 'safeEncoding' is 'true', then "safe" encoding of '<', '>' and '&' is preserved.
-func (mv Map) JsonIndentWriter(jsonWriter io.Writer, prefix, indent string, safeEncoding ...bool) error {
- b, err := mv.JsonIndent(prefix, indent, safeEncoding...)
- if err != nil {
- return err
- }
-
- _, err = jsonWriter.Write(b)
- return err
-}
-
-// Writes the Map as pretty JSON on the Writer. []byte is the raw JSON that was written.
-// If 'safeEncoding' is 'true', then "safe" encoding of '<', '>' and '&' is preserved.
-func (mv Map) JsonIndentWriterRaw(jsonWriter io.Writer, prefix, indent string, safeEncoding ...bool) ([]byte, error) {
- b, err := mv.JsonIndent(prefix, indent, safeEncoding...)
- if err != nil {
- return b, err
- }
-
- _, err = jsonWriter.Write(b)
- return b, err
-}
-
-// --------------------------- read JSON -----------------------------
-
-// Decode numericvalues as json.Number type Map values - see encoding/json#Number.
-// NOTE: this is for decoding JSON into a Map with NewMapJson(), NewMapJsonReader(),
-// etc.; it does not affect NewMapXml(), etc. The XML encoders mv.Xml() and mv.XmlIndent()
-// do recognize json.Number types; a JSON object can be decoded to a Map with json.Number
-// value types and the resulting Map can be correctly encoded into a XML object.
-var JsonUseNumber bool
-
-// Just a wrapper on json.Unmarshal
-// Converting JSON to XML is a simple as:
-// ...
-// mapVal, merr := mxj.NewMapJson(jsonVal)
-// if merr != nil {
-// // handle error
-// }
-// xmlVal, xerr := mapVal.Xml()
-// if xerr != nil {
-// // handle error
-// }
-// NOTE: as a special case, passing a list, e.g., [{"some-null-value":"", "a-non-null-value":"bar"}],
-// will be interpreted as having the root key 'object' prepended - {"object":[ ... ]} - to unmarshal to a Map.
-// See mxj/j2x/j2x_test.go.
-func NewMapJson(jsonVal []byte) (Map, error) {
- // empty or nil begets empty
- if len(jsonVal) == 0 {
- m := make(map[string]interface{}, 0)
- return m, nil
- }
- // handle a goofy case ...
- if jsonVal[0] == '[' {
- jsonVal = []byte(`{"object":` + string(jsonVal) + `}`)
- }
- m := make(map[string]interface{})
- // err := json.Unmarshal(jsonVal, &m)
- buf := bytes.NewReader(jsonVal)
- dec := json.NewDecoder(buf)
- if JsonUseNumber {
- dec.UseNumber()
- }
- err := dec.Decode(&m)
- return m, err
-}
-
-// Retrieve a Map value from an io.Reader.
-// NOTE: The raw JSON off the reader is buffered to []byte using a ByteReader. If the io.Reader is an
-// os.File, there may be significant performance impact. If the io.Reader is wrapping a []byte
-// value in-memory, however, such as http.Request.Body you CAN use it to efficiently unmarshal
-// a JSON object.
-func NewMapJsonReader(jsonReader io.Reader) (Map, error) {
- jb, err := getJson(jsonReader)
- if err != nil || len(*jb) == 0 {
- return nil, err
- }
-
- // Unmarshal the 'presumed' JSON string
- return NewMapJson(*jb)
-}
-
-// Retrieve a Map value and raw JSON - []byte - from an io.Reader.
-// NOTE: The raw JSON off the reader is buffered to []byte using a ByteReader. If the io.Reader is an
-// os.File, there may be significant performance impact. If the io.Reader is wrapping a []byte
-// value in-memory, however, such as http.Request.Body you CAN use it to efficiently unmarshal
-// a JSON object and retrieve the raw JSON in a single call.
-func NewMapJsonReaderRaw(jsonReader io.Reader) (Map, []byte, error) {
- jb, err := getJson(jsonReader)
- if err != nil || len(*jb) == 0 {
- return nil, *jb, err
- }
-
- // Unmarshal the 'presumed' JSON string
- m, merr := NewMapJson(*jb)
- return m, *jb, merr
-}
-
-// Pull the next JSON string off the stream: just read from first '{' to its closing '}'.
-// Returning a pointer to the slice saves 16 bytes - maybe unnecessary, but internal to package.
-func getJson(rdr io.Reader) (*[]byte, error) {
- bval := make([]byte, 1)
- jb := make([]byte, 0)
- var inQuote, inJson bool
- var parenCnt int
- var previous byte
-
- // scan the input for a matched set of {...}
- // json.Unmarshal will handle syntax checking.
- for {
- _, err := rdr.Read(bval)
- if err != nil {
- if err == io.EOF && inJson && parenCnt > 0 {
- return &jb, fmt.Errorf("no closing } for JSON string: %s", string(jb))
- }
- return &jb, err
- }
- switch bval[0] {
- case '{':
- if !inQuote {
- parenCnt++
- inJson = true
- }
- case '}':
- if !inQuote {
- parenCnt--
- }
- if parenCnt < 0 {
- return nil, fmt.Errorf("closing } without opening {: %s", string(jb))
- }
- case '"':
- if inQuote {
- if previous == '\\' {
- break
- }
- inQuote = false
- } else {
- inQuote = true
- }
- case '\n', '\r', '\t', ' ':
- if !inQuote {
- continue
- }
- }
- if inJson {
- jb = append(jb, bval[0])
- if parenCnt == 0 {
- break
- }
- }
- previous = bval[0]
- }
-
- return &jb, nil
-}
-
-// ------------------------------- JSON Reader handler via Map values -----------------------
-
-// Default poll delay to keep Handler from spinning on an open stream
-// like sitting on os.Stdin waiting for imput.
-var jhandlerPollInterval = time.Duration(1e6)
-
-// While unnecessary, we make HandleJsonReader() have the same signature as HandleXmlReader().
-// This avoids treating one or other as a special case and discussing the underlying stdlib logic.
-
-// Bulk process JSON using handlers that process a Map value.
-// 'rdr' is an io.Reader for the JSON (stream).
-// 'mapHandler' is the Map processing handler. Return of 'false' stops io.Reader processing.
-// 'errHandler' is the error processor. Return of 'false' stops io.Reader processing and returns the error.
-// Note: mapHandler() and errHandler() calls are blocking, so reading and processing of messages is serialized.
-// This means that you can stop reading the file on error or after processing a particular message.
-// To have reading and handling run concurrently, pass argument to a go routine in handler and return 'true'.
-func HandleJsonReader(jsonReader io.Reader, mapHandler func(Map) bool, errHandler func(error) bool) error {
- var n int
- for {
- m, merr := NewMapJsonReader(jsonReader)
- n++
-
- // handle error condition with errhandler
- if merr != nil && merr != io.EOF {
- merr = fmt.Errorf("[jsonReader: %d] %s", n, merr.Error())
- if ok := errHandler(merr); !ok {
- // caused reader termination
- return merr
- }
- continue
- }
-
- // pass to maphandler
- if len(m) != 0 {
- if ok := mapHandler(m); !ok {
- break
- }
- } else if merr != io.EOF {
- <-time.After(jhandlerPollInterval)
- }
-
- if merr == io.EOF {
- break
- }
- }
- return nil
-}
-
-// Bulk process JSON using handlers that process a Map value and the raw JSON.
-// 'rdr' is an io.Reader for the JSON (stream).
-// 'mapHandler' is the Map and raw JSON - []byte - processor. Return of 'false' stops io.Reader processing.
-// 'errHandler' is the error and raw JSON processor. Return of 'false' stops io.Reader processing and returns the error.
-// Note: mapHandler() and errHandler() calls are blocking, so reading and processing of messages is serialized.
-// This means that you can stop reading the file on error or after processing a particular message.
-// To have reading and handling run concurrently, pass argument(s) to a go routine in handler and return 'true'.
-func HandleJsonReaderRaw(jsonReader io.Reader, mapHandler func(Map, []byte) bool, errHandler func(error, []byte) bool) error {
- var n int
- for {
- m, raw, merr := NewMapJsonReaderRaw(jsonReader)
- n++
-
- // handle error condition with errhandler
- if merr != nil && merr != io.EOF {
- merr = fmt.Errorf("[jsonReader: %d] %s", n, merr.Error())
- if ok := errHandler(merr, raw); !ok {
- // caused reader termination
- return merr
- }
- continue
- }
-
- // pass to maphandler
- if len(m) != 0 {
- if ok := mapHandler(m, raw); !ok {
- break
- }
- } else if merr != io.EOF {
- <-time.After(jhandlerPollInterval)
- }
-
- if merr == io.EOF {
- break
- }
- }
- return nil
-}
diff --git a/vendor/github.com/clbanning/mxj/v2/keyvalues.go b/vendor/github.com/clbanning/mxj/v2/keyvalues.go
deleted file mode 100644
index 55620ca..0000000
--- a/vendor/github.com/clbanning/mxj/v2/keyvalues.go
+++ /dev/null
@@ -1,668 +0,0 @@
-// Copyright 2012-2014 Charles Banning. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file
-
-// keyvalues.go: Extract values from an arbitrary XML doc. Tag path can include wildcard characters.
-
-package mxj
-
-import (
- "errors"
- "fmt"
- "strconv"
- "strings"
-)
-
-// ----------------------------- get everything FOR a single key -------------------------
-
-const (
- minArraySize = 32
-)
-
-var defaultArraySize int = minArraySize
-
-// SetArraySize adjust the buffers for expected number of values to return from ValuesForKey() and ValuesForPath().
-// This can have the effect of significantly reducing memory allocation-copy functions for large data sets.
-// Returns the initial buffer size.
-func SetArraySize(size int) int {
- if size > minArraySize {
- defaultArraySize = size
- } else {
- defaultArraySize = minArraySize
- }
- return defaultArraySize
-}
-
-// ValuesForKey return all values in Map, 'mv', associated with a 'key'. If len(returned_values) == 0, then no match.
-// On error, the returned slice is 'nil'. NOTE: 'key' can be wildcard, "*".
-// 'subkeys' (optional) are "key:val[:type]" strings representing attributes or elements in a list.
-// - By default 'val' is of type string. "key:val:bool" and "key:val:float" to coerce them.
-// - For attributes prefix the label with the attribute prefix character, by default a
-// hyphen, '-', e.g., "-seq:3". (See SetAttrPrefix function.)
-// - If the 'key' refers to a list, then "key:value" could select a list member of the list.
-// - The subkey can be wildcarded - "key:*" - to require that it's there with some value.
-// - If a subkey is preceeded with the '!' character, the key:value[:type] entry is treated as an
-// exclusion critera - e.g., "!author:William T. Gaddis".
-// - If val contains ":" symbol, use SetFieldSeparator to a unused symbol, perhaps "|".
-func (mv Map) ValuesForKey(key string, subkeys ...string) ([]interface{}, error) {
- m := map[string]interface{}(mv)
- var subKeyMap map[string]interface{}
- if len(subkeys) > 0 {
- var err error
- subKeyMap, err = getSubKeyMap(subkeys...)
- if err != nil {
- return nil, err
- }
- }
-
- ret := make([]interface{}, 0, defaultArraySize)
- var cnt int
- hasKey(m, key, &ret, &cnt, subKeyMap)
- return ret[:cnt], nil
-}
-
-var KeyNotExistError = errors.New("Key does not exist")
-
-// ValueForKey is a wrapper on ValuesForKey. It returns the first member of []interface{}, if any.
-// If there is no value, "nil, nil" is returned.
-func (mv Map) ValueForKey(key string, subkeys ...string) (interface{}, error) {
- vals, err := mv.ValuesForKey(key, subkeys...)
- if err != nil {
- return nil, err
- }
- if len(vals) == 0 {
- return nil, KeyNotExistError
- }
- return vals[0], nil
-}
-
-// hasKey - if the map 'key' exists append it to array
-// if it doesn't do nothing except scan array and map values
-func hasKey(iv interface{}, key string, ret *[]interface{}, cnt *int, subkeys map[string]interface{}) {
- // func hasKey(iv interface{}, key string, ret *[]interface{}, subkeys map[string]interface{}) {
- switch iv.(type) {
- case map[string]interface{}:
- vv := iv.(map[string]interface{})
- // see if the current value is of interest
- if v, ok := vv[key]; ok {
- switch v.(type) {
- case map[string]interface{}:
- if hasSubKeys(v, subkeys) {
- *ret = append(*ret, v)
- *cnt++
- }
- case []interface{}:
- for _, av := range v.([]interface{}) {
- if hasSubKeys(av, subkeys) {
- *ret = append(*ret, av)
- *cnt++
- }
- }
- default:
- if len(subkeys) == 0 {
- *ret = append(*ret, v)
- *cnt++
- }
- }
- }
-
- // wildcard case
- if key == "*" {
- for _, v := range vv {
- switch v.(type) {
- case map[string]interface{}:
- if hasSubKeys(v, subkeys) {
- *ret = append(*ret, v)
- *cnt++
- }
- case []interface{}:
- for _, av := range v.([]interface{}) {
- if hasSubKeys(av, subkeys) {
- *ret = append(*ret, av)
- *cnt++
- }
- }
- default:
- if len(subkeys) == 0 {
- *ret = append(*ret, v)
- *cnt++
- }
- }
- }
- }
-
- // scan the rest
- for _, v := range vv {
- hasKey(v, key, ret, cnt, subkeys)
- }
- case []interface{}:
- for _, v := range iv.([]interface{}) {
- hasKey(v, key, ret, cnt, subkeys)
- }
- }
-}
-
-// ----------------------- get everything for a node in the Map ---------------------------
-
-// Allow indexed arrays in "path" specification. (Request from Abhijit Kadam - abhijitk100@gmail.com.)
-// 2014.04.28 - implementation note.
-// Implemented as a wrapper of (old)ValuesForPath() because we need look-ahead logic to handle expansion
-// of wildcards and unindexed arrays. Embedding such logic into valuesForKeyPath() would have made the
-// code much more complicated; this wrapper is straightforward, easy to debug, and doesn't add significant overhead.
-
-// ValuesForPatb retrieves all values for a path from the Map. If len(returned_values) == 0, then no match.
-// On error, the returned array is 'nil'.
-// 'path' is a dot-separated path of key values.
-// - If a node in the path is '*', then everything beyond is walked.
-// - 'path' can contain indexed array references, such as, "*.data[1]" and "msgs[2].data[0].field" -
-// even "*[2].*[0].field".
-// 'subkeys' (optional) are "key:val[:type]" strings representing attributes or elements in a list.
-// - By default 'val' is of type string. "key:val:bool" and "key:val:float" to coerce them.
-// - For attributes prefix the label with the attribute prefix character, by default a
-// hyphen, '-', e.g., "-seq:3". (See SetAttrPrefix function.)
-// - If the 'path' refers to a list, then "tag:value" would return member of the list.
-// - The subkey can be wildcarded - "key:*" - to require that it's there with some value.
-// - If a subkey is preceeded with the '!' character, the key:value[:type] entry is treated as an
-// exclusion critera - e.g., "!author:William T. Gaddis".
-// - If val contains ":" symbol, use SetFieldSeparator to a unused symbol, perhaps "|".
-func (mv Map) ValuesForPath(path string, subkeys ...string) ([]interface{}, error) {
- // If there are no array indexes in path, use legacy ValuesForPath() logic.
- if strings.Index(path, "[") < 0 {
- return mv.oldValuesForPath(path, subkeys...)
- }
-
- var subKeyMap map[string]interface{}
- if len(subkeys) > 0 {
- var err error
- subKeyMap, err = getSubKeyMap(subkeys...)
- if err != nil {
- return nil, err
- }
- }
-
- keys, kerr := parsePath(path)
- if kerr != nil {
- return nil, kerr
- }
-
- vals, verr := valuesForArray(keys, mv)
- if verr != nil {
- return nil, verr // Vals may be nil, but return empty array.
- }
-
- // Need to handle subkeys ... only return members of vals that satisfy conditions.
- retvals := make([]interface{}, 0)
- for _, v := range vals {
- if hasSubKeys(v, subKeyMap) {
- retvals = append(retvals, v)
- }
- }
- return retvals, nil
-}
-
-func valuesForArray(keys []*key, m Map) ([]interface{}, error) {
- var tmppath string
- var haveFirst bool
- var vals []interface{}
- var verr error
-
- lastkey := len(keys) - 1
- for i := 0; i <= lastkey; i++ {
- if !haveFirst {
- tmppath = keys[i].name
- haveFirst = true
- } else {
- tmppath += "." + keys[i].name
- }
-
- // Look-ahead: explode wildcards and unindexed arrays.
- // Need to handle un-indexed list recursively:
- // e.g., path is "stuff.data[0]" rather than "stuff[0].data[0]".
- // Need to treat it as "stuff[0].data[0]", "stuff[1].data[0]", ...
- if !keys[i].isArray && i < lastkey && keys[i+1].isArray {
- // Can't pass subkeys because we may not be at literal end of path.
- vv, vverr := m.oldValuesForPath(tmppath)
- if vverr != nil {
- return nil, vverr
- }
- for _, v := range vv {
- // See if we can walk the value.
- am, ok := v.(map[string]interface{})
- if !ok {
- continue
- }
- // Work the backend.
- nvals, nvalserr := valuesForArray(keys[i+1:], Map(am))
- if nvalserr != nil {
- return nil, nvalserr
- }
- vals = append(vals, nvals...)
- }
- break // have recursed the whole path - return
- }
-
- if keys[i].isArray || i == lastkey {
- // Don't pass subkeys because may not be at literal end of path.
- vals, verr = m.oldValuesForPath(tmppath)
- } else {
- continue
- }
- if verr != nil {
- return nil, verr
- }
-
- if i == lastkey && !keys[i].isArray {
- break
- }
-
- // Now we're looking at an array - supposedly.
- // Is index in range of vals?
- if len(vals) <= keys[i].position {
- vals = nil
- break
- }
-
- // Return the array member of interest, if at end of path.
- if i == lastkey {
- vals = vals[keys[i].position:(keys[i].position + 1)]
- break
- }
-
- // Extract the array member of interest.
- am := vals[keys[i].position:(keys[i].position + 1)]
-
- // must be a map[string]interface{} value so we can keep walking the path
- amm, ok := am[0].(map[string]interface{})
- if !ok {
- vals = nil
- break
- }
-
- m = Map(amm)
- haveFirst = false
- }
-
- return vals, nil
-}
-
-type key struct {
- name string
- isArray bool
- position int
-}
-
-func parsePath(s string) ([]*key, error) {
- keys := strings.Split(s, ".")
-
- ret := make([]*key, 0)
-
- for i := 0; i < len(keys); i++ {
- if keys[i] == "" {
- continue
- }
-
- newkey := new(key)
- if strings.Index(keys[i], "[") < 0 {
- newkey.name = keys[i]
- ret = append(ret, newkey)
- continue
- }
-
- p := strings.Split(keys[i], "[")
- newkey.name = p[0]
- p = strings.Split(p[1], "]")
- if p[0] == "" { // no right bracket
- return nil, fmt.Errorf("no right bracket on key index: %s", keys[i])
- }
- // convert p[0] to a int value
- pos, nerr := strconv.ParseInt(p[0], 10, 32)
- if nerr != nil {
- return nil, fmt.Errorf("cannot convert index to int value: %s", p[0])
- }
- newkey.position = int(pos)
- newkey.isArray = true
- ret = append(ret, newkey)
- }
-
- return ret, nil
-}
-
-// legacy ValuesForPath() - now wrapped to handle special case of indexed arrays in 'path'.
-func (mv Map) oldValuesForPath(path string, subkeys ...string) ([]interface{}, error) {
- m := map[string]interface{}(mv)
- var subKeyMap map[string]interface{}
- if len(subkeys) > 0 {
- var err error
- subKeyMap, err = getSubKeyMap(subkeys...)
- if err != nil {
- return nil, err
- }
- }
-
- keys := strings.Split(path, ".")
- if keys[len(keys)-1] == "" {
- keys = keys[:len(keys)-1]
- }
- ivals := make([]interface{}, 0, defaultArraySize)
- var cnt int
- valuesForKeyPath(&ivals, &cnt, m, keys, subKeyMap)
- return ivals[:cnt], nil
-}
-
-func valuesForKeyPath(ret *[]interface{}, cnt *int, m interface{}, keys []string, subkeys map[string]interface{}) {
- lenKeys := len(keys)
-
- // load 'm' values into 'ret'
- // expand any lists
- if lenKeys == 0 {
- switch m.(type) {
- case map[string]interface{}:
- if subkeys != nil {
- if ok := hasSubKeys(m, subkeys); !ok {
- return
- }
- }
- *ret = append(*ret, m)
- *cnt++
- case []interface{}:
- for i, v := range m.([]interface{}) {
- if subkeys != nil {
- if ok := hasSubKeys(v, subkeys); !ok {
- continue // only load list members with subkeys
- }
- }
- *ret = append(*ret, (m.([]interface{}))[i])
- *cnt++
- }
- default:
- if subkeys != nil {
- return // must be map[string]interface{} if there are subkeys
- }
- *ret = append(*ret, m)
- *cnt++
- }
- return
- }
-
- // key of interest
- key := keys[0]
- switch key {
- case "*": // wildcard - scan all values
- switch m.(type) {
- case map[string]interface{}:
- for _, v := range m.(map[string]interface{}) {
- // valuesForKeyPath(ret, v, keys[1:], subkeys)
- valuesForKeyPath(ret, cnt, v, keys[1:], subkeys)
- }
- case []interface{}:
- for _, v := range m.([]interface{}) {
- switch v.(type) {
- // flatten out a list of maps - keys are processed
- case map[string]interface{}:
- for _, vv := range v.(map[string]interface{}) {
- // valuesForKeyPath(ret, vv, keys[1:], subkeys)
- valuesForKeyPath(ret, cnt, vv, keys[1:], subkeys)
- }
- default:
- // valuesForKeyPath(ret, v, keys[1:], subkeys)
- valuesForKeyPath(ret, cnt, v, keys[1:], subkeys)
- }
- }
- }
- default: // key - must be map[string]interface{}
- switch m.(type) {
- case map[string]interface{}:
- if v, ok := m.(map[string]interface{})[key]; ok {
- // valuesForKeyPath(ret, v, keys[1:], subkeys)
- valuesForKeyPath(ret, cnt, v, keys[1:], subkeys)
- }
- case []interface{}: // may be buried in list
- for _, v := range m.([]interface{}) {
- switch v.(type) {
- case map[string]interface{}:
- if vv, ok := v.(map[string]interface{})[key]; ok {
- // valuesForKeyPath(ret, vv, keys[1:], subkeys)
- valuesForKeyPath(ret, cnt, vv, keys[1:], subkeys)
- }
- }
- }
- }
- }
-}
-
-// hasSubKeys() - interface{} equality works for string, float64, bool
-// 'v' must be a map[string]interface{} value to have subkeys
-// 'a' can have k:v pairs with v.(string) == "*", which is treated like a wildcard.
-func hasSubKeys(v interface{}, subkeys map[string]interface{}) bool {
- if len(subkeys) == 0 {
- return true
- }
-
- switch v.(type) {
- case map[string]interface{}:
- // do all subKey name:value pairs match?
- mv := v.(map[string]interface{})
- for skey, sval := range subkeys {
- isNotKey := false
- if skey[:1] == "!" { // a NOT-key
- skey = skey[1:]
- isNotKey = true
- }
- vv, ok := mv[skey]
- if !ok { // key doesn't exist
- if isNotKey { // key not there, but that's what we want
- if kv, ok := sval.(string); ok && kv == "*" {
- continue
- }
- }
- return false
- }
- // wildcard check
- if kv, ok := sval.(string); ok && kv == "*" {
- if isNotKey { // key is there, and we don't want it
- return false
- }
- continue
- }
- switch sval.(type) {
- case string:
- if s, ok := vv.(string); ok && s == sval.(string) {
- if isNotKey {
- return false
- }
- continue
- }
- case bool:
- if b, ok := vv.(bool); ok && b == sval.(bool) {
- if isNotKey {
- return false
- }
- continue
- }
- case float64:
- if f, ok := vv.(float64); ok && f == sval.(float64) {
- if isNotKey {
- return false
- }
- continue
- }
- }
- // key there but didn't match subkey value
- if isNotKey { // that's what we want
- continue
- }
- return false
- }
- // all subkeys matched
- return true
- }
-
- // not a map[string]interface{} value, can't have subkeys
- return false
-}
-
-// Generate map of key:value entries as map[string]string.
-// 'kv' arguments are "name:value" pairs: attribute keys are designated with prepended hyphen, '-'.
-// If len(kv) == 0, the return is (nil, nil).
-func getSubKeyMap(kv ...string) (map[string]interface{}, error) {
- if len(kv) == 0 {
- return nil, nil
- }
- m := make(map[string]interface{}, 0)
- for _, v := range kv {
- vv := strings.Split(v, fieldSep)
- switch len(vv) {
- case 2:
- m[vv[0]] = interface{}(vv[1])
- case 3:
- switch vv[2] {
- case "string", "char", "text":
- m[vv[0]] = interface{}(vv[1])
- case "bool", "boolean":
- // ParseBool treats "1"==true & "0"==false
- b, err := strconv.ParseBool(vv[1])
- if err != nil {
- return nil, fmt.Errorf("can't convert subkey value to bool: %s", vv[1])
- }
- m[vv[0]] = interface{}(b)
- case "float", "float64", "num", "number", "numeric":
- f, err := strconv.ParseFloat(vv[1], 64)
- if err != nil {
- return nil, fmt.Errorf("can't convert subkey value to float: %s", vv[1])
- }
- m[vv[0]] = interface{}(f)
- default:
- return nil, fmt.Errorf("unknown subkey conversion spec: %s", v)
- }
- default:
- return nil, fmt.Errorf("unknown subkey spec: %s", v)
- }
- }
- return m, nil
-}
-
-// ------------------------------- END of valuesFor ... ----------------------------
-
-// ----------------------- locate where a key value is in the tree -------------------
-
-//----------------------------- find all paths to a key --------------------------------
-
-// PathsForKey returns all paths through Map, 'mv', (in dot-notation) that terminate with the specified key.
-// Results can be used with ValuesForPath.
-func (mv Map) PathsForKey(key string) []string {
- m := map[string]interface{}(mv)
- breadbasket := make(map[string]bool, 0)
- breadcrumbs := ""
-
- hasKeyPath(breadcrumbs, m, key, breadbasket)
- if len(breadbasket) == 0 {
- return nil
- }
-
- // unpack map keys to return
- res := make([]string, len(breadbasket))
- var i int
- for k := range breadbasket {
- res[i] = k
- i++
- }
-
- return res
-}
-
-// PathForKeyShortest extracts the shortest path from all possible paths - from PathsForKey() - in Map, 'mv'..
-// Paths are strings using dot-notation.
-func (mv Map) PathForKeyShortest(key string) string {
- paths := mv.PathsForKey(key)
-
- lp := len(paths)
- if lp == 0 {
- return ""
- }
- if lp == 1 {
- return paths[0]
- }
-
- shortest := paths[0]
- shortestLen := len(strings.Split(shortest, "."))
-
- for i := 1; i < len(paths); i++ {
- vlen := len(strings.Split(paths[i], "."))
- if vlen < shortestLen {
- shortest = paths[i]
- shortestLen = vlen
- }
- }
-
- return shortest
-}
-
-// hasKeyPath - if the map 'key' exists append it to KeyPath.path and increment KeyPath.depth
-// This is really just a breadcrumber that saves all trails that hit the prescribed 'key'.
-func hasKeyPath(crumbs string, iv interface{}, key string, basket map[string]bool) {
- switch iv.(type) {
- case map[string]interface{}:
- vv := iv.(map[string]interface{})
- if _, ok := vv[key]; ok {
- // create a new breadcrumb, intialized with the one we have
- var nbc string
- if crumbs == "" {
- nbc = key
- } else {
- nbc = crumbs + "." + key
- }
- basket[nbc] = true
- }
- // walk on down the path, key could occur again at deeper node
- for k, v := range vv {
- // create a new breadcrumb, intialized with the one we have
- var nbc string
- if crumbs == "" {
- nbc = k
- } else {
- nbc = crumbs + "." + k
- }
- hasKeyPath(nbc, v, key, basket)
- }
- case []interface{}:
- // crumb-trail doesn't change, pass it on
- for _, v := range iv.([]interface{}) {
- hasKeyPath(crumbs, v, key, basket)
- }
- }
-}
-
-var PathNotExistError = errors.New("Path does not exist")
-
-// ValueForPath wraps ValuesFor Path and returns the first value returned.
-// If no value is found it returns 'nil' and PathNotExistError.
-func (mv Map) ValueForPath(path string) (interface{}, error) {
- vals, err := mv.ValuesForPath(path)
- if err != nil {
- return nil, err
- }
- if len(vals) == 0 {
- return nil, PathNotExistError
- }
- return vals[0], nil
-}
-
-// ValuesForPathString returns the first found value for the path as a string.
-func (mv Map) ValueForPathString(path string) (string, error) {
- vals, err := mv.ValuesForPath(path)
- if err != nil {
- return "", err
- }
- if len(vals) == 0 {
- return "", errors.New("ValueForPath: path not found")
- }
- val := vals[0]
- return fmt.Sprintf("%v", val), nil
-}
-
-// ValueOrEmptyForPathString returns the first found value for the path as a string.
-// If the path is not found then it returns an empty string.
-func (mv Map) ValueOrEmptyForPathString(path string) string {
- str, _ := mv.ValueForPathString(path)
- return str
-}
diff --git a/vendor/github.com/clbanning/mxj/v2/leafnode.go b/vendor/github.com/clbanning/mxj/v2/leafnode.go
deleted file mode 100644
index 1bc814f..0000000
--- a/vendor/github.com/clbanning/mxj/v2/leafnode.go
+++ /dev/null
@@ -1,112 +0,0 @@
-package mxj
-
-// leafnode.go - return leaf nodes with paths and values for the Map
-// inspired by: https://groups.google.com/forum/#!topic/golang-nuts/3JhuVKRuBbw
-
-import (
- "strconv"
- "strings"
-)
-
-const (
- NoAttributes = true // suppress LeafNode values that are attributes
-)
-
-// LeafNode - a terminal path value in a Map.
-// For XML Map values it represents an attribute or simple element value - of type
-// string unless Map was created using Cast flag. For JSON Map values it represents
-// a string, numeric, boolean, or null value.
-type LeafNode struct {
- Path string // a dot-notation representation of the path with array subscripting
- Value interface{} // the value at the path termination
-}
-
-// LeafNodes - returns an array of all LeafNode values for the Map.
-// The option no_attr argument suppresses attribute values (keys with prepended hyphen, '-')
-// as well as the "#text" key for the associated simple element value.
-//
-// PrependAttrWithHypen(false) will result in attributes having .attr-name as
-// terminal node in 'path' while the path for the element value, itself, will be
-// the base path w/o "#text".
-//
-// LeafUseDotNotation(true) causes list members to be identified using ".N" syntax
-// rather than "[N]" syntax.
-func (mv Map) LeafNodes(no_attr ...bool) []LeafNode {
- var a bool
- if len(no_attr) == 1 {
- a = no_attr[0]
- }
-
- l := make([]LeafNode, 0)
- getLeafNodes("", "", map[string]interface{}(mv), &l, a)
- return l
-}
-
-func getLeafNodes(path, node string, mv interface{}, l *[]LeafNode, noattr bool) {
- // if stripping attributes, then also strip "#text" key
- if !noattr || node != textK {
- if path != "" && node[:1] != "[" {
- path += "."
- }
- path += node
- }
- switch mv.(type) {
- case map[string]interface{}:
- for k, v := range mv.(map[string]interface{}) {
- // if noattr && k[:1] == "-" {
- if noattr && len(attrPrefix) > 0 && strings.Index(k, attrPrefix) == 0 {
- continue
- }
- getLeafNodes(path, k, v, l, noattr)
- }
- case []interface{}:
- for i, v := range mv.([]interface{}) {
- if useDotNotation {
- getLeafNodes(path, strconv.Itoa(i), v, l, noattr)
- } else {
- getLeafNodes(path, "["+strconv.Itoa(i)+"]", v, l, noattr)
- }
- }
- default:
- // can't walk any further, so create leaf
- n := LeafNode{path, mv}
- *l = append(*l, n)
- }
-}
-
-// LeafPaths - all paths that terminate in LeafNode values.
-func (mv Map) LeafPaths(no_attr ...bool) []string {
- ln := mv.LeafNodes()
- ss := make([]string, len(ln))
- for i := 0; i < len(ln); i++ {
- ss[i] = ln[i].Path
- }
- return ss
-}
-
-// LeafValues - all terminal values in the Map.
-func (mv Map) LeafValues(no_attr ...bool) []interface{} {
- ln := mv.LeafNodes()
- vv := make([]interface{}, len(ln))
- for i := 0; i < len(ln); i++ {
- vv[i] = ln[i].Value
- }
- return vv
-}
-
-// ====================== utilities ======================
-
-// https://groups.google.com/forum/#!topic/golang-nuts/pj0C5IrZk4I
-var useDotNotation bool
-
-// LeafUseDotNotation sets a flag that list members in LeafNode paths
-// should be identified using ".N" syntax rather than the default "[N]"
-// syntax. Calling LeafUseDotNotation with no arguments toggles the
-// flag on/off; otherwise, the argument sets the flag value 'true'/'false'.
-func LeafUseDotNotation(b ...bool) {
- if len(b) == 0 {
- useDotNotation = !useDotNotation
- return
- }
- useDotNotation = b[0]
-}
diff --git a/vendor/github.com/clbanning/mxj/v2/misc.go b/vendor/github.com/clbanning/mxj/v2/misc.go
deleted file mode 100644
index 5b4fab2..0000000
--- a/vendor/github.com/clbanning/mxj/v2/misc.go
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2016 Charles Banning. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file
-
-// misc.go - mimic functions (+others) called out in:
-// https://groups.google.com/forum/#!topic/golang-nuts/jm_aGsJNbdQ
-// Primarily these methods let you retrive XML structure information.
-
-package mxj
-
-import (
- "fmt"
- "sort"
- "strings"
-)
-
-// Return the root element of the Map. If there is not a single key in Map,
-// then an error is returned.
-func (mv Map) Root() (string, error) {
- mm := map[string]interface{}(mv)
- if len(mm) != 1 {
- return "", fmt.Errorf("Map does not have singleton root. Len: %d.", len(mm))
- }
- for k, _ := range mm {
- return k, nil
- }
- return "", nil
-}
-
-// If the path is an element with sub-elements, return a list of the sub-element
-// keys. (The list is alphabeticly sorted.) NOTE: Map keys that are prefixed with
-// '-', a hyphen, are considered attributes; see m.Attributes(path).
-func (mv Map) Elements(path string) ([]string, error) {
- e, err := mv.ValueForPath(path)
- if err != nil {
- return nil, err
- }
- switch e.(type) {
- case map[string]interface{}:
- ee := e.(map[string]interface{})
- elems := make([]string, len(ee))
- var i int
- for k, _ := range ee {
- if len(attrPrefix) > 0 && strings.Index(k, attrPrefix) == 0 {
- continue // skip attributes
- }
- elems[i] = k
- i++
- }
- elems = elems[:i]
- // alphabetic sort keeps things tidy
- sort.Strings(elems)
- return elems, nil
- }
- return nil, fmt.Errorf("no elements for path: %s", path)
-}
-
-// If the path is an element with attributes, return a list of the attribute
-// keys. (The list is alphabeticly sorted.) NOTE: Map keys that are not prefixed with
-// '-', a hyphen, are not treated as attributes; see m.Elements(path). Also, if the
-// attribute prefix is "" - SetAttrPrefix("") or PrependAttrWithHyphen(false) - then
-// there are no identifiable attributes.
-func (mv Map) Attributes(path string) ([]string, error) {
- a, err := mv.ValueForPath(path)
- if err != nil {
- return nil, err
- }
- switch a.(type) {
- case map[string]interface{}:
- aa := a.(map[string]interface{})
- attrs := make([]string, len(aa))
- var i int
- for k, _ := range aa {
- if len(attrPrefix) == 0 || strings.Index(k, attrPrefix) != 0 {
- continue // skip non-attributes
- }
- attrs[i] = k[len(attrPrefix):]
- i++
- }
- attrs = attrs[:i]
- // alphabetic sort keeps things tidy
- sort.Strings(attrs)
- return attrs, nil
- }
- return nil, fmt.Errorf("no attributes for path: %s", path)
-}
diff --git a/vendor/github.com/clbanning/mxj/v2/mxj.go b/vendor/github.com/clbanning/mxj/v2/mxj.go
deleted file mode 100644
index f0592f0..0000000
--- a/vendor/github.com/clbanning/mxj/v2/mxj.go
+++ /dev/null
@@ -1,128 +0,0 @@
-// mxj - A collection of map[string]interface{} and associated XML and JSON utilities.
-// Copyright 2012-2014 Charles Banning. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file
-
-package mxj
-
-import (
- "fmt"
- "sort"
-)
-
-const (
- Cast = true // for clarity - e.g., mxj.NewMapXml(doc, mxj.Cast)
- SafeEncoding = true // ditto - e.g., mv.Json(mxj.SafeEncoding)
-)
-
-type Map map[string]interface{}
-
-// Allocate a Map.
-func New() Map {
- m := make(map[string]interface{}, 0)
- return m
-}
-
-// Cast a Map to map[string]interface{}
-func (mv Map) Old() map[string]interface{} {
- return mv
-}
-
-// Return a copy of mv as a newly allocated Map. If the Map only contains string,
-// numeric, map[string]interface{}, and []interface{} values, then it can be thought
-// of as a "deep copy." Copying a structure (or structure reference) value is subject
-// to the noted restrictions.
-// NOTE: If 'mv' includes structure values with, possibly, JSON encoding tags
-// then only public fields of the structure are in the new Map - and with
-// keys that conform to any encoding tag instructions. The structure itself will
-// be represented as a map[string]interface{} value.
-func (mv Map) Copy() (Map, error) {
- // this is the poor-man's deep copy
- // not efficient, but it works
- j, jerr := mv.Json()
- // must handle, we don't know how mv got built
- if jerr != nil {
- return nil, jerr
- }
- return NewMapJson(j)
-}
-
-// --------------- StringIndent ... from x2j.WriteMap -------------
-
-// Pretty print a Map.
-func (mv Map) StringIndent(offset ...int) string {
- return writeMap(map[string]interface{}(mv), true, true, offset...)
-}
-
-// Pretty print a Map without the value type information - just key:value entries.
-func (mv Map) StringIndentNoTypeInfo(offset ...int) string {
- return writeMap(map[string]interface{}(mv), false, true, offset...)
-}
-
-// writeMap - dumps the map[string]interface{} for examination.
-// 'typeInfo' causes value type to be printed.
-// 'offset' is initial indentation count; typically: Write(m).
-func writeMap(m interface{}, typeInfo, root bool, offset ...int) string {
- var indent int
- if len(offset) == 1 {
- indent = offset[0]
- }
-
- var s string
- switch m.(type) {
- case []interface{}:
- if typeInfo {
- s += "[[]interface{}]"
- }
- for _, v := range m.([]interface{}) {
- s += "\n"
- for i := 0; i < indent; i++ {
- s += " "
- }
- s += writeMap(v, typeInfo, false, indent+1)
- }
- case map[string]interface{}:
- list := make([][2]string, len(m.(map[string]interface{})))
- var n int
- for k, v := range m.(map[string]interface{}) {
- list[n][0] = k
- list[n][1] = writeMap(v, typeInfo, false, indent+1)
- n++
- }
- sort.Sort(mapList(list))
- for _, v := range list {
- if root {
- root = false
- } else {
- s += "\n"
- }
- for i := 0; i < indent; i++ {
- s += " "
- }
- s += v[0] + " : " + v[1]
- }
- default:
- if typeInfo {
- s += fmt.Sprintf("[%T] %+v", m, m)
- } else {
- s += fmt.Sprintf("%+v", m)
- }
- }
- return s
-}
-
-// ======================== utility ===============
-
-type mapList [][2]string
-
-func (ml mapList) Len() int {
- return len(ml)
-}
-
-func (ml mapList) Swap(i, j int) {
- ml[i], ml[j] = ml[j], ml[i]
-}
-
-func (ml mapList) Less(i, j int) bool {
- return ml[i][0] <= ml[j][0]
-}
diff --git a/vendor/github.com/clbanning/mxj/v2/newmap.go b/vendor/github.com/clbanning/mxj/v2/newmap.go
deleted file mode 100644
index b293949..0000000
--- a/vendor/github.com/clbanning/mxj/v2/newmap.go
+++ /dev/null
@@ -1,184 +0,0 @@
-// mxj - A collection of map[string]interface{} and associated XML and JSON utilities.
-// Copyright 2012-2014, 2018 Charles Banning. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file
-
-// remap.go - build a new Map from the current Map based on keyOld:keyNew mapppings
-// keys can use dot-notation, keyOld can use wildcard, '*'
-//
-// Computational strategy -
-// Using the key path - []string - traverse a new map[string]interface{} and
-// insert the oldVal as the newVal when we arrive at the end of the path.
-// If the type at the end is nil, then that is newVal
-// If the type at the end is a singleton (string, float64, bool) an array is created.
-// If the type at the end is an array, newVal is just appended.
-// If the type at the end is a map, it is inserted if possible or the map value
-// is converted into an array if necessary.
-
-package mxj
-
-import (
- "errors"
- "strings"
-)
-
-// (Map)NewMap - create a new Map from data in the current Map.
-// 'keypairs' are key mappings "oldKey:newKey" and specify that the current value of 'oldKey'
-// should be the value for 'newKey' in the returned Map.
-// - 'oldKey' supports dot-notation as described for (Map)ValuesForPath()
-// - 'newKey' supports dot-notation but with no wildcards, '*', or indexed arrays
-// - "oldKey" is shorthand for the keypair value "oldKey:oldKey"
-// - "oldKey:" and ":newKey" are invalid keypair values
-// - if 'oldKey' does not exist in the current Map, it is not written to the new Map.
-// "null" is not supported unless it is the current Map.
-// - see newmap_test.go for several syntax examples
-// - mv.NewMap() == mxj.New()
-//
-// NOTE: "examples/partial.go" shows how to create arbitrary sub-docs of an XML doc.
-func (mv Map) NewMap(keypairs ...string) (Map, error) {
- n := make(map[string]interface{}, 0)
- if len(keypairs) == 0 {
- return n, nil
- }
-
- // loop through the pairs
- var oldKey, newKey string
- var path []string
- for _, v := range keypairs {
- if len(v) == 0 {
- continue // just skip over empty keypair arguments
- }
-
- // initialize oldKey, newKey and check
- vv := strings.Split(v, ":")
- if len(vv) > 2 {
- return n, errors.New("oldKey:newKey keypair value not valid - " + v)
- }
- if len(vv) == 1 {
- oldKey, newKey = vv[0], vv[0]
- } else {
- oldKey, newKey = vv[0], vv[1]
- }
- strings.TrimSpace(oldKey)
- strings.TrimSpace(newKey)
- if i := strings.Index(newKey, "*"); i > -1 {
- return n, errors.New("newKey value cannot contain wildcard character - " + v)
- }
- if i := strings.Index(newKey, "["); i > -1 {
- return n, errors.New("newKey value cannot contain indexed arrays - " + v)
- }
- if oldKey == "" || newKey == "" {
- return n, errors.New("oldKey or newKey is not specified - " + v)
- }
-
- // get oldKey value
- oldVal, err := mv.ValuesForPath(oldKey)
- if err != nil {
- return n, err
- }
- if len(oldVal) == 0 {
- continue // oldKey has no value, may not exist in mv
- }
-
- // break down path
- path = strings.Split(newKey, ".")
- if path[len(path)-1] == "" { // ignore a trailing dot in newKey spec
- path = path[:len(path)-1]
- }
-
- addNewVal(&n, path, oldVal)
- }
-
- return n, nil
-}
-
-// navigate 'n' to end of path and add val
-func addNewVal(n *map[string]interface{}, path []string, val []interface{}) {
- // newVal - either singleton or array
- var newVal interface{}
- if len(val) == 1 {
- newVal = val[0] // is type interface{}
- } else {
- newVal = interface{}(val)
- }
-
- // walk to the position of interest, create it if necessary
- m := (*n) // initialize map walker
- var k string // key for m
- lp := len(path) - 1 // when to stop looking
- for i := 0; i < len(path); i++ {
- k = path[i]
- if i == lp {
- break
- }
- var nm map[string]interface{} // holds position of next-map
- switch m[k].(type) {
- case nil: // need a map for next node in path, so go there
- nm = make(map[string]interface{}, 0)
- m[k] = interface{}(nm)
- m = m[k].(map[string]interface{})
- case map[string]interface{}:
- // OK - got somewhere to walk to, go there
- m = m[k].(map[string]interface{})
- case []interface{}:
- // add a map and nm points to new map unless there's already
- // a map in the array, then nm points there
- // The placement of the next value in the array is dependent
- // on the sequence of members - could land on a map or a nil
- // value first. TODO: how to test this.
- a := make([]interface{}, 0)
- var foundmap bool
- for _, vv := range m[k].([]interface{}) {
- switch vv.(type) {
- case nil: // doesn't appear that this occurs, need a test case
- if foundmap { // use the first one in array
- a = append(a, vv)
- continue
- }
- nm = make(map[string]interface{}, 0)
- a = append(a, interface{}(nm))
- foundmap = true
- case map[string]interface{}:
- if foundmap { // use the first one in array
- a = append(a, vv)
- continue
- }
- nm = vv.(map[string]interface{})
- a = append(a, vv)
- foundmap = true
- default:
- a = append(a, vv)
- }
- }
- // no map found in array
- if !foundmap {
- nm = make(map[string]interface{}, 0)
- a = append(a, interface{}(nm))
- }
- m[k] = interface{}(a) // must insert in map
- m = nm
- default: // it's a string, float, bool, etc.
- aa := make([]interface{}, 0)
- nm = make(map[string]interface{}, 0)
- aa = append(aa, m[k], nm)
- m[k] = interface{}(aa)
- m = nm
- }
- }
-
- // value is nil, array or a singleton of some kind
- // initially m.(type) == map[string]interface{}
- v := m[k]
- switch v.(type) {
- case nil: // initialized
- m[k] = newVal
- case []interface{}:
- a := m[k].([]interface{})
- a = append(a, newVal)
- m[k] = interface{}(a)
- default: // v exists:string, float64, bool, map[string]interface, etc.
- a := make([]interface{}, 0)
- a = append(a, v, newVal)
- m[k] = interface{}(a)
- }
-}
diff --git a/vendor/github.com/clbanning/mxj/v2/readme.md b/vendor/github.com/clbanning/mxj/v2/readme.md
deleted file mode 100644
index 0e0a09a..0000000
--- a/vendor/github.com/clbanning/mxj/v2/readme.md
+++ /dev/null
@@ -1,209 +0,0 @@
-
mxj - to/from maps, XML and JSON
-Decode/encode XML to/from map[string]interface{} (or JSON) values, and extract/modify values from maps by key or key-path, including wildcards.
-
-mxj supplants the legacy x2j and j2x packages. If you want the old syntax, use mxj/x2j and mxj/j2x packages.
-
-
Installation
-Using go.mod:
-
-go get github.com/clbanning/mxj/v2@v2.7
-
-
-
-import "github.com/clbanning/mxj/v2"
-
-
-... or just vendor the package.
-
-
Related Packages
-
-https://github.com/clbanning/checkxml provides functions for validating XML data.
-
-
Refactor Encoder - 2020.05.01
-Issue #70 highlighted that encoding large maps does not scale well, since the original logic used string appends operations. Using bytes.Buffer results in linear scaling for very large XML docs. (Metrics based on MacBook Pro i7 w/ 16 GB.)
-
- Nodes m.XML() time
- 54809 12.53708ms
- 109780 32.403183ms
- 164678 59.826412ms
- 482598 109.358007ms
-
-
Refactor Decoder - 2015.11.15
-For over a year I've wanted to refactor the XML-to-map[string]interface{} decoder to make it more performant. I recently took the time to do that, since we were using github.com/clbanning/mxj in a production system that could be deployed on a Raspberry Pi. Now the decoder is comparable to the stdlib JSON-to-map[string]interface{} decoder in terms of its additional processing overhead relative to decoding to a structure value. As shown by:
-
- BenchmarkNewMapXml-4 100000 18043 ns/op
- BenchmarkNewStructXml-4 100000 14892 ns/op
- BenchmarkNewMapJson-4 300000 4633 ns/op
- BenchmarkNewStructJson-4 300000 3427 ns/op
- BenchmarkNewMapXmlBooks-4 20000 82850 ns/op
- BenchmarkNewStructXmlBooks-4 20000 67822 ns/op
- BenchmarkNewMapJsonBooks-4 100000 17222 ns/op
- BenchmarkNewStructJsonBooks-4 100000 15309 ns/op
-
-
Notices
-
- 2022.11.28: v2.7 - add SetGlobalKeyMapPrefix to change default prefix, '#', for default keys
- 2022.11.20: v2.6 - add NewMapForattedXmlSeq for XML docs formatted with whitespace character
- 2021.02.02: v2.5 - add XmlCheckIsValid toggle to force checking that the encoded XML is valid
- 2020.12.14: v2.4 - add XMLEscapeCharsDecoder to preserve XML escaped characters in Map values
- 2020.10.28: v2.3 - add TrimWhiteSpace option
- 2020.05.01: v2.2 - optimize map to XML encoding for large XML docs.
- 2019.07.04: v2.0 - remove unnecessary methods - mv.XmlWriterRaw, mv.XmlIndentWriterRaw - for Map and MapSeq.
- 2019.07.04: Add MapSeq type and move associated functions and methods from Map to MapSeq.
- 2019.01.21: DecodeSimpleValuesAsMap - decode to map[:map["#text":]] rather than map[:]
- 2018.04.18: mv.Xml/mv.XmlIndent encodes non-map[string]interface{} map values - map[string]string, map[int]uint, etc.
- 2018.03.29: mv.Gob/NewMapGob support gob encoding/decoding of Maps.
- 2018.03.26: Added mxj/x2j-wrapper sub-package for migrating from legacy x2j package.
- 2017.02.22: LeafNode paths can use ".N" syntax rather than "[N]" for list member indexing.
- 2017.02.10: SetFieldSeparator changes field separator for args in UpdateValuesForPath, ValuesFor... methods.
- 2017.02.06: Support XMPP stream processing - HandleXMPPStreamTag().
- 2016.11.07: Preserve name space prefix syntax in XmlSeq parser - NewMapXmlSeq(), etc.
- 2016.06.25: Support overriding default XML attribute prefix, "-", in Map keys - SetAttrPrefix().
- 2016.05.26: Support customization of xml.Decoder by exposing CustomDecoder variable.
- 2016.03.19: Escape invalid chars when encoding XML attribute and element values - XMLEscapeChars().
- 2016.03.02: By default decoding XML with float64 and bool value casting will not cast "NaN", "Inf", and "-Inf".
- To cast them to float64, first set flag with CastNanInf(true).
- 2016.02.22: New mv.Root(), mv.Elements(), mv.Attributes methods let you examine XML document structure.
- 2016.02.16: Add CoerceKeysToLower() option to handle tags with mixed capitalization.
- 2016.02.12: Seek for first xml.StartElement token; only return error if io.EOF is reached first (handles BOM).
- 2015.12.02: XML decoding/encoding that preserves original structure of document. See NewMapXmlSeq()
- and mv.XmlSeq() / mv.XmlSeqIndent().
- 2015-05-20: New: mv.StringIndentNoTypeInfo().
- Also, alphabetically sort map[string]interface{} values by key to prettify output for mv.Xml(),
- mv.XmlIndent(), mv.StringIndent(), mv.StringIndentNoTypeInfo().
- 2014-11-09: IncludeTagSeqNum() adds "_seq" key with XML doc positional information.
- (NOTE: PreserveXmlList() is similar and will be here soon.)
- 2014-09-18: inspired by NYTimes fork, added PrependAttrWithHyphen() to allow stripping hyphen from attribute tag.
- 2014-08-02: AnyXml() and AnyXmlIndent() will try to marshal arbitrary values to XML.
- 2014-04-28: ValuesForPath() and NewMap() now accept path with indexed array references.
-
-
Basic Unmarshal XML to map[string]interface{}
-
type Map map[string]interface{}
-
-Create a `Map` value, 'mv', from any `map[string]interface{}` value, 'v':
-
mv := Map(v)
-
-Unmarshal / marshal XML as a `Map` value, 'mv':
-
-
-Unmarshal XML from an `io.Reader` as a `Map` value, 'mv':
-
mv, err := NewMapXmlReader(xmlReader) // repeated calls, as with an os.File Reader, will process stream
-mv, raw, err := NewMapXmlReaderRaw(xmlReader) // 'raw' is the raw XML that was decoded
-
-Marshal `Map` value, 'mv', to an XML Writer (`io.Writer`):
-
err := mv.XmlWriter(xmlWriter)
-raw, err := mv.XmlWriterRaw(xmlWriter) // 'raw' is the raw XML that was written on xmlWriter
-
-Converting XML to JSON: see Examples for `NewMapXml` and `HandleXmlReader`.
-
-There are comparable functions and methods for JSON processing.
-
-Arbitrary structure values can be decoded to / encoded from `Map` values:
-
-To work with XML tag values, JSON or Map key values or structure field values, decode the XML, JSON
-or structure to a `Map` value, 'mv', or cast a `map[string]interface{}` value to a `Map` value, 'mv', then:
-
-
-A new `Map` with whatever keys are desired can be created from the current `Map` and then encoded in XML
-or JSON. (Note: keys can use dot-notation.)
-
newMap, err := mv.NewMap("oldKey_1:newKey_1", "oldKey_2:newKey_2", ..., "oldKey_N:newKey_N")
-newMap, err := mv.NewMap("oldKey1", "oldKey3", "oldKey5") // a subset of 'mv'; see "examples/partial.go"
-newXml, err := newMap.Xml() // for example
-newJson, err := newMap.Json() // ditto
-
-
Usage
-
-The package is fairly well [self-documented with examples](http://godoc.org/github.com/clbanning/mxj).
-
-Also, the subdirectory "examples" contains a wide range of examples, several taken from golang-nuts discussions.
-
-
XML parsing conventions
-
-Using NewMapXml()
-
- - Attributes are parsed to `map[string]interface{}` values by prefixing a hyphen, `-`,
- to the attribute label. (Unless overridden by `PrependAttrWithHyphen(false)` or
- `SetAttrPrefix()`.)
- - If the element is a simple element and has attributes, the element value
- is given the key `#text` for its `map[string]interface{}` representation. (See
- the 'atomFeedString.xml' test data, below.)
- - XML comments, directives, and process instructions are ignored.
- - If CoerceKeysToLower() has been called, then the resultant keys will be lower case.
-
-Using NewMapXmlSeq()
-
- - Attributes are parsed to `map["#attr"]map[]map[string]interface{}`values
- where the `` value has "#text" and "#seq" keys - the "#text" key holds the
- value for ``.
- - All elements, except for the root, have a "#seq" key.
- - Comments, directives, and process instructions are unmarshalled into the Map using the
- keys "#comment", "#directive", and "#procinst", respectively. (See documentation for more
- specifics.)
- - Name space syntax is preserved:
- - `something` parses to `map["ns:key"]interface{}{"something"}`
- - `xmlns:ns="http://myns.com/ns"` parses to `map["xmlns:ns"]interface{}{"http://myns.com/ns"}`
-
-Both
-
- - By default, "Nan", "Inf", and "-Inf" values are not cast to float64. If you want them
- to be cast, set a flag to cast them using CastNanInf(true).
-
-
XML encoding conventions
-
- - 'nil' `Map` values, which may represent 'null' JSON values, are encoded as ``.
- NOTE: the operation is not symmetric as `` elements are decoded as `tag:""` `Map` values,
- which, then, encode in JSON as `"tag":""` values.
- - ALSO: there is no guarantee that the encoded XML doc will be the same as the decoded one. (Go
- randomizes the walk through map[string]interface{} values.) If you plan to re-encode the
- Map value to XML and want the same sequencing of elements look at NewMapXmlSeq() and
- mv.XmlSeq() - these try to preserve the element sequencing but with added complexity when
- working with the Map representation.
-
-
Running "go test"
-
-Because there are no guarantees on the sequence map elements are retrieved, the tests have been
-written for visual verification in most cases. One advantage is that you can easily use the
-output from running "go test" as examples of calling the various functions and methods.
-
-
Motivation
-
-I make extensive use of JSON for messaging and typically unmarshal the messages into
-`map[string]interface{}` values. This is easily done using `json.Unmarshal` from the
-standard Go libraries. Unfortunately, many legacy solutions use structured
-XML messages; in those environments the applications would have to be refactored to
-interoperate with my components.
-
-The better solution is to just provide an alternative HTTP handler that receives
-XML messages and parses it into a `map[string]interface{}` value and then reuse
-all the JSON-based code. The Go `xml.Unmarshal()` function does not provide the same
-option of unmarshaling XML messages into `map[string]interface{}` values. So I wrote
-a couple of small functions to fill this gap and released them as the x2j package.
-
-Over the next year and a half additional features were added, and the companion j2x
-package was released to address XML encoding of arbitrary JSON and `map[string]interface{}`
-values. As part of a refactoring of our production system and looking at how we had been
-using the x2j and j2x packages we found that we rarely performed direct XML-to-JSON or
-JSON-to_XML conversion and that working with the XML or JSON as `map[string]interface{}`
-values was the primary value. Thus, everything was refactored into the mxj package.
-
diff --git a/vendor/github.com/clbanning/mxj/v2/remove.go b/vendor/github.com/clbanning/mxj/v2/remove.go
deleted file mode 100644
index 8362ab1..0000000
--- a/vendor/github.com/clbanning/mxj/v2/remove.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package mxj
-
-import "strings"
-
-// Removes the path.
-func (mv Map) Remove(path string) error {
- m := map[string]interface{}(mv)
- return remove(m, path)
-}
-
-func remove(m interface{}, path string) error {
- val, err := prevValueByPath(m, path)
- if err != nil {
- return err
- }
-
- lastKey := lastKey(path)
- delete(val, lastKey)
-
- return nil
-}
-
-// returns the last key of the path.
-// lastKey("a.b.c") would had returned "c"
-func lastKey(path string) string {
- keys := strings.Split(path, ".")
- key := keys[len(keys)-1]
- return key
-}
-
-// returns the path without the last key
-// parentPath("a.b.c") whould had returned "a.b"
-func parentPath(path string) string {
- keys := strings.Split(path, ".")
- parentPath := strings.Join(keys[0:len(keys)-1], ".")
- return parentPath
-}
diff --git a/vendor/github.com/clbanning/mxj/v2/rename.go b/vendor/github.com/clbanning/mxj/v2/rename.go
deleted file mode 100644
index 4c655ed..0000000
--- a/vendor/github.com/clbanning/mxj/v2/rename.go
+++ /dev/null
@@ -1,61 +0,0 @@
-package mxj
-
-import (
- "errors"
- "strings"
-)
-
-// RenameKey renames a key in a Map.
-// It works only for nested maps.
-// It doesn't work for cases when the key is in a list.
-func (mv Map) RenameKey(path string, newName string) error {
- var v bool
- var err error
- if v, err = mv.Exists(path); err == nil && !v {
- return errors.New("RenameKey: path not found: " + path)
- } else if err != nil {
- return err
- }
- if v, err = mv.Exists(parentPath(path) + "." + newName); err == nil && v {
- return errors.New("RenameKey: key already exists: " + newName)
- } else if err != nil {
- return err
- }
-
- m := map[string]interface{}(mv)
- return renameKey(m, path, newName)
-}
-
-func renameKey(m interface{}, path string, newName string) error {
- val, err := prevValueByPath(m, path)
- if err != nil {
- return err
- }
-
- oldName := lastKey(path)
- val[newName] = val[oldName]
- delete(val, oldName)
-
- return nil
-}
-
-// returns a value which contains a last key in the path
-// For example: prevValueByPath("a.b.c", {a{b{c: 3}}}) returns {c: 3}
-func prevValueByPath(m interface{}, path string) (map[string]interface{}, error) {
- keys := strings.Split(path, ".")
-
- switch mValue := m.(type) {
- case map[string]interface{}:
- for key, value := range mValue {
- if key == keys[0] {
- if len(keys) == 1 {
- return mValue, nil
- } else {
- // keep looking for the full path to the key
- return prevValueByPath(value, strings.Join(keys[1:], "."))
- }
- }
- }
- }
- return nil, errors.New("prevValueByPath: didn't find path – " + path)
-}
diff --git a/vendor/github.com/clbanning/mxj/v2/set.go b/vendor/github.com/clbanning/mxj/v2/set.go
deleted file mode 100644
index a297fc3..0000000
--- a/vendor/github.com/clbanning/mxj/v2/set.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package mxj
-
-import (
- "strings"
-)
-
-// Sets the value for the path
-func (mv Map) SetValueForPath(value interface{}, path string) error {
- pathAry := strings.Split(path, ".")
- parentPathAry := pathAry[0 : len(pathAry)-1]
- parentPath := strings.Join(parentPathAry, ".")
-
- val, err := mv.ValueForPath(parentPath)
- if err != nil {
- return err
- }
- if val == nil {
- return nil // we just ignore the request if there's no val
- }
-
- key := pathAry[len(pathAry)-1]
- cVal := val.(map[string]interface{})
- cVal[key] = value
-
- return nil
-}
diff --git a/vendor/github.com/clbanning/mxj/v2/setfieldsep.go b/vendor/github.com/clbanning/mxj/v2/setfieldsep.go
deleted file mode 100644
index b70715e..0000000
--- a/vendor/github.com/clbanning/mxj/v2/setfieldsep.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package mxj
-
-// Per: https://github.com/clbanning/mxj/issues/37#issuecomment-278651862
-var fieldSep string = ":"
-
-// SetFieldSeparator changes the default field separator, ":", for the
-// newVal argument in mv.UpdateValuesForPath and the optional 'subkey' arguments
-// in mv.ValuesForKey and mv.ValuesForPath.
-//
-// E.g., if the newVal value is "http://blah/blah", setting the field separator
-// to "|" will allow the newVal specification, "|http://blah/blah" to parse
-// properly. If called with no argument or an empty string value, the field
-// separator is set to the default, ":".
-func SetFieldSeparator(s ...string) {
- if len(s) == 0 || s[0] == "" {
- fieldSep = ":" // the default
- return
- }
- fieldSep = s[0]
-}
diff --git a/vendor/github.com/clbanning/mxj/v2/songtext.xml b/vendor/github.com/clbanning/mxj/v2/songtext.xml
deleted file mode 100644
index 8c0f2be..0000000
--- a/vendor/github.com/clbanning/mxj/v2/songtext.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-
- help me!
-
-
-
- Henry was a renegade
- Didn't like to play it safe
- One component at a time
- There's got to be a better way
- Oh, people came from miles around
- Searching for a steady job
- Welcome to the Motor Town
- Booming like an atom bomb
-
-
- Oh, Henry was the end of the story
- Then everything went wrong
- And we'll return it to its former glory
- But it just takes so long
-
-
-
- It's going to take a long time
- It's going to take it, but we'll make it one day
- It's going to take a long time
- It's going to take it, but we'll make it one day
-
-
-
diff --git a/vendor/github.com/clbanning/mxj/v2/strict.go b/vendor/github.com/clbanning/mxj/v2/strict.go
deleted file mode 100644
index 1e76956..0000000
--- a/vendor/github.com/clbanning/mxj/v2/strict.go
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2016 Charles Banning. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file
-
-// strict.go actually addresses setting xml.Decoder attribute
-// values. This'll let you parse non-standard XML.
-
-package mxj
-
-import (
- "encoding/xml"
-)
-
-// CustomDecoder can be used to specify xml.Decoder attribute
-// values, e.g., Strict:false, to be used. By default CustomDecoder
-// is nil. If CustomeDecoder != nil, then mxj.XmlCharsetReader variable is
-// ignored and must be set as part of the CustomDecoder value, if needed.
-// Usage:
-// mxj.CustomDecoder = &xml.Decoder{Strict:false}
-var CustomDecoder *xml.Decoder
-
-// useCustomDecoder copy over public attributes from customDecoder
-func useCustomDecoder(d *xml.Decoder) {
- d.Strict = CustomDecoder.Strict
- d.AutoClose = CustomDecoder.AutoClose
- d.Entity = CustomDecoder.Entity
- d.CharsetReader = CustomDecoder.CharsetReader
- d.DefaultSpace = CustomDecoder.DefaultSpace
-}
-
diff --git a/vendor/github.com/clbanning/mxj/v2/struct.go b/vendor/github.com/clbanning/mxj/v2/struct.go
deleted file mode 100644
index 9be636c..0000000
--- a/vendor/github.com/clbanning/mxj/v2/struct.go
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2012-2017 Charles Banning. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file
-
-package mxj
-
-import (
- "encoding/json"
- "errors"
- "reflect"
-
- // "github.com/fatih/structs"
-)
-
-// Create a new Map value from a structure. Error returned if argument is not a structure.
-// Only public structure fields are decoded in the Map value. See github.com/fatih/structs#Map
-// for handling of "structs" tags.
-
-// DEPRECATED - import github.com/fatih/structs and cast result of structs.Map to mxj.Map.
-// import "github.com/fatih/structs"
-// ...
-// sm, err := structs.Map()
-// if err != nil {
-// // handle error
-// }
-// m := mxj.Map(sm)
-// Alernatively uncomment the old source and import in struct.go.
-func NewMapStruct(structVal interface{}) (Map, error) {
- return nil, errors.New("deprecated - see package documentation")
- /*
- if !structs.IsStruct(structVal) {
- return nil, errors.New("NewMapStruct() error: argument is not type Struct")
- }
- return structs.Map(structVal), nil
- */
-}
-
-// Marshal a map[string]interface{} into a structure referenced by 'structPtr'. Error returned
-// if argument is not a pointer or if json.Unmarshal returns an error.
-// json.Unmarshal structure encoding rules are followed to encode public structure fields.
-func (mv Map) Struct(structPtr interface{}) error {
- // should check that we're getting a pointer.
- if reflect.ValueOf(structPtr).Kind() != reflect.Ptr {
- return errors.New("mv.Struct() error: argument is not type Ptr")
- }
-
- m := map[string]interface{}(mv)
- j, err := json.Marshal(m)
- if err != nil {
- return err
- }
-
- return json.Unmarshal(j, structPtr)
-}
diff --git a/vendor/github.com/clbanning/mxj/v2/updatevalues.go b/vendor/github.com/clbanning/mxj/v2/updatevalues.go
deleted file mode 100644
index 9e10d84..0000000
--- a/vendor/github.com/clbanning/mxj/v2/updatevalues.go
+++ /dev/null
@@ -1,258 +0,0 @@
-// Copyright 2012-2014, 2017 Charles Banning. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file
-
-// updatevalues.go - modify a value based on path and possibly sub-keys
-// TODO(clb): handle simple elements with attributes and NewMapXmlSeq Map values.
-
-package mxj
-
-import (
- "fmt"
- "strconv"
- "strings"
-)
-
-// Update value based on path and possible sub-key values.
-// A count of the number of values changed and any error are returned.
-// If the count == 0, then no path (and subkeys) matched.
-// 'newVal' can be a Map or map[string]interface{} value with a single 'key' that is the key to be modified
-// or a string value "key:value[:type]" where type is "bool" or "num" to cast the value.
-// 'path' is dot-notation list of keys to traverse; last key in path can be newVal key
-// NOTE: 'path' spec does not currently support indexed array references.
-// 'subkeys' are "key:value[:type]" entries that must match for path node
-// - For attributes prefix the label with the attribute prefix character, by default a
-// hyphen, '-', e.g., "-seq:3". (See SetAttrPrefix function.)
-// - The subkey can be wildcarded - "key:*" - to require that it's there with some value.
-// - If a subkey is preceeded with the '!' character, the key:value[:type] entry is treated as an
-// exclusion critera - e.g., "!author:William T. Gaddis".
-//
-// NOTES:
-// 1. Simple elements with attributes need a path terminated as ".#text" to modify the actual value.
-// 2. Values in Maps created using NewMapXmlSeq are map[string]interface{} values with a "#text" key.
-// 3. If values in 'newVal' or 'subkeys' args contain ":", use SetFieldSeparator to an unused symbol,
-// perhaps "|".
-func (mv Map) UpdateValuesForPath(newVal interface{}, path string, subkeys ...string) (int, error) {
- m := map[string]interface{}(mv)
-
- // extract the subkeys
- var subKeyMap map[string]interface{}
- if len(subkeys) > 0 {
- var err error
- subKeyMap, err = getSubKeyMap(subkeys...)
- if err != nil {
- return 0, err
- }
- }
-
- // extract key and value from newVal
- var key string
- var val interface{}
- switch newVal.(type) {
- case map[string]interface{}, Map:
- switch newVal.(type) { // "fallthrough is not permitted in type switch" (Spec)
- case Map:
- newVal = newVal.(Map).Old()
- }
- if len(newVal.(map[string]interface{})) != 1 {
- return 0, fmt.Errorf("newVal map can only have len == 1 - %+v", newVal)
- }
- for key, val = range newVal.(map[string]interface{}) {
- }
- case string: // split it as a key:value pair
- ss := strings.Split(newVal.(string), fieldSep)
- n := len(ss)
- if n < 2 || n > 3 {
- return 0, fmt.Errorf("unknown newVal spec - %+v", newVal)
- }
- key = ss[0]
- if n == 2 {
- val = interface{}(ss[1])
- } else if n == 3 {
- switch ss[2] {
- case "bool", "boolean":
- nv, err := strconv.ParseBool(ss[1])
- if err != nil {
- return 0, fmt.Errorf("can't convert newVal to bool - %+v", newVal)
- }
- val = interface{}(nv)
- case "num", "numeric", "float", "int":
- nv, err := strconv.ParseFloat(ss[1], 64)
- if err != nil {
- return 0, fmt.Errorf("can't convert newVal to float64 - %+v", newVal)
- }
- val = interface{}(nv)
- default:
- return 0, fmt.Errorf("unknown type for newVal value - %+v", newVal)
- }
- }
- default:
- return 0, fmt.Errorf("invalid newVal type - %+v", newVal)
- }
-
- // parse path
- keys := strings.Split(path, ".")
-
- var count int
- updateValuesForKeyPath(key, val, m, keys, subKeyMap, &count)
-
- return count, nil
-}
-
-// navigate the path
-func updateValuesForKeyPath(key string, value interface{}, m interface{}, keys []string, subkeys map[string]interface{}, cnt *int) {
- // ----- at end node: looking at possible node to get 'key' ----
- if len(keys) == 1 {
- updateValue(key, value, m, keys[0], subkeys, cnt)
- return
- }
-
- // ----- here we are navigating the path thru the penultimate node --------
- // key of interest is keys[0] - the next in the path
- switch keys[0] {
- case "*": // wildcard - scan all values
- switch m.(type) {
- case map[string]interface{}:
- for _, v := range m.(map[string]interface{}) {
- updateValuesForKeyPath(key, value, v, keys[1:], subkeys, cnt)
- }
- case []interface{}:
- for _, v := range m.([]interface{}) {
- switch v.(type) {
- // flatten out a list of maps - keys are processed
- case map[string]interface{}:
- for _, vv := range v.(map[string]interface{}) {
- updateValuesForKeyPath(key, value, vv, keys[1:], subkeys, cnt)
- }
- default:
- updateValuesForKeyPath(key, value, v, keys[1:], subkeys, cnt)
- }
- }
- }
- default: // key - must be map[string]interface{}
- switch m.(type) {
- case map[string]interface{}:
- if v, ok := m.(map[string]interface{})[keys[0]]; ok {
- updateValuesForKeyPath(key, value, v, keys[1:], subkeys, cnt)
- }
- case []interface{}: // may be buried in list
- for _, v := range m.([]interface{}) {
- switch v.(type) {
- case map[string]interface{}:
- if vv, ok := v.(map[string]interface{})[keys[0]]; ok {
- updateValuesForKeyPath(key, value, vv, keys[1:], subkeys, cnt)
- }
- }
- }
- }
- }
-}
-
-// change value if key and subkeys are present
-func updateValue(key string, value interface{}, m interface{}, keys0 string, subkeys map[string]interface{}, cnt *int) {
- // there are two possible options for the value of 'keys0': map[string]interface, []interface{}
- // and 'key' is a key in the map or is a key in a map in a list.
- switch m.(type) {
- case map[string]interface{}: // gotta have the last key
- if keys0 == "*" {
- for k := range m.(map[string]interface{}) {
- updateValue(key, value, m, k, subkeys, cnt)
- }
- return
- }
- endVal, _ := m.(map[string]interface{})[keys0]
-
- // if newV key is the end of path, replace the value for path-end
- // may be []interface{} - means replace just an entry w/ subkeys
- // otherwise replace the keys0 value if subkeys are there
- // NOTE: this will replace the subkeys, also
- if key == keys0 {
- switch endVal.(type) {
- case map[string]interface{}:
- if hasSubKeys(m, subkeys) {
- (m.(map[string]interface{}))[keys0] = value
- (*cnt)++
- }
- case []interface{}:
- // without subkeys can't select list member to modify
- // so key:value spec is it ...
- if hasSubKeys(m, subkeys) {
- (m.(map[string]interface{}))[keys0] = value
- (*cnt)++
- break
- }
- nv := make([]interface{}, 0)
- var valmodified bool
- for _, v := range endVal.([]interface{}) {
- // check entry subkeys
- if hasSubKeys(v, subkeys) {
- // replace v with value
- nv = append(nv, value)
- valmodified = true
- (*cnt)++
- continue
- }
- nv = append(nv, v)
- }
- if valmodified {
- (m.(map[string]interface{}))[keys0] = interface{}(nv)
- }
- default: // anything else is a strict replacement
- if hasSubKeys(m, subkeys) {
- (m.(map[string]interface{}))[keys0] = value
- (*cnt)++
- }
- }
- return
- }
-
- // so value is for an element of endVal
- // if endVal is a map then 'key' must be there w/ subkeys
- // if endVal is a list then 'key' must be in a list member w/ subkeys
- switch endVal.(type) {
- case map[string]interface{}:
- if !hasSubKeys(endVal, subkeys) {
- return
- }
- if _, ok := (endVal.(map[string]interface{}))[key]; ok {
- (endVal.(map[string]interface{}))[key] = value
- (*cnt)++
- }
- case []interface{}: // keys0 points to a list, check subkeys
- for _, v := range endVal.([]interface{}) {
- // got to be a map so we can replace value for 'key'
- vv, vok := v.(map[string]interface{})
- if !vok {
- continue
- }
- if _, ok := vv[key]; !ok {
- continue
- }
- if !hasSubKeys(vv, subkeys) {
- continue
- }
- vv[key] = value
- (*cnt)++
- }
- }
- case []interface{}: // key may be in a list member
- // don't need to handle keys0 == "*"; we're looking at everything, anyway.
- for _, v := range m.([]interface{}) {
- // only map values - we're looking for 'key'
- mm, ok := v.(map[string]interface{})
- if !ok {
- continue
- }
- if _, ok := mm[key]; !ok {
- continue
- }
- if !hasSubKeys(mm, subkeys) {
- continue
- }
- mm[key] = value
- (*cnt)++
- }
- }
-
- // return
-}
diff --git a/vendor/github.com/clbanning/mxj/v2/xml.go b/vendor/github.com/clbanning/mxj/v2/xml.go
deleted file mode 100644
index b72a146..0000000
--- a/vendor/github.com/clbanning/mxj/v2/xml.go
+++ /dev/null
@@ -1,1440 +0,0 @@
-// Copyright 2012-2016, 2018-2019 Charles Banning. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file
-
-// xml.go - basically the core of X2j for map[string]interface{} values.
-// NewMapXml, NewMapXmlReader, mv.Xml, mv.XmlWriter
-// see x2j and j2x for wrappers to provide end-to-end transformation of XML and JSON messages.
-
-package mxj
-
-import (
- "bytes"
- "encoding/json"
- "encoding/xml"
- "errors"
- "fmt"
- "io"
- "reflect"
- "sort"
- "strconv"
- "strings"
- "time"
-)
-
-var (
- textK = "#text"
- seqK = "#seq"
- commentK = "#comment"
- attrK = "#attr"
- directiveK = "#directive"
- procinstK = "#procinst"
- targetK = "#target"
- instK = "#inst"
-)
-
-// Support overriding default Map keys prefix
-
-func SetGlobalKeyMapPrefix(s string) {
- textK = strings.ReplaceAll(textK, textK[0:1], s)
- seqK = strings.ReplaceAll(seqK, seqK[0:1], s)
- commentK = strings.ReplaceAll(commentK, commentK[0:1], s)
- directiveK = strings.ReplaceAll(directiveK, directiveK[0:1], s)
- procinstK = strings.ReplaceAll(procinstK, procinstK[0:1], s)
- targetK = strings.ReplaceAll(targetK, targetK[0:1], s)
- instK = strings.ReplaceAll(instK, instK[0:1], s)
- attrK = strings.ReplaceAll(attrK, attrK[0:1], s)
-}
-
-// ------------------- NewMapXml & NewMapXmlReader ... -------------------------
-
-// If XmlCharsetReader != nil, it will be used to decode the XML, if required.
-// Note: if CustomDecoder != nil, then XmlCharsetReader is ignored;
-// set the CustomDecoder attribute instead.
-// import (
-// charset "code.google.com/p/go-charset/charset"
-// github.com/clbanning/mxj
-// )
-// ...
-// mxj.XmlCharsetReader = charset.NewReader
-// m, merr := mxj.NewMapXml(xmlValue)
-var XmlCharsetReader func(charset string, input io.Reader) (io.Reader, error)
-
-// NewMapXml - convert a XML doc into a Map
-// (This is analogous to unmarshalling a JSON string to map[string]interface{} using json.Unmarshal().)
-// If the optional argument 'cast' is 'true', then values will be converted to boolean or float64 if possible.
-//
-// Converting XML to JSON is a simple as:
-// ...
-// mapVal, merr := mxj.NewMapXml(xmlVal)
-// if merr != nil {
-// // handle error
-// }
-// jsonVal, jerr := mapVal.Json()
-// if jerr != nil {
-// // handle error
-// }
-//
-// NOTES:
-// 1. Declarations, directives, process instructions and comments are NOT parsed.
-// 2. The 'xmlVal' will be parsed looking for an xml.StartElement, so BOM and other
-// extraneous xml.CharData will be ignored unless io.EOF is reached first.
-// 3. If CoerceKeysToLower() has been called, then all key values will be lower case.
-// 4. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
-// 5. If DisableTrimWhiteSpace(b bool) has been called, then all values will be trimmed or not. 'true' by default.
-func NewMapXml(xmlVal []byte, cast ...bool) (Map, error) {
- var r bool
- if len(cast) == 1 {
- r = cast[0]
- }
- return xmlToMap(xmlVal, r)
-}
-
-// Get next XML doc from an io.Reader as a Map value. Returns Map value.
-// NOTES:
-// 1. Declarations, directives, process instructions and comments are NOT parsed.
-// 2. The 'xmlReader' will be parsed looking for an xml.StartElement, so BOM and other
-// extraneous xml.CharData will be ignored unless io.EOF is reached first.
-// 3. If CoerceKeysToLower() has been called, then all key values will be lower case.
-// 4. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
-func NewMapXmlReader(xmlReader io.Reader, cast ...bool) (Map, error) {
- var r bool
- if len(cast) == 1 {
- r = cast[0]
- }
-
- // We need to put an *os.File reader in a ByteReader or the xml.NewDecoder
- // will wrap it in a bufio.Reader and seek on the file beyond where the
- // xml.Decoder parses!
- if _, ok := xmlReader.(io.ByteReader); !ok {
- xmlReader = myByteReader(xmlReader) // see code at EOF
- }
-
- // build the map
- return xmlReaderToMap(xmlReader, r)
-}
-
-// Get next XML doc from an io.Reader as a Map value. Returns Map value and slice with the raw XML.
-// NOTES:
-// 1. Declarations, directives, process instructions and comments are NOT parsed.
-// 2. Due to the implementation of xml.Decoder, the raw XML off the reader is buffered to []byte
-// using a ByteReader. If the io.Reader is an os.File, there may be significant performance impact.
-// See the examples - getmetrics1.go through getmetrics4.go - for comparative use cases on a large
-// data set. If the io.Reader is wrapping a []byte value in-memory, however, such as http.Request.Body
-// you CAN use it to efficiently unmarshal a XML doc and retrieve the raw XML in a single call.
-// 3. The 'raw' return value may be larger than the XML text value.
-// 4. The 'xmlReader' will be parsed looking for an xml.StartElement, so BOM and other
-// extraneous xml.CharData will be ignored unless io.EOF is reached first.
-// 5. If CoerceKeysToLower() has been called, then all key values will be lower case.
-// 6. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
-func NewMapXmlReaderRaw(xmlReader io.Reader, cast ...bool) (Map, []byte, error) {
- var r bool
- if len(cast) == 1 {
- r = cast[0]
- }
- // create TeeReader so we can retrieve raw XML
- buf := make([]byte, 0)
- wb := bytes.NewBuffer(buf)
- trdr := myTeeReader(xmlReader, wb) // see code at EOF
-
- m, err := xmlReaderToMap(trdr, r)
-
- // retrieve the raw XML that was decoded
- b := wb.Bytes()
-
- if err != nil {
- return nil, b, err
- }
-
- return m, b, nil
-}
-
-// xmlReaderToMap() - parse a XML io.Reader to a map[string]interface{} value
-func xmlReaderToMap(rdr io.Reader, r bool) (map[string]interface{}, error) {
- // parse the Reader
- p := xml.NewDecoder(rdr)
- if CustomDecoder != nil {
- useCustomDecoder(p)
- } else {
- p.CharsetReader = XmlCharsetReader
- }
- return xmlToMapParser("", nil, p, r)
-}
-
-// xmlToMap - convert a XML doc into map[string]interface{} value
-func xmlToMap(doc []byte, r bool) (map[string]interface{}, error) {
- b := bytes.NewReader(doc)
- p := xml.NewDecoder(b)
- if CustomDecoder != nil {
- useCustomDecoder(p)
- } else {
- p.CharsetReader = XmlCharsetReader
- }
- return xmlToMapParser("", nil, p, r)
-}
-
-// ===================================== where the work happens =============================
-
-// PrependAttrWithHyphen. Prepend attribute tags with a hyphen.
-// Default is 'true'. (Not applicable to NewMapXmlSeq(), mv.XmlSeq(), etc.)
-// Note:
-// If 'false', unmarshaling and marshaling is not symmetric. Attributes will be
-// marshal'd as attr and may be part of a list.
-func PrependAttrWithHyphen(v bool) {
- if v {
- attrPrefix = "-"
- lenAttrPrefix = len(attrPrefix)
- return
- }
- attrPrefix = ""
- lenAttrPrefix = len(attrPrefix)
-}
-
-// Include sequence id with inner tags. - per Sean Murphy, murphysean84@gmail.com.
-var includeTagSeqNum bool
-
-// IncludeTagSeqNum - include a "_seq":N key:value pair with each inner tag, denoting
-// its position when parsed. This is of limited usefulness, since list values cannot
-// be tagged with "_seq" without changing their depth in the Map.
-// So THIS SHOULD BE USED WITH CAUTION - see the test cases. Here's a sample of what
-// you get.
-/*
-
-
-
-
- hello
-
-
- parses as:
-
- {
- Obj:{
- "-c":"la",
- "-h":"da",
- "-x":"dee",
- "intObj":[
- {
- "-id"="3",
- "_seq":"0" // if mxj.Cast is passed, then: "_seq":0
- },
- {
- "-id"="2",
- "_seq":"2"
- }],
- "intObj1":{
- "-id":"1",
- "_seq":"1"
- },
- "StrObj":{
- "#text":"hello", // simple element value gets "#text" tag
- "_seq":"3"
- }
- }
- }
-*/
-func IncludeTagSeqNum(b ...bool) {
- if len(b) == 0 {
- includeTagSeqNum = !includeTagSeqNum
- } else if len(b) == 1 {
- includeTagSeqNum = b[0]
- }
-}
-
-// all keys will be "lower case"
-var lowerCase bool
-
-// Coerce all tag values to keys in lower case. This is useful if you've got sources with variable
-// tag capitalization, and you want to use m.ValuesForKeys(), etc., with the key or path spec
-// in lower case.
-// CoerceKeysToLower() will toggle the coercion flag true|false - on|off
-// CoerceKeysToLower(true|false) will set the coercion flag on|off
-//
-// NOTE: only recognized by NewMapXml, NewMapXmlReader, and NewMapXmlReaderRaw functions as well as
-// the associated HandleXmlReader and HandleXmlReaderRaw.
-func CoerceKeysToLower(b ...bool) {
- if len(b) == 0 {
- lowerCase = !lowerCase
- } else if len(b) == 1 {
- lowerCase = b[0]
- }
-}
-
-// disableTrimWhiteSpace sets if the white space should be removed or not
-var disableTrimWhiteSpace bool
-var trimRunes = "\t\r\b\n "
-
-// DisableTrimWhiteSpace set if the white space should be trimmed or not. By default white space is always trimmed. If
-// no argument is provided, trim white space will be disabled.
-func DisableTrimWhiteSpace(b ...bool) {
- if len(b) == 0 {
- disableTrimWhiteSpace = true
- } else {
- disableTrimWhiteSpace = b[0]
- }
-
- if disableTrimWhiteSpace {
- trimRunes = "\t\r\b\n"
- } else {
- trimRunes = "\t\r\b\n "
- }
-}
-
-// 25jun16: Allow user to specify the "prefix" character for XML attribute key labels.
-// We do this by replacing '`' constant with attrPrefix var, replacing useHyphen with attrPrefix = "",
-// and adding a SetAttrPrefix(s string) function.
-
-var attrPrefix string = `-` // the default
-var lenAttrPrefix int = 1 // the default
-
-// SetAttrPrefix changes the default, "-", to the specified value, s.
-// SetAttrPrefix("") is the same as PrependAttrWithHyphen(false).
-// (Not applicable for NewMapXmlSeq(), mv.XmlSeq(), etc.)
-func SetAttrPrefix(s string) {
- attrPrefix = s
- lenAttrPrefix = len(attrPrefix)
-}
-
-// 18jan17: Allows user to specify if the map keys should be in snake case instead
-// of the default hyphenated notation.
-var snakeCaseKeys bool
-
-// CoerceKeysToSnakeCase changes the default, false, to the specified value, b.
-// Note: the attribute prefix will be a hyphen, '-', or what ever string value has
-// been specified using SetAttrPrefix.
-func CoerceKeysToSnakeCase(b ...bool) {
- if len(b) == 0 {
- snakeCaseKeys = !snakeCaseKeys
- } else if len(b) == 1 {
- snakeCaseKeys = b[0]
- }
-}
-
-// 10jan19: use of pull request #57 should be conditional - legacy code assumes
-// numeric values are float64.
-var castToInt bool
-
-// CastValuesToInt tries to coerce numeric valus to int64 or uint64 instead of the
-// default float64. Repeated calls with no argument will toggle this on/off, or this
-// handling will be set with the value of 'b'.
-func CastValuesToInt(b ...bool) {
- if len(b) == 0 {
- castToInt = !castToInt
- } else if len(b) == 1 {
- castToInt = b[0]
- }
-}
-
-// 05feb17: support processing XMPP streams (issue #36)
-var handleXMPPStreamTag bool
-
-// HandleXMPPStreamTag causes decoder to parse XMPP elements.
-// If called with no argument, XMPP stream element handling is toggled on/off.
-// (See xmppStream_test.go for example.)
-// If called with NewMapXml, NewMapXmlReader, New MapXmlReaderRaw the "stream"
-// element will be returned as:
-// map["stream"]interface{}{map[-]interface{}}.
-// If called with NewMapSeq, NewMapSeqReader, NewMapSeqReaderRaw the "stream"
-// element will be returned as:
-// map["stream:stream"]interface{}{map["#attr"]interface{}{map[string]interface{}}}
-// where the "#attr" values have "#text" and "#seq" keys. (See NewMapXmlSeq.)
-func HandleXMPPStreamTag(b ...bool) {
- if len(b) == 0 {
- handleXMPPStreamTag = !handleXMPPStreamTag
- } else if len(b) == 1 {
- handleXMPPStreamTag = b[0]
- }
-}
-
-// 21jan18 - decode all values as map["#text":value] (issue #56)
-var decodeSimpleValuesAsMap bool
-
-// DecodeSimpleValuesAsMap forces all values to be decoded as map["#text":].
-// If called with no argument, the decoding is toggled on/off.
-//
-// By default the NewMapXml functions decode simple values without attributes as
-// map[:]. This function causes simple values without attributes to be
-// decoded the same as simple values with attributes - map[:map["#text":]].
-func DecodeSimpleValuesAsMap(b ...bool) {
- if len(b) == 0 {
- decodeSimpleValuesAsMap = !decodeSimpleValuesAsMap
- } else if len(b) == 1 {
- decodeSimpleValuesAsMap = b[0]
- }
-}
-
-// xmlToMapParser (2015.11.12) - load a 'clean' XML doc into a map[string]interface{} directly.
-// A refactoring of xmlToTreeParser(), markDuplicate() and treeToMap() - here, all-in-one.
-// We've removed the intermediate *node tree with the allocation and subsequent rescanning.
-func xmlToMapParser(skey string, a []xml.Attr, p *xml.Decoder, r bool) (map[string]interface{}, error) {
- if lowerCase {
- skey = strings.ToLower(skey)
- }
- if snakeCaseKeys {
- skey = strings.Replace(skey, "-", "_", -1)
- }
-
- // NOTE: all attributes and sub-elements parsed into 'na', 'na' is returned as value for 'skey' in 'n'.
- // Unless 'skey' is a simple element w/o attributes, in which case the xml.CharData value is the value.
- var n, na map[string]interface{}
- var seq int // for includeTagSeqNum
-
- // Allocate maps and load attributes, if any.
- // NOTE: on entry from NewMapXml(), etc., skey=="", and we fall through
- // to get StartElement then recurse with skey==xml.StartElement.Name.Local
- // where we begin allocating map[string]interface{} values 'n' and 'na'.
- if skey != "" {
- n = make(map[string]interface{}) // old n
- na = make(map[string]interface{}) // old n.nodes
- if len(a) > 0 {
- for _, v := range a {
- if snakeCaseKeys {
- v.Name.Local = strings.Replace(v.Name.Local, "-", "_", -1)
- }
- var key string
- key = attrPrefix + v.Name.Local
- if lowerCase {
- key = strings.ToLower(key)
- }
- if xmlEscapeCharsDecoder { // per issue#84
- v.Value = escapeChars(v.Value)
- }
- na[key] = cast(v.Value, r, key)
- }
- }
- }
- // Return XMPP message.
- if handleXMPPStreamTag && skey == "stream" {
- n[skey] = na
- return n, nil
- }
-
- for {
- t, err := p.Token()
- if err != nil {
- if err != io.EOF {
- return nil, errors.New("xml.Decoder.Token() - " + err.Error())
- }
- return nil, err
- }
- switch t.(type) {
- case xml.StartElement:
- tt := t.(xml.StartElement)
-
- // First call to xmlToMapParser() doesn't pass xml.StartElement - the map key.
- // So when the loop is first entered, the first token is the root tag along
- // with any attributes, which we process here.
- //
- // Subsequent calls to xmlToMapParser() will pass in tag+attributes for
- // processing before getting the next token which is the element value,
- // which is done above.
- if skey == "" {
- return xmlToMapParser(tt.Name.Local, tt.Attr, p, r)
- }
-
- // If not initializing the map, parse the element.
- // len(nn) == 1, necessarily - it is just an 'n'.
- nn, err := xmlToMapParser(tt.Name.Local, tt.Attr, p, r)
- if err != nil {
- return nil, err
- }
-
- // The nn map[string]interface{} value is a na[nn_key] value.
- // We need to see if nn_key already exists - means we're parsing a list.
- // This may require converting na[nn_key] value into []interface{} type.
- // First, extract the key:val for the map - it's a singleton.
- // Note:
- // * if CoerceKeysToLower() called, then key will be lower case.
- // * if CoerceKeysToSnakeCase() called, then key will be converted to snake case.
- var key string
- var val interface{}
- for key, val = range nn {
- break
- }
-
- // IncludeTagSeqNum requests that the element be augmented with a "_seq" sub-element.
- // In theory, we don't need this if len(na) == 1. But, we don't know what might
- // come next - we're only parsing forward. So if you ask for 'includeTagSeqNum' you
- // get it on every element. (Personally, I never liked this, but I added it on request
- // and did get a $50 Amazon gift card in return - now we support it for backwards compatibility!)
- if includeTagSeqNum {
- switch val.(type) {
- case []interface{}:
- // noop - There's no clean way to handle this w/o changing message structure.
- case map[string]interface{}:
- val.(map[string]interface{})["_seq"] = seq // will overwrite an "_seq" XML tag
- seq++
- case interface{}: // a non-nil simple element: string, float64, bool
- v := map[string]interface{}{textK: val}
- v["_seq"] = seq
- seq++
- val = v
- }
- }
-
- // 'na' holding sub-elements of n.
- // See if 'key' already exists.
- // If 'key' exists, then this is a list, if not just add key:val to na.
- if v, ok := na[key]; ok {
- var a []interface{}
- switch v.(type) {
- case []interface{}:
- a = v.([]interface{})
- default: // anything else - note: v.(type) != nil
- a = []interface{}{v}
- }
- a = append(a, val)
- na[key] = a
- } else {
- na[key] = val // save it as a singleton
- }
- case xml.EndElement:
- // len(n) > 0 if this is a simple element w/o xml.Attrs - see xml.CharData case.
- if len(n) == 0 {
- // If len(na)==0 we have an empty element == "";
- // it has no xml.Attr nor xml.CharData.
- // Note: in original node-tree parser, val defaulted to "";
- // so we always had the default if len(node.nodes) == 0.
- if len(na) > 0 {
- n[skey] = na
- } else {
- n[skey] = "" // empty element
- }
- } else if len(n) == 1 && len(na) > 0 {
- // it's a simple element w/ no attributes w/ subelements
- for _, v := range n {
- na[textK] = v
- }
- n[skey] = na
- }
- return n, nil
- case xml.CharData:
- // clean up possible noise
- tt := strings.Trim(string(t.(xml.CharData)), trimRunes)
- if xmlEscapeCharsDecoder { // issue#84
- tt = escapeChars(tt)
- }
- if len(tt) > 0 {
- if len(na) > 0 || decodeSimpleValuesAsMap {
- na[textK] = cast(tt, r, textK)
- } else if skey != "" {
- n[skey] = cast(tt, r, skey)
- } else {
- // per Adrian (http://www.adrianlungu.com/) catch stray text
- // in decoder stream -
- // https://github.com/clbanning/mxj/pull/14#issuecomment-182816374
- // NOTE: CharSetReader must be set to non-UTF-8 CharSet or you'll get
- // a p.Token() decoding error when the BOM is UTF-16 or UTF-32.
- continue
- }
- }
- default:
- // noop
- }
- }
-}
-
-var castNanInf bool
-
-// Cast "Nan", "Inf", "-Inf" XML values to 'float64'.
-// By default, these values will be decoded as 'string'.
-func CastNanInf(b ...bool) {
- if len(b) == 0 {
- castNanInf = !castNanInf
- } else if len(b) == 1 {
- castNanInf = b[0]
- }
-}
-
-// cast - try to cast string values to bool or float64
-// 't' is the tag key that can be checked for 'not-casting'
-func cast(s string, r bool, t string) interface{} {
- if checkTagToSkip != nil && t != "" && checkTagToSkip(t) {
- // call the check-function here with 't[0]'
- // if 'true' return s
- return s
- }
-
- if r {
- // handle nan and inf
- if !castNanInf {
- switch strings.ToLower(s) {
- case "nan", "inf", "-inf":
- return s
- }
- }
-
- // handle numeric strings ahead of boolean
- if castToInt {
- if f, err := strconv.ParseInt(s, 10, 64); err == nil {
- return f
- }
- if f, err := strconv.ParseUint(s, 10, 64); err == nil {
- return f
- }
- }
-
- if castToFloat {
- if f, err := strconv.ParseFloat(s, 64); err == nil {
- return f
- }
- }
-
- // ParseBool treats "1"==true & "0"==false, we've already scanned those
- // values as float64. See if value has 't' or 'f' as initial screen to
- // minimize calls to ParseBool; also, see if len(s) < 6.
- if castToBool {
- if len(s) > 0 && len(s) < 6 {
- switch s[:1] {
- case "t", "T", "f", "F":
- if b, err := strconv.ParseBool(s); err == nil {
- return b
- }
- }
- }
- }
- }
- return s
-}
-
-// pull request, #59
-var castToFloat = true
-
-// CastValuesToFloat can be used to skip casting to float64 when
-// "cast" argument is 'true' in NewMapXml, etc.
-// Default is true.
-func CastValuesToFloat(b ...bool) {
- if len(b) == 0 {
- castToFloat = !castToFloat
- } else if len(b) == 1 {
- castToFloat = b[0]
- }
-}
-
-var castToBool = true
-
-// CastValuesToBool can be used to skip casting to bool when
-// "cast" argument is 'true' in NewMapXml, etc.
-// Default is true.
-func CastValuesToBool(b ...bool) {
- if len(b) == 0 {
- castToBool = !castToBool
- } else if len(b) == 1 {
- castToBool = b[0]
- }
-}
-
-// checkTagToSkip - switch to address Issue #58
-
-var checkTagToSkip func(string) bool
-
-// SetCheckTagToSkipFunc registers function to test whether the value
-// for a tag should be cast to bool or float64 when "cast" argument is 'true'.
-// (Dot tag path notation is not supported.)
-// NOTE: key may be "#text" if it's a simple element with attributes
-// or "decodeSimpleValuesAsMap == true".
-// NOTE: does not apply to NewMapXmlSeq... functions.
-func SetCheckTagToSkipFunc(fn func(string) bool) {
- checkTagToSkip = fn
-}
-
-// ------------------ END: NewMapXml & NewMapXmlReader -------------------------
-
-// ------------------ mv.Xml & mv.XmlWriter - from j2x ------------------------
-
-const (
- DefaultRootTag = "doc"
-)
-
-var useGoXmlEmptyElemSyntax bool
-
-// XmlGoEmptyElemSyntax() - rather than .
-// Go's encoding/xml package marshals empty XML elements as . By default this package
-// encodes empty elements as . If you're marshaling Map values that include structures
-// (which are passed to xml.Marshal for encoding), this will let you conform to the standard package.
-func XmlGoEmptyElemSyntax() {
- useGoXmlEmptyElemSyntax = true
-}
-
-// XmlDefaultEmptyElemSyntax() - rather than .
-// Return XML encoding for empty elements to the default package setting.
-// Reverses effect of XmlGoEmptyElemSyntax().
-func XmlDefaultEmptyElemSyntax() {
- useGoXmlEmptyElemSyntax = false
-}
-
-// ------- issue #88 ----------
-// xmlCheckIsValid set switch to force decoding the encoded XML to
-// see if it is valid XML.
-var xmlCheckIsValid bool
-
-// XmlCheckIsValid forces the encoded XML to be checked for validity.
-func XmlCheckIsValid(b ...bool) {
- if len(b) == 1 {
- xmlCheckIsValid = b[0]
- return
- }
- xmlCheckIsValid = !xmlCheckIsValid
-}
-
-// Encode a Map as XML. The companion of NewMapXml().
-// The following rules apply.
-// - The key label "#text" is treated as the value for a simple element with attributes.
-// - Map keys that begin with a hyphen, '-', are interpreted as attributes.
-// It is an error if the attribute doesn't have a []byte, string, number, or boolean value.
-// - Map value type encoding:
-// > string, bool, float64, int, int32, int64, float32: per "%v" formating
-// > []bool, []uint8: by casting to string
-// > structures, etc.: handed to xml.Marshal() - if there is an error, the element
-// value is "UNKNOWN"
-// - Elements with only attribute values or are null are terminated using "/>".
-// - If len(mv) == 1 and no rootTag is provided, then the map key is used as the root tag, possible.
-// Thus, `{ "key":"value" }` encodes as "value".
-// - To encode empty elements in a syntax consistent with encoding/xml call UseGoXmlEmptyElementSyntax().
-// The attributes tag=value pairs are alphabetized by "tag". Also, when encoding map[string]interface{} values -
-// complex elements, etc. - the key:value pairs are alphabetized by key so the resulting tags will appear sorted.
-func (mv Map) Xml(rootTag ...string) ([]byte, error) {
- m := map[string]interface{}(mv)
- var err error
- b := new(bytes.Buffer)
- p := new(pretty) // just a stub
-
- if len(m) == 1 && len(rootTag) == 0 {
- for key, value := range m {
- // if it an array, see if all values are map[string]interface{}
- // we force a new root tag if we'll end up with no key:value in the list
- // so: key:[string_val, bool:true] --> string_valtrue
- switch value.(type) {
- case []interface{}:
- for _, v := range value.([]interface{}) {
- switch v.(type) {
- case map[string]interface{}: // noop
- default: // anything else
- err = marshalMapToXmlIndent(false, b, DefaultRootTag, m, p)
- goto done
- }
- }
- }
- err = marshalMapToXmlIndent(false, b, key, value, p)
- }
- } else if len(rootTag) == 1 {
- err = marshalMapToXmlIndent(false, b, rootTag[0], m, p)
- } else {
- err = marshalMapToXmlIndent(false, b, DefaultRootTag, m, p)
- }
-done:
- if xmlCheckIsValid {
- d := xml.NewDecoder(bytes.NewReader(b.Bytes()))
- for {
- _, err = d.Token()
- if err == io.EOF {
- err = nil
- break
- } else if err != nil {
- return nil, err
- }
- }
- }
- return b.Bytes(), err
-}
-
-// The following implementation is provided only for symmetry with NewMapXmlReader[Raw]
-// The names will also provide a key for the number of return arguments.
-
-// Writes the Map as XML on the Writer.
-// See Xml() for encoding rules.
-func (mv Map) XmlWriter(xmlWriter io.Writer, rootTag ...string) error {
- x, err := mv.Xml(rootTag...)
- if err != nil {
- return err
- }
-
- _, err = xmlWriter.Write(x)
- return err
-}
-
-// Writes the Map as XML on the Writer. []byte is the raw XML that was written.
-// See Xml() for encoding rules.
-/*
-func (mv Map) XmlWriterRaw(xmlWriter io.Writer, rootTag ...string) ([]byte, error) {
- x, err := mv.Xml(rootTag...)
- if err != nil {
- return x, err
- }
-
- _, err = xmlWriter.Write(x)
- return x, err
-}
-*/
-
-// Writes the Map as pretty XML on the Writer.
-// See Xml() for encoding rules.
-func (mv Map) XmlIndentWriter(xmlWriter io.Writer, prefix, indent string, rootTag ...string) error {
- x, err := mv.XmlIndent(prefix, indent, rootTag...)
- if err != nil {
- return err
- }
-
- _, err = xmlWriter.Write(x)
- return err
-}
-
-// Writes the Map as pretty XML on the Writer. []byte is the raw XML that was written.
-// See Xml() for encoding rules.
-/*
-func (mv Map) XmlIndentWriterRaw(xmlWriter io.Writer, prefix, indent string, rootTag ...string) ([]byte, error) {
- x, err := mv.XmlIndent(prefix, indent, rootTag...)
- if err != nil {
- return x, err
- }
-
- _, err = xmlWriter.Write(x)
- return x, err
-}
-*/
-
-// -------------------- END: mv.Xml & mv.XmlWriter -------------------------------
-
-// -------------- Handle XML stream by processing Map value --------------------
-
-// Default poll delay to keep Handler from spinning on an open stream
-// like sitting on os.Stdin waiting for imput.
-var xhandlerPollInterval = time.Millisecond
-
-// Bulk process XML using handlers that process a Map value.
-// 'rdr' is an io.Reader for XML (stream)
-// 'mapHandler' is the Map processor. Return of 'false' stops io.Reader processing.
-// 'errHandler' is the error processor. Return of 'false' stops io.Reader processing and returns the error.
-// Note: mapHandler() and errHandler() calls are blocking, so reading and processing of messages is serialized.
-// This means that you can stop reading the file on error or after processing a particular message.
-// To have reading and handling run concurrently, pass argument to a go routine in handler and return 'true'.
-func HandleXmlReader(xmlReader io.Reader, mapHandler func(Map) bool, errHandler func(error) bool) error {
- var n int
- for {
- m, merr := NewMapXmlReader(xmlReader)
- n++
-
- // handle error condition with errhandler
- if merr != nil && merr != io.EOF {
- merr = fmt.Errorf("[xmlReader: %d] %s", n, merr.Error())
- if ok := errHandler(merr); !ok {
- // caused reader termination
- return merr
- }
- continue
- }
-
- // pass to maphandler
- if len(m) != 0 {
- if ok := mapHandler(m); !ok {
- break
- }
- } else if merr != io.EOF {
- time.Sleep(xhandlerPollInterval)
- }
-
- if merr == io.EOF {
- break
- }
- }
- return nil
-}
-
-// Bulk process XML using handlers that process a Map value and the raw XML.
-// 'rdr' is an io.Reader for XML (stream)
-// 'mapHandler' is the Map and raw XML - []byte - processor. Return of 'false' stops io.Reader processing.
-// 'errHandler' is the error and raw XML processor. Return of 'false' stops io.Reader processing and returns the error.
-// Note: mapHandler() and errHandler() calls are blocking, so reading and processing of messages is serialized.
-// This means that you can stop reading the file on error or after processing a particular message.
-// To have reading and handling run concurrently, pass argument(s) to a go routine in handler and return 'true'.
-// See NewMapXmlReaderRaw for comment on performance associated with retrieving raw XML from a Reader.
-func HandleXmlReaderRaw(xmlReader io.Reader, mapHandler func(Map, []byte) bool, errHandler func(error, []byte) bool) error {
- var n int
- for {
- m, raw, merr := NewMapXmlReaderRaw(xmlReader)
- n++
-
- // handle error condition with errhandler
- if merr != nil && merr != io.EOF {
- merr = fmt.Errorf("[xmlReader: %d] %s", n, merr.Error())
- if ok := errHandler(merr, raw); !ok {
- // caused reader termination
- return merr
- }
- continue
- }
-
- // pass to maphandler
- if len(m) != 0 {
- if ok := mapHandler(m, raw); !ok {
- break
- }
- } else if merr != io.EOF {
- time.Sleep(xhandlerPollInterval)
- }
-
- if merr == io.EOF {
- break
- }
- }
- return nil
-}
-
-// ----------------- END: Handle XML stream by processing Map value --------------
-
-// -------- a hack of io.TeeReader ... need one that's an io.ByteReader for xml.NewDecoder() ----------
-
-// This is a clone of io.TeeReader with the additional method t.ReadByte().
-// Thus, this TeeReader is also an io.ByteReader.
-// This is necessary because xml.NewDecoder uses a ByteReader not a Reader. It appears to have been written
-// with bufio.Reader or bytes.Reader in mind ... not a generic io.Reader, which doesn't have to have ReadByte()..
-// If NewDecoder is passed a Reader that does not satisfy ByteReader() it wraps the Reader with
-// bufio.NewReader and uses ReadByte rather than Read that runs the TeeReader pipe logic.
-
-type teeReader struct {
- r io.Reader
- w io.Writer
- b []byte
-}
-
-func myTeeReader(r io.Reader, w io.Writer) io.Reader {
- b := make([]byte, 1)
- return &teeReader{r, w, b}
-}
-
-// need for io.Reader - but we don't use it ...
-func (t *teeReader) Read(p []byte) (int, error) {
- return 0, nil
-}
-
-func (t *teeReader) ReadByte() (byte, error) {
- n, err := t.r.Read(t.b)
- if n > 0 {
- if _, err := t.w.Write(t.b[:1]); err != nil {
- return t.b[0], err
- }
- }
- return t.b[0], err
-}
-
-// For use with NewMapXmlReader & NewMapXmlSeqReader.
-type byteReader struct {
- r io.Reader
- b []byte
-}
-
-func myByteReader(r io.Reader) io.Reader {
- b := make([]byte, 1)
- return &byteReader{r, b}
-}
-
-// Need for io.Reader interface ...
-// Needed if reading a malformed http.Request.Body - issue #38.
-func (b *byteReader) Read(p []byte) (int, error) {
- return b.r.Read(p)
-}
-
-func (b *byteReader) ReadByte() (byte, error) {
- _, err := b.r.Read(b.b)
- if len(b.b) > 0 {
- // issue #38
- return b.b[0], err
- }
- var c byte
- return c, err
-}
-
-// ----------------------- END: io.TeeReader hack -----------------------------------
-
-// ---------------------- XmlIndent - from j2x package ----------------------------
-
-// Encode a map[string]interface{} as a pretty XML string.
-// See Xml for encoding rules.
-func (mv Map) XmlIndent(prefix, indent string, rootTag ...string) ([]byte, error) {
- m := map[string]interface{}(mv)
-
- var err error
- b := new(bytes.Buffer)
- p := new(pretty)
- p.indent = indent
- p.padding = prefix
-
- if len(m) == 1 && len(rootTag) == 0 {
- // this can extract the key for the single map element
- // use it if it isn't a key for a list
- for key, value := range m {
- if _, ok := value.([]interface{}); ok {
- err = marshalMapToXmlIndent(true, b, DefaultRootTag, m, p)
- } else {
- err = marshalMapToXmlIndent(true, b, key, value, p)
- }
- }
- } else if len(rootTag) == 1 {
- err = marshalMapToXmlIndent(true, b, rootTag[0], m, p)
- } else {
- err = marshalMapToXmlIndent(true, b, DefaultRootTag, m, p)
- }
- if xmlCheckIsValid {
- d := xml.NewDecoder(bytes.NewReader(b.Bytes()))
- for {
- _, err = d.Token()
- if err == io.EOF {
- err = nil
- break
- } else if err != nil {
- return nil, err
- }
- }
- }
- return b.Bytes(), err
-}
-
-type pretty struct {
- indent string
- cnt int
- padding string
- mapDepth int
- start int
-}
-
-func (p *pretty) Indent() {
- p.padding += p.indent
- p.cnt++
-}
-
-func (p *pretty) Outdent() {
- if p.cnt > 0 {
- p.padding = p.padding[:len(p.padding)-len(p.indent)]
- p.cnt--
- }
-}
-
-// where the work actually happens
-// returns an error if an attribute is not atomic
-// NOTE: 01may20 - replaces mapToXmlIndent(); uses bytes.Buffer instead for string appends.
-func marshalMapToXmlIndent(doIndent bool, b *bytes.Buffer, key string, value interface{}, pp *pretty) error {
- var err error
- var endTag bool
- var isSimple bool
- var elen int
- p := &pretty{pp.indent, pp.cnt, pp.padding, pp.mapDepth, pp.start}
-
- // per issue #48, 18apr18 - try and coerce maps to map[string]interface{}
- // Don't need for mapToXmlSeqIndent, since maps there are decoded by NewMapXmlSeq().
- if reflect.ValueOf(value).Kind() == reflect.Map {
- switch value.(type) {
- case map[string]interface{}:
- default:
- val := make(map[string]interface{})
- vv := reflect.ValueOf(value)
- keys := vv.MapKeys()
- for _, k := range keys {
- val[fmt.Sprint(k)] = vv.MapIndex(k).Interface()
- }
- value = val
- }
- }
-
- // 14jul20. The following block of code has become something of a catch all for odd stuff
- // that might be passed in as a result of casting an arbitrary map[] to an mxj.Map
- // value and then call m.Xml or m.XmlIndent. See issue #71 (and #73) for such edge cases.
- switch value.(type) {
- // these types are handled during encoding
- case map[string]interface{}, []byte, string, float64, bool, int, int32, int64, float32, json.Number:
- case []map[string]interface{}, []string, []float64, []bool, []int, []int32, []int64, []float32, []json.Number:
- case []interface{}:
- case nil:
- value = ""
- default:
- // see if value is a struct, if so marshal using encoding/xml package
- if reflect.ValueOf(value).Kind() == reflect.Struct {
- if v, err := xml.Marshal(value); err != nil {
- return err
- } else {
- value = string(v)
- }
- } else {
- // coerce eveything else into a string value
- value = fmt.Sprint(value)
- }
- }
-
- // start the XML tag with required indentaton and padding
- if doIndent {
- switch value.(type) {
- case []interface{}, []string:
- // list processing handles indentation for all elements
- default:
- if _, err = b.WriteString(p.padding); err != nil {
- return err
- }
- }
- }
- switch value.(type) {
- case []interface{}:
- default:
- if _, err = b.WriteString(`<` + key); err != nil {
- return err
- }
- }
-
- switch value.(type) {
- case map[string]interface{}:
- vv := value.(map[string]interface{})
- lenvv := len(vv)
- // scan out attributes - attribute keys have prepended attrPrefix
- attrlist := make([][2]string, len(vv))
- var n int
- var ss string
- for k, v := range vv {
- if lenAttrPrefix > 0 && lenAttrPrefix < len(k) && k[:lenAttrPrefix] == attrPrefix {
- switch v.(type) {
- case string:
- if xmlEscapeChars {
- ss = escapeChars(v.(string))
- } else {
- ss = v.(string)
- }
- attrlist[n][0] = k[lenAttrPrefix:]
- attrlist[n][1] = ss
- case float64, bool, int, int32, int64, float32, json.Number:
- attrlist[n][0] = k[lenAttrPrefix:]
- attrlist[n][1] = fmt.Sprintf("%v", v)
- case []byte:
- if xmlEscapeChars {
- ss = escapeChars(string(v.([]byte)))
- } else {
- ss = string(v.([]byte))
- }
- attrlist[n][0] = k[lenAttrPrefix:]
- attrlist[n][1] = ss
- default:
- return fmt.Errorf("invalid attribute value for: %s:<%T>", k, v)
- }
- n++
- }
- }
- if n > 0 {
- attrlist = attrlist[:n]
- sort.Sort(attrList(attrlist))
- for _, v := range attrlist {
- if _, err = b.WriteString(` ` + v[0] + `="` + v[1] + `"`); err != nil {
- return err
- }
- }
- }
- // only attributes?
- if n == lenvv {
- if useGoXmlEmptyElemSyntax {
- if _, err = b.WriteString(`` + key + ">"); err != nil {
- return err
- }
- } else {
- if _, err = b.WriteString(`/>`); err != nil {
- return err
- }
- }
- break
- }
-
- // simple element? Note: '#text" is an invalid XML tag.
- isComplex := false
- if v, ok := vv[textK]; ok && n+1 == lenvv {
- // just the value and attributes
- switch v.(type) {
- case string:
- if xmlEscapeChars {
- v = escapeChars(v.(string))
- } else {
- v = v.(string)
- }
- case []byte:
- if xmlEscapeChars {
- v = escapeChars(string(v.([]byte)))
- } else {
- v = string(v.([]byte))
- }
- }
- if _, err = b.WriteString(">" + fmt.Sprintf("%v", v)); err != nil {
- return err
- }
- endTag = true
- elen = 1
- isSimple = true
- break
- } else if ok {
- // need to handle when there are subelements in addition to the simple element value
- // issue #90
- switch v.(type) {
- case string:
- if xmlEscapeChars {
- v = escapeChars(v.(string))
- } else {
- v = v.(string)
- }
- case []byte:
- if xmlEscapeChars {
- v = escapeChars(string(v.([]byte)))
- } else {
- v = string(v.([]byte))
- }
- }
- if _, err = b.WriteString(">" + fmt.Sprintf("%v", v)); err != nil {
- return err
- }
- isComplex = true
- }
-
- // close tag with possible attributes
- if !isComplex {
- if _, err = b.WriteString(">"); err != nil {
- return err
- }
- }
- if doIndent {
- // *s += "\n"
- if _, err = b.WriteString("\n"); err != nil {
- return err
- }
- }
- // something more complex
- p.mapDepth++
- // extract the map k:v pairs and sort on key
- elemlist := make([][2]interface{}, len(vv))
- n = 0
- for k, v := range vv {
- if k == textK {
- // simple element handled above
- continue
- }
- if lenAttrPrefix > 0 && lenAttrPrefix < len(k) && k[:lenAttrPrefix] == attrPrefix {
- continue
- }
- elemlist[n][0] = k
- elemlist[n][1] = v
- n++
- }
- elemlist = elemlist[:n]
- sort.Sort(elemList(elemlist))
- var i int
- for _, v := range elemlist {
- switch v[1].(type) {
- case []interface{}:
- default:
- if i == 0 && doIndent {
- p.Indent()
- }
- }
- i++
- if err := marshalMapToXmlIndent(doIndent, b, v[0].(string), v[1], p); err != nil {
- return err
- }
- switch v[1].(type) {
- case []interface{}: // handled in []interface{} case
- default:
- if doIndent {
- p.Outdent()
- }
- }
- i--
- }
- p.mapDepth--
- endTag = true
- elen = 1 // we do have some content ...
- case []interface{}:
- // special case - found during implementing Issue #23
- if len(value.([]interface{})) == 0 {
- if doIndent {
- if _, err = b.WriteString(p.padding + p.indent); err != nil {
- return err
- }
- }
- if _, err = b.WriteString("<" + key); err != nil {
- return err
- }
- elen = 0
- endTag = true
- break
- }
- for _, v := range value.([]interface{}) {
- if doIndent {
- p.Indent()
- }
- if err := marshalMapToXmlIndent(doIndent, b, key, v, p); err != nil {
- return err
- }
- if doIndent {
- p.Outdent()
- }
- }
- return nil
- case []string:
- // This was added by https://github.com/slotix ... not a type that
- // would be encountered if mv generated from NewMapXml, NewMapJson.
- // Could be encountered in AnyXml(), so we'll let it stay, though
- // it should be merged with case []interface{}, above.
- //quick fix for []string type
- //[]string should be treated exaclty as []interface{}
- if len(value.([]string)) == 0 {
- if doIndent {
- if _, err = b.WriteString(p.padding + p.indent); err != nil {
- return err
- }
- }
- if _, err = b.WriteString("<" + key); err != nil {
- return err
- }
- elen = 0
- endTag = true
- break
- }
- for _, v := range value.([]string) {
- if doIndent {
- p.Indent()
- }
- if err := marshalMapToXmlIndent(doIndent, b, key, v, p); err != nil {
- return err
- }
- if doIndent {
- p.Outdent()
- }
- }
- return nil
- case nil:
- // terminate the tag
- if doIndent {
- // *s += p.padding
- if _, err = b.WriteString(p.padding); err != nil {
- return err
- }
- }
- if _, err = b.WriteString("<" + key); err != nil {
- return err
- }
- endTag, isSimple = true, true
- break
- default: // handle anything - even goofy stuff
- elen = 0
- switch value.(type) {
- case string:
- v := value.(string)
- if xmlEscapeChars {
- v = escapeChars(v)
- }
- elen = len(v)
- if elen > 0 {
- // *s += ">" + v
- if _, err = b.WriteString(">" + v); err != nil {
- return err
- }
- }
- case float64, bool, int, int32, int64, float32, json.Number:
- v := fmt.Sprintf("%v", value)
- elen = len(v) // always > 0
- if _, err = b.WriteString(">" + v); err != nil {
- return err
- }
- case []byte: // NOTE: byte is just an alias for uint8
- // similar to how xml.Marshal handles []byte structure members
- v := string(value.([]byte))
- if xmlEscapeChars {
- v = escapeChars(v)
- }
- elen = len(v)
- if elen > 0 {
- // *s += ">" + v
- if _, err = b.WriteString(">" + v); err != nil {
- return err
- }
- }
- default:
- if _, err = b.WriteString(">"); err != nil {
- return err
- }
- var v []byte
- var err error
- if doIndent {
- v, err = xml.MarshalIndent(value, p.padding, p.indent)
- } else {
- v, err = xml.Marshal(value)
- }
- if err != nil {
- if _, err = b.WriteString(">UNKNOWN"); err != nil {
- return err
- }
- } else {
- elen = len(v)
- if elen > 0 {
- if _, err = b.Write(v); err != nil {
- return err
- }
- }
- }
- }
- isSimple = true
- endTag = true
- }
- if endTag {
- if doIndent {
- if !isSimple {
- if _, err = b.WriteString(p.padding); err != nil {
- return err
- }
- }
- }
- if elen > 0 || useGoXmlEmptyElemSyntax {
- if elen == 0 {
- if _, err = b.WriteString(">"); err != nil {
- return err
- }
- }
- if _, err = b.WriteString(`` + key + ">"); err != nil {
- return err
- }
- } else {
- if _, err = b.WriteString(`/>`); err != nil {
- return err
- }
- }
- }
- if doIndent {
- if p.cnt > p.start {
- if _, err = b.WriteString("\n"); err != nil {
- return err
- }
- }
- p.Outdent()
- }
-
- return nil
-}
-
-// ============================ sort interface implementation =================
-
-type attrList [][2]string
-
-func (a attrList) Len() int {
- return len(a)
-}
-
-func (a attrList) Swap(i, j int) {
- a[i], a[j] = a[j], a[i]
-}
-
-func (a attrList) Less(i, j int) bool {
- return a[i][0] <= a[j][0]
-}
-
-type elemList [][2]interface{}
-
-func (e elemList) Len() int {
- return len(e)
-}
-
-func (e elemList) Swap(i, j int) {
- e[i], e[j] = e[j], e[i]
-}
-
-func (e elemList) Less(i, j int) bool {
- return e[i][0].(string) <= e[j][0].(string)
-}
diff --git a/vendor/github.com/clbanning/mxj/v2/xmlseq.go b/vendor/github.com/clbanning/mxj/v2/xmlseq.go
deleted file mode 100644
index 9732dec..0000000
--- a/vendor/github.com/clbanning/mxj/v2/xmlseq.go
+++ /dev/null
@@ -1,902 +0,0 @@
-// Copyright 2012-2016, 2019 Charles Banning. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file
-
-// xmlseq.go - version of xml.go with sequence # injection on Decoding and sorting on Encoding.
-// Also, handles comments, directives and process instructions.
-
-package mxj
-
-import (
- "bytes"
- "encoding/xml"
- "errors"
- "fmt"
- "io"
- "regexp"
- "sort"
- "strings"
-)
-
-// MapSeq is like Map but contains seqencing indices to allow recovering the original order of
-// the XML elements when the map[string]interface{} is marshaled. Element attributes are
-// stored as a map["#attr"]map[]map[string]interface{}{"#text":"", "#seq":}
-// value instead of denoting the keys with a prefix character. Also, comments, directives and
-// process instructions are preserved.
-type MapSeq map[string]interface{}
-
-// NoRoot is returned by NewXmlSeq, etc., when a comment, directive or procinstr element is parsed
-// in the XML data stream and the element is not contained in an XML object with a root element.
-var NoRoot = errors.New("no root key")
-var NO_ROOT = NoRoot // maintain backwards compatibility
-
-// ------------------- NewMapXmlSeq & NewMapXmlSeqReader ... -------------------------
-
-// NewMapXmlSeq converts a XML doc into a MapSeq value with elements id'd with decoding sequence key represented
-// as map["#seq"].
-// If the optional argument 'cast' is 'true', then values will be converted to boolean or float64 if possible.
-// NOTE: "#seq" key/value pairs are removed on encoding with msv.Xml() / msv.XmlIndent().
-// • attributes are a map - map["#attr"]map["attr_key"]map[string]interface{}{"#text":, "#seq":}
-// • all simple elements are decoded as map["#text"]interface{} with a "#seq" k:v pair, as well.
-// • lists always decode as map["list_tag"][]map[string]interface{} where the array elements are maps that
-// include a "#seq" k:v pair based on sequence they are decoded. Thus, XML like:
-//
-// value 1
-// value 2
-// value 3
-//
-// is decoded as:
-// doc :
-// ltag :[[]interface{}]
-// [item: 0]
-// #seq :[int] 0
-// #text :[string] value 1
-// [item: 1]
-// #seq :[int] 2
-// #text :[string] value 3
-// newtag :
-// #seq :[int] 1
-// #text :[string] value 2
-// It will encode in proper sequence even though the MapSeq representation merges all "ltag" elements in an array.
-// • comments - "" - are decoded as map["#comment"]map["#text"]"cmnt_text" with a "#seq" k:v pair.
-// • directives - "" - are decoded as map["#directive"]map[#text"]"directive_text" with a "#seq" k:v pair.
-// • process instructions - "" - are decoded as map["#procinst"]interface{} where the #procinst value
-// is of map[string]interface{} type with the following keys: #target, #inst, and #seq.
-// • comments, directives, and procinsts that are NOT part of a document with a root key will be returned as
-// map[string]interface{} and the error value 'NoRoot'.
-// • note: ": tag preserve the
-// ":" notation rather than stripping it as with NewMapXml().
-// 2. Attribute keys for name space prefix declarations preserve "xmlns:" notation.
-//
-// ERRORS:
-// 1. If a NoRoot error, "no root key," is returned, check the initial map key for a "#comment",
-// "#directive" or #procinst" key.
-// 2. Unmarshaling an XML doc that is formatted using the whitespace character, " ", will error, since
-// Decoder.RawToken treats such occurances as significant. See NewMapFormattedXmlSeq().
-func NewMapXmlSeq(xmlVal []byte, cast ...bool) (MapSeq, error) {
- var r bool
- if len(cast) == 1 {
- r = cast[0]
- }
- return xmlSeqToMap(xmlVal, r)
-}
-
-// NewMapFormattedXmlSeq performs the same as NewMapXmlSeq but is useful for processing XML objects that
-// are formatted using the whitespace character, " ". (The stdlib xml.Decoder, by default, treats all
-// whitespace as significant; Decoder.Token() and Decoder.RawToken() will return strings of one or more
-// whitespace characters and without alphanumeric or punctuation characters as xml.CharData values.)
-//
-// If you're processing such XML, then this will convert all occurrences of whitespace-only strings
-// into an empty string, "", prior to parsing the XML - irrespective of whether the occurrence is
-// formatting or is a actual element value.
-func NewMapFormattedXmlSeq(xmlVal []byte, cast ...bool) (MapSeq, error) {
- var c bool
- if len(cast) == 1 {
- c = cast[0]
- }
-
- // Per PR #104 - clean out formatting characters so they don't show up in Decoder.RawToken() stream.
- // NOTE: Also replaces element values that are solely comprised of formatting/whitespace characters
- // with empty string, "".
- r := regexp.MustCompile(`>[\n\t\r ]*<`)
- xmlVal = r.ReplaceAll(xmlVal, []byte("><"))
- return xmlSeqToMap(xmlVal, c)
-}
-
-// NewMpaXmlSeqReader returns next XML doc from an io.Reader as a MapSeq value.
-// NOTES:
-// 1. The 'xmlReader' will be parsed looking for an xml.StartElement, xml.Comment, etc., so BOM and other
-// extraneous xml.CharData will be ignored unless io.EOF is reached first.
-// 2. CoerceKeysToLower() is NOT recognized, since the intent here is to eventually call m.XmlSeq() to
-// re-encode the message in its original structure.
-// 3. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
-//
-// ERRORS:
-// 1. If a NoRoot error, "no root key," is returned, check the initial map key for a "#comment",
-// "#directive" or #procinst" key.
-func NewMapXmlSeqReader(xmlReader io.Reader, cast ...bool) (MapSeq, error) {
- var r bool
- if len(cast) == 1 {
- r = cast[0]
- }
-
- // We need to put an *os.File reader in a ByteReader or the xml.NewDecoder
- // will wrap it in a bufio.Reader and seek on the file beyond where the
- // xml.Decoder parses!
- if _, ok := xmlReader.(io.ByteReader); !ok {
- xmlReader = myByteReader(xmlReader) // see code at EOF
- }
-
- // build the map
- return xmlSeqReaderToMap(xmlReader, r)
-}
-
-// NewMapXmlSeqReaderRaw returns the next XML doc from an io.Reader as a MapSeq value.
-// Returns MapSeq value, slice with the raw XML, and any error.
-// NOTES:
-// 1. Due to the implementation of xml.Decoder, the raw XML off the reader is buffered to []byte
-// using a ByteReader. If the io.Reader is an os.File, there may be significant performance impact.
-// See the examples - getmetrics1.go through getmetrics4.go - for comparative use cases on a large
-// data set. If the io.Reader is wrapping a []byte value in-memory, however, such as http.Request.Body
-// you CAN use it to efficiently unmarshal a XML doc and retrieve the raw XML in a single call.
-// 2. The 'raw' return value may be larger than the XML text value.
-// 3. The 'xmlReader' will be parsed looking for an xml.StartElement, xml.Comment, etc., so BOM and other
-// extraneous xml.CharData will be ignored unless io.EOF is reached first.
-// 4. CoerceKeysToLower() is NOT recognized, since the intent here is to eventually call m.XmlSeq() to
-// re-encode the message in its original structure.
-// 5. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
-//
-// ERRORS:
-// 1. If a NoRoot error, "no root key," is returned, check if the initial map key is "#comment",
-// "#directive" or #procinst" key.
-func NewMapXmlSeqReaderRaw(xmlReader io.Reader, cast ...bool) (MapSeq, []byte, error) {
- var r bool
- if len(cast) == 1 {
- r = cast[0]
- }
- // create TeeReader so we can retrieve raw XML
- buf := make([]byte, 0)
- wb := bytes.NewBuffer(buf)
- trdr := myTeeReader(xmlReader, wb)
-
- m, err := xmlSeqReaderToMap(trdr, r)
-
- // retrieve the raw XML that was decoded
- b := wb.Bytes()
-
- // err may be NoRoot
- return m, b, err
-}
-
-// xmlSeqReaderToMap() - parse a XML io.Reader to a map[string]interface{} value
-func xmlSeqReaderToMap(rdr io.Reader, r bool) (map[string]interface{}, error) {
- // parse the Reader
- p := xml.NewDecoder(rdr)
- if CustomDecoder != nil {
- useCustomDecoder(p)
- } else {
- p.CharsetReader = XmlCharsetReader
- }
- return xmlSeqToMapParser("", nil, p, r)
-}
-
-// xmlSeqToMap - convert a XML doc into map[string]interface{} value
-func xmlSeqToMap(doc []byte, r bool) (map[string]interface{}, error) {
- b := bytes.NewReader(doc)
- p := xml.NewDecoder(b)
- if CustomDecoder != nil {
- useCustomDecoder(p)
- } else {
- p.CharsetReader = XmlCharsetReader
- }
- return xmlSeqToMapParser("", nil, p, r)
-}
-
-// ===================================== where the work happens =============================
-
-// xmlSeqToMapParser - load a 'clean' XML doc into a map[string]interface{} directly.
-// Add #seq tag value for each element decoded - to be used for Encoding later.
-func xmlSeqToMapParser(skey string, a []xml.Attr, p *xml.Decoder, r bool) (map[string]interface{}, error) {
- if snakeCaseKeys {
- skey = strings.Replace(skey, "-", "_", -1)
- }
-
- // NOTE: all attributes and sub-elements parsed into 'na', 'na' is returned as value for 'skey' in 'n'.
- var n, na map[string]interface{}
- var seq int // for including seq num when decoding
-
- // Allocate maps and load attributes, if any.
- // NOTE: on entry from NewMapXml(), etc., skey=="", and we fall through
- // to get StartElement then recurse with skey==xml.StartElement.Name.Local
- // where we begin allocating map[string]interface{} values 'n' and 'na'.
- if skey != "" {
- // 'n' only needs one slot - save call to runtime•hashGrow()
- // 'na' we don't know
- n = make(map[string]interface{}, 1)
- na = make(map[string]interface{})
- if len(a) > 0 {
- // xml.Attr is decoded into: map["#attr"]map[]interface{}
- // where interface{} is map[string]interface{}{"#text":, "#seq":}
- aa := make(map[string]interface{}, len(a))
- for i, v := range a {
- if snakeCaseKeys {
- v.Name.Local = strings.Replace(v.Name.Local, "-", "_", -1)
- }
- if xmlEscapeCharsDecoder { // per issue#84
- v.Value = escapeChars(v.Value)
- }
- if len(v.Name.Space) > 0 {
- aa[v.Name.Space+`:`+v.Name.Local] = map[string]interface{}{textK: cast(v.Value, r, ""), seqK: i}
- } else {
- aa[v.Name.Local] = map[string]interface{}{textK: cast(v.Value, r, ""), seqK: i}
- }
- }
- na[attrK] = aa
- }
- }
-
- // Return XMPP message.
- if handleXMPPStreamTag && skey == "stream:stream" {
- n[skey] = na
- return n, nil
- }
-
- for {
- t, err := p.RawToken()
- if err != nil {
- if err != io.EOF {
- return nil, errors.New("xml.Decoder.Token() - " + err.Error())
- }
- return nil, err
- }
- switch t.(type) {
- case xml.StartElement:
- tt := t.(xml.StartElement)
-
- // First call to xmlSeqToMapParser() doesn't pass xml.StartElement - the map key.
- // So when the loop is first entered, the first token is the root tag along
- // with any attributes, which we process here.
- //
- // Subsequent calls to xmlSeqToMapParser() will pass in tag+attributes for
- // processing before getting the next token which is the element value,
- // which is done above.
- if skey == "" {
- if len(tt.Name.Space) > 0 {
- return xmlSeqToMapParser(tt.Name.Space+`:`+tt.Name.Local, tt.Attr, p, r)
- } else {
- return xmlSeqToMapParser(tt.Name.Local, tt.Attr, p, r)
- }
- }
-
- // If not initializing the map, parse the element.
- // len(nn) == 1, necessarily - it is just an 'n'.
- var nn map[string]interface{}
- if len(tt.Name.Space) > 0 {
- nn, err = xmlSeqToMapParser(tt.Name.Space+`:`+tt.Name.Local, tt.Attr, p, r)
- } else {
- nn, err = xmlSeqToMapParser(tt.Name.Local, tt.Attr, p, r)
- }
- if err != nil {
- return nil, err
- }
-
- // The nn map[string]interface{} value is a na[nn_key] value.
- // We need to see if nn_key already exists - means we're parsing a list.
- // This may require converting na[nn_key] value into []interface{} type.
- // First, extract the key:val for the map - it's a singleton.
- var key string
- var val interface{}
- for key, val = range nn {
- break
- }
-
- // add "#seq" k:v pair -
- // Sequence number included even in list elements - this should allow us
- // to properly resequence even something goofy like:
- // item 1
- // item 2
- // item 3
- // where all the "list" subelements are decoded into an array.
- switch val.(type) {
- case map[string]interface{}:
- val.(map[string]interface{})[seqK] = seq
- seq++
- case interface{}: // a non-nil simple element: string, float64, bool
- v := map[string]interface{}{textK: val, seqK: seq}
- seq++
- val = v
- }
-
- // 'na' holding sub-elements of n.
- // See if 'key' already exists.
- // If 'key' exists, then this is a list, if not just add key:val to na.
- if v, ok := na[key]; ok {
- var a []interface{}
- switch v.(type) {
- case []interface{}:
- a = v.([]interface{})
- default: // anything else - note: v.(type) != nil
- a = []interface{}{v}
- }
- a = append(a, val)
- na[key] = a
- } else {
- na[key] = val // save it as a singleton
- }
- case xml.EndElement:
- if skey != "" {
- tt := t.(xml.EndElement)
- if snakeCaseKeys {
- tt.Name.Local = strings.Replace(tt.Name.Local, "-", "_", -1)
- }
- var name string
- if len(tt.Name.Space) > 0 {
- name = tt.Name.Space + `:` + tt.Name.Local
- } else {
- name = tt.Name.Local
- }
- if skey != name {
- return nil, fmt.Errorf("element %s not properly terminated, got %s at #%d",
- skey, name, p.InputOffset())
- }
- }
- // len(n) > 0 if this is a simple element w/o xml.Attrs - see xml.CharData case.
- if len(n) == 0 {
- // If len(na)==0 we have an empty element == "";
- // it has no xml.Attr nor xml.CharData.
- // Empty element content will be map["etag"]map["#text"]""
- // after #seq injection - map["etag"]map["#seq"]seq - after return.
- if len(na) > 0 {
- n[skey] = na
- } else {
- n[skey] = "" // empty element
- }
- }
- return n, nil
- case xml.CharData:
- // clean up possible noise
- tt := strings.Trim(string(t.(xml.CharData)), trimRunes)
- if xmlEscapeCharsDecoder { // issue#84
- tt = escapeChars(tt)
- }
- if skey == "" {
- // per Adrian (http://www.adrianlungu.com/) catch stray text
- // in decoder stream -
- // https://github.com/clbanning/mxj/pull/14#issuecomment-182816374
- // NOTE: CharSetReader must be set to non-UTF-8 CharSet or you'll get
- // a p.Token() decoding error when the BOM is UTF-16 or UTF-32.
- continue
- }
- if len(tt) > 0 {
- // every simple element is a #text and has #seq associated with it
- na[textK] = cast(tt, r, "")
- na[seqK] = seq
- seq++
- }
- case xml.Comment:
- if n == nil { // no root 'key'
- n = map[string]interface{}{commentK: string(t.(xml.Comment))}
- return n, NoRoot
- }
- cm := make(map[string]interface{}, 2)
- cm[textK] = string(t.(xml.Comment))
- cm[seqK] = seq
- seq++
- na[commentK] = cm
- case xml.Directive:
- if n == nil { // no root 'key'
- n = map[string]interface{}{directiveK: string(t.(xml.Directive))}
- return n, NoRoot
- }
- dm := make(map[string]interface{}, 2)
- dm[textK] = string(t.(xml.Directive))
- dm[seqK] = seq
- seq++
- na[directiveK] = dm
- case xml.ProcInst:
- if n == nil {
- na = map[string]interface{}{targetK: t.(xml.ProcInst).Target, instK: string(t.(xml.ProcInst).Inst)}
- n = map[string]interface{}{procinstK: na}
- return n, NoRoot
- }
- pm := make(map[string]interface{}, 3)
- pm[targetK] = t.(xml.ProcInst).Target
- pm[instK] = string(t.(xml.ProcInst).Inst)
- pm[seqK] = seq
- seq++
- na[procinstK] = pm
- default:
- // noop - shouldn't ever get here, now, since we handle all token types
- }
- }
-}
-
-// ------------------ END: NewMapXml & NewMapXmlReader -------------------------
-
-// --------------------- mv.XmlSeq & mv.XmlSeqWriter -------------------------
-
-// Xml encodes a MapSeq as XML with elements sorted on #seq. The companion of NewMapXmlSeq().
-// The following rules apply.
-// - The "#seq" key value is used to seqence the subelements or attributes only.
-// - The "#attr" map key identifies the map of attribute map[string]interface{} values with "#text" key.
-// - The "#comment" map key identifies a comment in the value "#text" map entry - .
-// - The "#directive" map key identifies a directive in the value "#text" map entry - .
-// - The "#procinst" map key identifies a process instruction in the value "#target" and "#inst"
-// map entries - .
-// - Value type encoding:
-// > string, bool, float64, int, int32, int64, float32: per "%v" formating
-// > []bool, []uint8: by casting to string
-// > structures, etc.: handed to xml.Marshal() - if there is an error, the element
-// value is "UNKNOWN"
-// - Elements with only attribute values or are null are terminated using "/>" unless XmlGoEmptyElemSystax() called.
-// - If len(mv) == 1 and no rootTag is provided, then the map key is used as the root tag, possible.
-// Thus, `{ "key":"value" }` encodes as "value".
-func (mv MapSeq) Xml(rootTag ...string) ([]byte, error) {
- m := map[string]interface{}(mv)
- var err error
- s := new(string)
- p := new(pretty) // just a stub
-
- if len(m) == 1 && len(rootTag) == 0 {
- for key, value := range m {
- // if it's an array, see if all values are map[string]interface{}
- // we force a new root tag if we'll end up with no key:value in the list
- // so: key:[string_val, bool:true] --> string_valtrue
- switch value.(type) {
- case []interface{}:
- for _, v := range value.([]interface{}) {
- switch v.(type) {
- case map[string]interface{}: // noop
- default: // anything else
- err = mapToXmlSeqIndent(false, s, DefaultRootTag, m, p)
- goto done
- }
- }
- }
- err = mapToXmlSeqIndent(false, s, key, value, p)
- }
- } else if len(rootTag) == 1 {
- err = mapToXmlSeqIndent(false, s, rootTag[0], m, p)
- } else {
- err = mapToXmlSeqIndent(false, s, DefaultRootTag, m, p)
- }
-done:
- if xmlCheckIsValid {
- d := xml.NewDecoder(bytes.NewReader([]byte(*s)))
- for {
- _, err = d.Token()
- if err == io.EOF {
- err = nil
- break
- } else if err != nil {
- return nil, err
- }
- }
- }
- return []byte(*s), err
-}
-
-// The following implementation is provided only for symmetry with NewMapXmlReader[Raw]
-// The names will also provide a key for the number of return arguments.
-
-// XmlWriter Writes the MapSeq value as XML on the Writer.
-// See MapSeq.Xml() for encoding rules.
-func (mv MapSeq) XmlWriter(xmlWriter io.Writer, rootTag ...string) error {
- x, err := mv.Xml(rootTag...)
- if err != nil {
- return err
- }
-
- _, err = xmlWriter.Write(x)
- return err
-}
-
-// XmlWriteRaw writes the MapSeq value as XML on the Writer. []byte is the raw XML that was written.
-// See Map.XmlSeq() for encoding rules.
-/*
-func (mv MapSeq) XmlWriterRaw(xmlWriter io.Writer, rootTag ...string) ([]byte, error) {
- x, err := mv.Xml(rootTag...)
- if err != nil {
- return x, err
- }
-
- _, err = xmlWriter.Write(x)
- return x, err
-}
-*/
-
-// XmlIndentWriter writes the MapSeq value as pretty XML on the Writer.
-// See MapSeq.Xml() for encoding rules.
-func (mv MapSeq) XmlIndentWriter(xmlWriter io.Writer, prefix, indent string, rootTag ...string) error {
- x, err := mv.XmlIndent(prefix, indent, rootTag...)
- if err != nil {
- return err
- }
-
- _, err = xmlWriter.Write(x)
- return err
-}
-
-// XmlIndentWriterRaw writes the Map as pretty XML on the Writer. []byte is the raw XML that was written.
-// See Map.XmlSeq() for encoding rules.
-/*
-func (mv MapSeq) XmlIndentWriterRaw(xmlWriter io.Writer, prefix, indent string, rootTag ...string) ([]byte, error) {
- x, err := mv.XmlSeqIndent(prefix, indent, rootTag...)
- if err != nil {
- return x, err
- }
-
- _, err = xmlWriter.Write(x)
- return x, err
-}
-*/
-
-// -------------------- END: mv.Xml & mv.XmlWriter -------------------------------
-
-// ---------------------- XmlSeqIndent ----------------------------
-
-// XmlIndent encodes a map[string]interface{} as a pretty XML string.
-// See MapSeq.XmlSeq() for encoding rules.
-func (mv MapSeq) XmlIndent(prefix, indent string, rootTag ...string) ([]byte, error) {
- m := map[string]interface{}(mv)
-
- var err error
- s := new(string)
- p := new(pretty)
- p.indent = indent
- p.padding = prefix
-
- if len(m) == 1 && len(rootTag) == 0 {
- // this can extract the key for the single map element
- // use it if it isn't a key for a list
- for key, value := range m {
- if _, ok := value.([]interface{}); ok {
- err = mapToXmlSeqIndent(true, s, DefaultRootTag, m, p)
- } else {
- err = mapToXmlSeqIndent(true, s, key, value, p)
- }
- }
- } else if len(rootTag) == 1 {
- err = mapToXmlSeqIndent(true, s, rootTag[0], m, p)
- } else {
- err = mapToXmlSeqIndent(true, s, DefaultRootTag, m, p)
- }
- if xmlCheckIsValid {
- if _, err = NewMapXml([]byte(*s)); err != nil {
- return nil, err
- }
- d := xml.NewDecoder(bytes.NewReader([]byte(*s)))
- for {
- _, err = d.Token()
- if err == io.EOF {
- err = nil
- break
- } else if err != nil {
- return nil, err
- }
- }
- }
- return []byte(*s), err
-}
-
-// where the work actually happens
-// returns an error if an attribute is not atomic
-func mapToXmlSeqIndent(doIndent bool, s *string, key string, value interface{}, pp *pretty) error {
- var endTag bool
- var isSimple bool
- var noEndTag bool
- var elen int
- var ss string
- p := &pretty{pp.indent, pp.cnt, pp.padding, pp.mapDepth, pp.start}
-
- switch value.(type) {
- case map[string]interface{}, []byte, string, float64, bool, int, int32, int64, float32:
- if doIndent {
- *s += p.padding
- }
- if key != commentK && key != directiveK && key != procinstK {
- *s += `<` + key
- }
- }
- switch value.(type) {
- case map[string]interface{}:
- val := value.(map[string]interface{})
-
- if key == commentK {
- *s += ``
- noEndTag = true
- break
- }
-
- if key == directiveK {
- *s += ``
- noEndTag = true
- break
- }
-
- if key == procinstK {
- *s += `` + val[targetK].(string) + ` ` + val[instK].(string) + `?>`
- noEndTag = true
- break
- }
-
- haveAttrs := false
- // process attributes first
- if v, ok := val[attrK].(map[string]interface{}); ok {
- // First, unroll the map[string]interface{} into a []keyval array.
- // Then sequence it.
- kv := make([]keyval, len(v))
- n := 0
- for ak, av := range v {
- kv[n] = keyval{ak, av}
- n++
- }
- sort.Sort(elemListSeq(kv))
- // Now encode the attributes in original decoding sequence, using keyval array.
- for _, a := range kv {
- vv := a.v.(map[string]interface{})
- switch vv[textK].(type) {
- case string:
- if xmlEscapeChars {
- ss = escapeChars(vv[textK].(string))
- } else {
- ss = vv[textK].(string)
- }
- *s += ` ` + a.k + `="` + ss + `"`
- case float64, bool, int, int32, int64, float32:
- *s += ` ` + a.k + `="` + fmt.Sprintf("%v", vv[textK]) + `"`
- case []byte:
- if xmlEscapeChars {
- ss = escapeChars(string(vv[textK].([]byte)))
- } else {
- ss = string(vv[textK].([]byte))
- }
- *s += ` ` + a.k + `="` + ss + `"`
- default:
- return fmt.Errorf("invalid attribute value for: %s", a.k)
- }
- }
- haveAttrs = true
- }
-
- // simple element?
- // every map value has, at least, "#seq" and, perhaps, "#text" and/or "#attr"
- _, seqOK := val[seqK] // have key
- if v, ok := val[textK]; ok && ((len(val) == 3 && haveAttrs) || (len(val) == 2 && !haveAttrs)) && seqOK {
- if stmp, ok := v.(string); ok && stmp != "" {
- if xmlEscapeChars {
- stmp = escapeChars(stmp)
- }
- *s += ">" + stmp
- endTag = true
- elen = 1
- }
- isSimple = true
- break
- } else if !ok && ((len(val) == 2 && haveAttrs) || (len(val) == 1 && !haveAttrs)) && seqOK {
- // here no #text but have #seq or #seq+#attr
- endTag = false
- break
- }
-
- // we now need to sequence everything except attributes
- // 'kv' will hold everything that needs to be written
- kv := make([]keyval, 0)
- for k, v := range val {
- if k == attrK { // already processed
- continue
- }
- if k == seqK { // ignore - just for sorting
- continue
- }
- switch v.(type) {
- case []interface{}:
- // unwind the array as separate entries
- for _, vv := range v.([]interface{}) {
- kv = append(kv, keyval{k, vv})
- }
- default:
- kv = append(kv, keyval{k, v})
- }
- }
-
- // close tag with possible attributes
- *s += ">"
- if doIndent {
- *s += "\n"
- }
- // something more complex
- p.mapDepth++
- sort.Sort(elemListSeq(kv))
- i := 0
- for _, v := range kv {
- switch v.v.(type) {
- case []interface{}:
- default:
- if i == 0 && doIndent {
- p.Indent()
- }
- }
- i++
- if err := mapToXmlSeqIndent(doIndent, s, v.k, v.v, p); err != nil {
- return err
- }
- switch v.v.(type) {
- case []interface{}: // handled in []interface{} case
- default:
- if doIndent {
- p.Outdent()
- }
- }
- i--
- }
- p.mapDepth--
- endTag = true
- elen = 1 // we do have some content other than attrs
- case []interface{}:
- for _, v := range value.([]interface{}) {
- if doIndent {
- p.Indent()
- }
- if err := mapToXmlSeqIndent(doIndent, s, key, v, p); err != nil {
- return err
- }
- if doIndent {
- p.Outdent()
- }
- }
- return nil
- case nil:
- // terminate the tag
- if doIndent {
- *s += p.padding
- }
- *s += "<" + key
- endTag, isSimple = true, true
- break
- default: // handle anything - even goofy stuff
- elen = 0
- switch value.(type) {
- case string:
- if xmlEscapeChars {
- ss = escapeChars(value.(string))
- } else {
- ss = value.(string)
- }
- elen = len(ss)
- if elen > 0 {
- *s += ">" + ss
- }
- case float64, bool, int, int32, int64, float32:
- v := fmt.Sprintf("%v", value)
- elen = len(v)
- if elen > 0 {
- *s += ">" + v
- }
- case []byte: // NOTE: byte is just an alias for uint8
- // similar to how xml.Marshal handles []byte structure members
- if xmlEscapeChars {
- ss = escapeChars(string(value.([]byte)))
- } else {
- ss = string(value.([]byte))
- }
- elen = len(ss)
- if elen > 0 {
- *s += ">" + ss
- }
- default:
- var v []byte
- var err error
- if doIndent {
- v, err = xml.MarshalIndent(value, p.padding, p.indent)
- } else {
- v, err = xml.Marshal(value)
- }
- if err != nil {
- *s += ">UNKNOWN"
- } else {
- elen = len(v)
- if elen > 0 {
- *s += string(v)
- }
- }
- }
- isSimple = true
- endTag = true
- }
- if endTag && !noEndTag {
- if doIndent {
- if !isSimple {
- *s += p.padding
- }
- }
- switch value.(type) {
- case map[string]interface{}, []byte, string, float64, bool, int, int32, int64, float32:
- if elen > 0 || useGoXmlEmptyElemSyntax {
- if elen == 0 {
- *s += ">"
- }
- *s += `` + key + ">"
- } else {
- *s += `/>`
- }
- }
- } else if !noEndTag {
- if useGoXmlEmptyElemSyntax {
- *s += `` + key + ">"
- // *s += ">" + key + ">"
- } else {
- *s += "/>"
- }
- }
- if doIndent {
- if p.cnt > p.start {
- *s += "\n"
- }
- p.Outdent()
- }
-
- return nil
-}
-
-// the element sort implementation
-
-type keyval struct {
- k string
- v interface{}
-}
-type elemListSeq []keyval
-
-func (e elemListSeq) Len() int {
- return len(e)
-}
-
-func (e elemListSeq) Swap(i, j int) {
- e[i], e[j] = e[j], e[i]
-}
-
-func (e elemListSeq) Less(i, j int) bool {
- var iseq, jseq int
- var fiseq, fjseq float64
- var ok bool
- if iseq, ok = e[i].v.(map[string]interface{})[seqK].(int); !ok {
- if fiseq, ok = e[i].v.(map[string]interface{})[seqK].(float64); ok {
- iseq = int(fiseq)
- } else {
- iseq = 9999999
- }
- }
-
- if jseq, ok = e[j].v.(map[string]interface{})[seqK].(int); !ok {
- if fjseq, ok = e[j].v.(map[string]interface{})[seqK].(float64); ok {
- jseq = int(fjseq)
- } else {
- jseq = 9999999
- }
- }
-
- return iseq <= jseq
-}
-
-// =============== https://groups.google.com/forum/#!topic/golang-nuts/lHPOHD-8qio
-
-// BeautifyXml (re)formats an XML doc similar to Map.XmlIndent().
-// It preserves comments, directives and process instructions,
-func BeautifyXml(b []byte, prefix, indent string) ([]byte, error) {
- x, err := NewMapXmlSeq(b)
- if err != nil {
- return nil, err
- }
- return x.XmlIndent(prefix, indent)
-}
diff --git a/vendor/github.com/clbanning/mxj/v2/xmlseq2.go b/vendor/github.com/clbanning/mxj/v2/xmlseq2.go
deleted file mode 100644
index 467fd07..0000000
--- a/vendor/github.com/clbanning/mxj/v2/xmlseq2.go
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2012-2016, 2019 Charles Banning. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file
-
-package mxj
-
-// ---------------- expose Map methods to MapSeq type ---------------------------
-
-// Pretty print a Map.
-func (msv MapSeq) StringIndent(offset ...int) string {
- return writeMap(map[string]interface{}(msv), true, true, offset...)
-}
-
-// Pretty print a Map without the value type information - just key:value entries.
-func (msv MapSeq) StringIndentNoTypeInfo(offset ...int) string {
- return writeMap(map[string]interface{}(msv), false, true, offset...)
-}
-
diff --git a/vendor/github.com/codegangsta/negroni/.gitignore b/vendor/github.com/codegangsta/negroni/.gitignore
new file mode 100644
index 0000000..3f2bc47
--- /dev/null
+++ b/vendor/github.com/codegangsta/negroni/.gitignore
@@ -0,0 +1 @@
+/coverage.txt
diff --git a/vendor/github.com/codegangsta/negroni/.travis.yml b/vendor/github.com/codegangsta/negroni/.travis.yml
new file mode 100644
index 0000000..1810433
--- /dev/null
+++ b/vendor/github.com/codegangsta/negroni/.travis.yml
@@ -0,0 +1,27 @@
+language: go
+
+sudo: false
+dist: trusty
+
+go:
+- 1.x
+- 1.2.x
+- 1.3.x
+- 1.4.x
+- 1.5.x
+- 1.6.x
+- 1.7.x
+- 1.8.x
+- master
+
+before_install:
+- find "${GOPATH%%:*}" -name '*.a' -delete
+- rm -rf "${GOPATH%%:*}/src/golang.org"
+- go get golang.org/x/tools/cover
+- go get golang.org/x/tools/cmd/cover
+
+script:
+- go test -race -coverprofile=coverage.txt -covermode=atomic
+
+after_success:
+- bash <(curl -s "https://codecov.io/bash")
diff --git a/vendor/github.com/codegangsta/negroni/CHANGELOG.md b/vendor/github.com/codegangsta/negroni/CHANGELOG.md
new file mode 100644
index 0000000..1b757d0
--- /dev/null
+++ b/vendor/github.com/codegangsta/negroni/CHANGELOG.md
@@ -0,0 +1,37 @@
+# Change Log
+
+**ATTN**: This project uses [semantic versioning](http://semver.org/).
+
+## [Unreleased]
+### Added
+- `Recovery.ErrorHandlerFunc` for custom error handling during recovery
+- `With()` helper for building a new `Negroni` struct chaining handlers from
+ existing `Negroni` structs
+
+### Fixed
+- `Written()` correct returns `false` if no response header has been written
+
+### Changed
+- Set default status to `0` in the case that no handler writes status -- was
+ previously `200` (in 0.2.0, before that it was `0` so this reestablishes that
+ behavior)
+- Catch `panic`s thrown by callbacks provided to the `Recovery` handler
+
+## [0.2.0] - 2016-05-10
+### Added
+- Support for variadic handlers in `New()`
+- Added `Negroni.Handlers()` to fetch all of the handlers for a given chain
+- Allowed size in `Recovery` handler was bumped to 8k
+- `Negroni.UseFunc` to push another handler onto the chain
+
+### Changed
+- Set the status before calling `beforeFuncs` so the information is available to them
+- Set default status to `200` in the case that no handler writes status -- was previously `0`
+- Panic if `nil` handler is given to `negroni.Use`
+
+## 0.1.0 - 2013-07-22
+### Added
+- Initial implementation.
+
+[Unreleased]: https://github.com/urfave/negroni/compare/v0.2.0...HEAD
+[0.2.0]: https://github.com/urfave/negroni/compare/v0.1.0...v0.2.0
diff --git a/vendor/github.com/Microsoft/go-winio/LICENSE b/vendor/github.com/codegangsta/negroni/LICENSE
similarity index 97%
rename from vendor/github.com/Microsoft/go-winio/LICENSE
rename to vendor/github.com/codegangsta/negroni/LICENSE
index b8b569d..08b5e20 100644
--- a/vendor/github.com/Microsoft/go-winio/LICENSE
+++ b/vendor/github.com/codegangsta/negroni/LICENSE
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (c) 2015 Microsoft
+Copyright (c) 2014 Jeremy Saenz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -19,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-
diff --git a/vendor/github.com/codegangsta/negroni/README.md b/vendor/github.com/codegangsta/negroni/README.md
new file mode 100644
index 0000000..997acbd
--- /dev/null
+++ b/vendor/github.com/codegangsta/negroni/README.md
@@ -0,0 +1,546 @@
+# Negroni
+[](http://godoc.org/github.com/urfave/negroni)
+[](https://travis-ci.org/urfave/negroni)
+[](https://codebeat.co/projects/github-com-urfave-negroni)
+[](https://codecov.io/gh/urfave/negroni)
+
+**Notice:** This is the library formerly known as
+`github.com/codegangsta/negroni` -- Github will automatically redirect requests
+to this repository, but we recommend updating your references for clarity.
+
+Negroni is an idiomatic approach to web middleware in Go. It is tiny,
+non-intrusive, and encourages use of `net/http` Handlers.
+
+If you like the idea of [Martini](https://github.com/go-martini/martini), but
+you think it contains too much magic, then Negroni is a great fit.
+
+Language Translations:
+* [Deutsch (de_DE)](translations/README_de_de.md)
+* [Português Brasileiro (pt_BR)](translations/README_pt_br.md)
+* [简体中文 (zh_cn)](translations/README_zh_cn.md)
+* [繁體中文 (zh_tw)](translations/README_zh_tw.md)
+* [日本語 (ja_JP)](translations/README_ja_JP.md)
+* [Français (fr_FR)](translations/README_fr_FR.md)
+
+## Getting Started
+
+After installing Go and setting up your
+[GOPATH](http://golang.org/doc/code.html#GOPATH), create your first `.go` file.
+We'll call it `server.go`.
+
+
+``` go
+package main
+
+import (
+ "fmt"
+ "net/http"
+
+ "github.com/urfave/negroni"
+)
+
+func main() {
+ mux := http.NewServeMux()
+ mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
+ fmt.Fprintf(w, "Welcome to the home page!")
+ })
+
+ n := negroni.Classic() // Includes some default middlewares
+ n.UseHandler(mux)
+
+ http.ListenAndServe(":3000", n)
+}
+```
+
+Then install the Negroni package (**NOTE**: >= **go 1.1** is required):
+
+```
+go get github.com/urfave/negroni
+```
+
+Then run your server:
+
+```
+go run server.go
+```
+
+You will now have a Go `net/http` webserver running on `localhost:3000`.
+
+### Packaging
+
+If you are on Debian, `negroni` is also available as [a
+package](https://packages.debian.org/sid/golang-github-urfave-negroni-dev) that
+you can install via `apt install golang-github-urfave-negroni-dev` (at the time
+of writing, it is in the `sid` repositories).
+
+## Is Negroni a Framework?
+
+Negroni is **not** a framework. It is a middleware-focused library that is
+designed to work directly with `net/http`.
+
+## Routing?
+
+Negroni is BYOR (Bring your own Router). The Go community already has a number
+of great http routers available, and Negroni tries to play well with all of them
+by fully supporting `net/http`. For instance, integrating with [Gorilla Mux]
+looks like so:
+
+``` go
+router := mux.NewRouter()
+router.HandleFunc("/", HomeHandler)
+
+n := negroni.New(Middleware1, Middleware2)
+// Or use a middleware with the Use() function
+n.Use(Middleware3)
+// router goes last
+n.UseHandler(router)
+
+http.ListenAndServe(":3001", n)
+```
+
+## `negroni.Classic()`
+
+`negroni.Classic()` provides some default middleware that is useful for most
+applications:
+
+* [`negroni.Recovery`](#recovery) - Panic Recovery Middleware.
+* [`negroni.Logger`](#logger) - Request/Response Logger Middleware.
+* [`negroni.Static`](#static) - Static File serving under the "public"
+ directory.
+
+This makes it really easy to get started with some useful features from Negroni.
+
+## Handlers
+
+Negroni provides a bidirectional middleware flow. This is done through the
+`negroni.Handler` interface:
+
+``` go
+type Handler interface {
+ ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
+}
+```
+
+If a middleware hasn't already written to the `ResponseWriter`, it should call
+the next `http.HandlerFunc` in the chain to yield to the next middleware
+handler. This can be used for great good:
+
+``` go
+func MyMiddleware(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
+ // do some stuff before
+ next(rw, r)
+ // do some stuff after
+}
+```
+
+And you can map it to the handler chain with the `Use` function:
+
+``` go
+n := negroni.New()
+n.Use(negroni.HandlerFunc(MyMiddleware))
+```
+
+You can also map plain old `http.Handler`s:
+
+``` go
+n := negroni.New()
+
+mux := http.NewServeMux()
+// map your routes
+
+n.UseHandler(mux)
+
+http.ListenAndServe(":3000", n)
+```
+
+## `With()`
+
+Negroni has a convenience function called `With`. `With` takes one or more
+`Handler` instances and returns a new `Negroni` with the combination of the
+receiver's handlers and the new handlers.
+
+```go
+// middleware we want to reuse
+common := negroni.New()
+common.Use(MyMiddleware1)
+common.Use(MyMiddleware2)
+
+// `specific` is a new negroni with the handlers from `common` combined with the
+// the handlers passed in
+specific := common.With(
+ SpecificMiddleware1,
+ SpecificMiddleware2
+)
+```
+
+## `Run()`
+
+Negroni has a convenience function called `Run`. `Run` takes an addr string
+identical to [`http.ListenAndServe`](https://godoc.org/net/http#ListenAndServe).
+
+
+``` go
+package main
+
+import (
+ "github.com/urfave/negroni"
+)
+
+func main() {
+ n := negroni.Classic()
+ n.Run(":8080")
+}
+```
+If no address is provided, the `PORT` environment variable is used instead.
+If the `PORT` environment variable is not defined, the default address will be used.
+See [Run](https://godoc.org/github.com/urfave/negroni#Negroni.Run) for a complete description.
+
+In general, you will want to use `net/http` methods and pass `negroni` as a
+`Handler`, as this is more flexible, e.g.:
+
+
+``` go
+package main
+
+import (
+ "fmt"
+ "log"
+ "net/http"
+ "time"
+
+ "github.com/urfave/negroni"
+)
+
+func main() {
+ mux := http.NewServeMux()
+ mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
+ fmt.Fprintf(w, "Welcome to the home page!")
+ })
+
+ n := negroni.Classic() // Includes some default middlewares
+ n.UseHandler(mux)
+
+ s := &http.Server{
+ Addr: ":8080",
+ Handler: n,
+ ReadTimeout: 10 * time.Second,
+ WriteTimeout: 10 * time.Second,
+ MaxHeaderBytes: 1 << 20,
+ }
+ log.Fatal(s.ListenAndServe())
+}
+```
+
+## Route Specific Middleware
+
+If you have a route group of routes that need specific middleware to be
+executed, you can simply create a new Negroni instance and use it as your route
+handler.
+
+``` go
+router := mux.NewRouter()
+adminRoutes := mux.NewRouter()
+// add admin routes here
+
+// Create a new negroni for the admin middleware
+router.PathPrefix("/admin").Handler(negroni.New(
+ Middleware1,
+ Middleware2,
+ negroni.Wrap(adminRoutes),
+))
+```
+
+If you are using [Gorilla Mux], here is an example using a subrouter:
+
+``` go
+router := mux.NewRouter()
+subRouter := mux.NewRouter().PathPrefix("/subpath").Subrouter().StrictSlash(true)
+subRouter.HandleFunc("/", someSubpathHandler) // "/subpath/"
+subRouter.HandleFunc("/:id", someSubpathHandler) // "/subpath/:id"
+
+// "/subpath" is necessary to ensure the subRouter and main router linkup
+router.PathPrefix("/subpath").Handler(negroni.New(
+ Middleware1,
+ Middleware2,
+ negroni.Wrap(subRouter),
+))
+```
+
+`With()` can be used to eliminate redundancy for middlewares shared across
+routes.
+
+``` go
+router := mux.NewRouter()
+apiRoutes := mux.NewRouter()
+// add api routes here
+webRoutes := mux.NewRouter()
+// add web routes here
+
+// create common middleware to be shared across routes
+common := negroni.New(
+ Middleware1,
+ Middleware2,
+)
+
+// create a new negroni for the api middleware
+// using the common middleware as a base
+router.PathPrefix("/api").Handler(common.With(
+ APIMiddleware1,
+ negroni.Wrap(apiRoutes),
+))
+// create a new negroni for the web middleware
+// using the common middleware as a base
+router.PathPrefix("/web").Handler(common.With(
+ WebMiddleware1,
+ negroni.Wrap(webRoutes),
+))
+```
+
+## Bundled Middleware
+
+### Static
+
+This middleware will serve files on the filesystem. If the files do not exist,
+it proxies the request to the next middleware. If you want the requests for
+non-existent files to return a `404 File Not Found` to the user you should look
+at using [http.FileServer](https://golang.org/pkg/net/http/#FileServer) as
+a handler.
+
+Example:
+
+
+``` go
+package main
+
+import (
+ "fmt"
+ "net/http"
+
+ "github.com/urfave/negroni"
+)
+
+func main() {
+ mux := http.NewServeMux()
+ mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
+ fmt.Fprintf(w, "Welcome to the home page!")
+ })
+
+ // Example of using a http.FileServer if you want "server-like" rather than "middleware" behavior
+ // mux.Handle("/public", http.FileServer(http.Dir("/home/public")))
+
+ n := negroni.New()
+ n.Use(negroni.NewStatic(http.Dir("/tmp")))
+ n.UseHandler(mux)
+
+ http.ListenAndServe(":3002", n)
+}
+```
+
+Will serve files from the `/tmp` directory first, but proxy calls to the next
+handler if the request does not match a file on the filesystem.
+
+### Recovery
+
+This middleware catches `panic`s and responds with a `500` response code. If
+any other middleware has written a response code or body, this middleware will
+fail to properly send a 500 to the client, as the client has already received
+the HTTP response code. Additionally, an `ErrorHandlerFunc` can be attached
+to report 500's to an error reporting service such as Sentry or Airbrake.
+
+Example:
+
+
+``` go
+package main
+
+import (
+ "net/http"
+
+ "github.com/urfave/negroni"
+)
+
+func main() {
+ mux := http.NewServeMux()
+ mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
+ panic("oh no")
+ })
+
+ n := negroni.New()
+ n.Use(negroni.NewRecovery())
+ n.UseHandler(mux)
+
+ http.ListenAndServe(":3003", n)
+}
+```
+
+Will return a `500 Internal Server Error` to each request. It will also log the
+stack traces as well as print the stack trace to the requester if `PrintStack`
+is set to `true` (the default).
+
+Example with error handler:
+
+``` go
+package main
+
+import (
+ "net/http"
+
+ "github.com/urfave/negroni"
+)
+
+func main() {
+ mux := http.NewServeMux()
+ mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
+ panic("oh no")
+ })
+
+ n := negroni.New()
+ recovery := negroni.NewRecovery()
+ recovery.ErrorHandlerFunc = reportToSentry
+ n.Use(recovery)
+ n.UseHandler(mux)
+
+ http.ListenAndServe(":3003", n)
+}
+
+func reportToSentry(error interface{}) {
+ // write code here to report error to Sentry
+}
+```
+
+The middleware simply output the informations on STDOUT by default.
+You can customize the output process by using the `SetFormatter()` function.
+
+You can use also the `HTMLPanicFormatter` to display a pretty HTML when a crash occurs.
+
+
+``` go
+package main
+
+import (
+ "net/http"
+
+ "github.com/urfave/negroni"
+)
+
+func main() {
+ mux := http.NewServeMux()
+ mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
+ panic("oh no")
+ })
+
+ n := negroni.New()
+ recovery := negroni.NewRecovery()
+ recovery.Formatter = &negroni.HTMLPanicFormatter{}
+ n.Use(recovery)
+ n.UseHandler(mux)
+
+ http.ListenAndServe(":3003", n)
+}
+```
+
+## Logger
+
+This middleware logs each incoming request and response.
+
+Example:
+
+
+``` go
+package main
+
+import (
+ "fmt"
+ "net/http"
+
+ "github.com/urfave/negroni"
+)
+
+func main() {
+ mux := http.NewServeMux()
+ mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
+ fmt.Fprintf(w, "Welcome to the home page!")
+ })
+
+ n := negroni.New()
+ n.Use(negroni.NewLogger())
+ n.UseHandler(mux)
+
+ http.ListenAndServe(":3004", n)
+}
+```
+
+Will print a log similar to:
+
+```
+[negroni] 2017-10-04T14:56:25+02:00 | 200 | 378µs | localhost:3004 | GET /
+```
+
+on each request.
+
+You can also set your own log format by calling the `SetFormat` function. The format is a template string with fields as mentioned in the `LoggerEntry` struct. So, as an example -
+
+```go
+l.SetFormat("[{{.Status}} {{.Duration}}] - {{.Request.UserAgent}}")
+```
+
+will show something like - `[200 18.263µs] - Go-User-Agent/1.1 `
+
+## Third Party Middleware
+
+Here is a current list of Negroni compatible middlware. Feel free to put up a PR
+linking your middleware if you have built one:
+
+| Middleware | Author | Description |
+| -----------|--------|-------------|
+| [authz](https://github.com/casbin/negroni-authz) | [Yang Luo](https://github.com/hsluoyz) | ACL, RBAC, ABAC Authorization middlware based on [Casbin](https://github.com/casbin/casbin) |
+| [binding](https://github.com/mholt/binding) | [Matt Holt](https://github.com/mholt) | Data binding from HTTP requests into structs |
+| [cloudwatch](https://github.com/cvillecsteele/negroni-cloudwatch) | [Colin Steele](https://github.com/cvillecsteele) | AWS cloudwatch metrics middleware |
+| [cors](https://github.com/rs/cors) | [Olivier Poitrey](https://github.com/rs) | [Cross Origin Resource Sharing](http://www.w3.org/TR/cors/) (CORS) support |
+| [csp](https://github.com/awakenetworks/csp) | [Awake Networks](https://github.com/awakenetworks) | [Content Security Policy](https://www.w3.org/TR/CSP2/) (CSP) support |
+| [delay](https://github.com/jeffbmartinez/delay) | [Jeff Martinez](https://github.com/jeffbmartinez) | Add delays/latency to endpoints. Useful when testing effects of high latency |
+| [New Relic Go Agent](https://github.com/yadvendar/negroni-newrelic-go-agent) | [Yadvendar Champawat](https://github.com/yadvendar) | Official [New Relic Go Agent](https://github.com/newrelic/go-agent) (currently in beta) |
+| [gorelic](https://github.com/jingweno/negroni-gorelic) | [Jingwen Owen Ou](https://github.com/jingweno) | New Relic agent for Go runtime |
+| [Graceful](https://github.com/tylerb/graceful) | [Tyler Bunnell](https://github.com/tylerb) | Graceful HTTP Shutdown |
+| [gzip](https://github.com/phyber/negroni-gzip) | [phyber](https://github.com/phyber) | GZIP response compression |
+| [JWT Middleware](https://github.com/auth0/go-jwt-middleware) | [Auth0](https://github.com/auth0) | Middleware checks for a JWT on the `Authorization` header on incoming requests and decodes it|
+| [logrus](https://github.com/meatballhat/negroni-logrus) | [Dan Buch](https://github.com/meatballhat) | Logrus-based logger |
+| [oauth2](https://github.com/goincremental/negroni-oauth2) | [David Bochenski](https://github.com/bochenski) | oAuth2 middleware |
+| [onthefly](https://github.com/xyproto/onthefly) | [Alexander Rødseth](https://github.com/xyproto) | Generate TinySVG, HTML and CSS on the fly |
+| [permissions2](https://github.com/xyproto/permissions2) | [Alexander Rødseth](https://github.com/xyproto) | Cookies, users and permissions |
+| [prometheus](https://github.com/zbindenren/negroni-prometheus) | [Rene Zbinden](https://github.com/zbindenren) | Easily create metrics endpoint for the [prometheus](http://prometheus.io) instrumentation tool |
+| [render](https://github.com/unrolled/render) | [Cory Jacobsen](https://github.com/unrolled) | Render JSON, XML and HTML templates |
+| [RestGate](https://github.com/pjebs/restgate) | [Prasanga Siripala](https://github.com/pjebs) | Secure authentication for REST API endpoints |
+| [secure](https://github.com/unrolled/secure) | [Cory Jacobsen](https://github.com/unrolled) | Middleware that implements a few quick security wins |
+| [sessions](https://github.com/goincremental/negroni-sessions) | [David Bochenski](https://github.com/bochenski) | Session Management |
+| [stats](https://github.com/thoas/stats) | [Florent Messa](https://github.com/thoas) | Store information about your web application (response time, etc.) |
+| [VanGoH](https://github.com/auroratechnologies/vangoh) | [Taylor Wrobel](https://github.com/twrobel3) | Configurable [AWS-Style](http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html) HMAC authentication middleware |
+| [xrequestid](https://github.com/pilu/xrequestid) | [Andrea Franz](https://github.com/pilu) | Middleware that assigns a random X-Request-Id header to each request |
+| [mgo session](https://github.com/joeljames/nigroni-mgo-session) | [Joel James](https://github.com/joeljames) | Middleware that handles creating and closing mgo sessions per request |
+| [digits](https://github.com/bamarni/digits) | [Bilal Amarni](https://github.com/bamarni) | Middleware that handles [Twitter Digits](https://get.digits.com/) authentication |
+
+## Examples
+
+[Alexander Rødseth](https://github.com/xyproto) created
+[mooseware](https://github.com/xyproto/mooseware), a skeleton for writing a
+Negroni middleware handler.
+
+[Prasanga Siripala](https://github.com/pjebs) created an effective skeleton structure for web-based Go/Negroni projects: [Go-Skeleton](https://github.com/pjebs/go-skeleton)
+
+## Live code reload?
+
+[gin](https://github.com/codegangsta/gin) and
+[fresh](https://github.com/pilu/fresh) both live reload negroni apps.
+
+## Essential Reading for Beginners of Go & Negroni
+
+* [Using a Context to pass information from middleware to end handler](http://elithrar.github.io/article/map-string-interface/)
+* [Understanding middleware](https://mattstauffer.co/blog/laravel-5.0-middleware-filter-style)
+
+## About
+
+Negroni is obsessively designed by none other than the [Code
+Gangsta](https://codegangsta.io/)
+
+[Gorilla Mux]: https://github.com/gorilla/mux
+[`http.FileSystem`]: https://godoc.org/net/http#FileSystem
diff --git a/vendor/github.com/codegangsta/negroni/doc.go b/vendor/github.com/codegangsta/negroni/doc.go
new file mode 100644
index 0000000..add1ed9
--- /dev/null
+++ b/vendor/github.com/codegangsta/negroni/doc.go
@@ -0,0 +1,25 @@
+// Package negroni is an idiomatic approach to web middleware in Go. It is tiny, non-intrusive, and encourages use of net/http Handlers.
+//
+// If you like the idea of Martini, but you think it contains too much magic, then Negroni is a great fit.
+//
+// For a full guide visit http://github.com/urfave/negroni
+//
+// package main
+//
+// import (
+// "github.com/urfave/negroni"
+// "net/http"
+// "fmt"
+// )
+//
+// func main() {
+// mux := http.NewServeMux()
+// mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
+// fmt.Fprintf(w, "Welcome to the home page!")
+// })
+//
+// n := negroni.Classic()
+// n.UseHandler(mux)
+// n.Run(":3000")
+// }
+package negroni
diff --git a/vendor/github.com/codegangsta/negroni/logger.go b/vendor/github.com/codegangsta/negroni/logger.go
new file mode 100644
index 0000000..5bf7731
--- /dev/null
+++ b/vendor/github.com/codegangsta/negroni/logger.go
@@ -0,0 +1,82 @@
+package negroni
+
+import (
+ "bytes"
+
+ "log"
+ "net/http"
+ "os"
+ "text/template"
+ "time"
+)
+
+// LoggerEntry is the structure
+// passed to the template.
+type LoggerEntry struct {
+ StartTime string
+ Status int
+ Duration time.Duration
+ Hostname string
+ Method string
+ Path string
+ Request *http.Request
+}
+
+// LoggerDefaultFormat is the format
+// logged used by the default Logger instance.
+var LoggerDefaultFormat = "{{.StartTime}} | {{.Status}} | \t {{.Duration}} | {{.Hostname}} | {{.Method}} {{.Path}} \n"
+
+// LoggerDefaultDateFormat is the
+// format used for date by the
+// default Logger instance.
+var LoggerDefaultDateFormat = time.RFC3339
+
+// ALogger interface
+type ALogger interface {
+ Println(v ...interface{})
+ Printf(format string, v ...interface{})
+}
+
+// Logger is a middleware handler that logs the request as it goes in and the response as it goes out.
+type Logger struct {
+ // ALogger implements just enough log.Logger interface to be compatible with other implementations
+ ALogger
+ dateFormat string
+ template *template.Template
+}
+
+// NewLogger returns a new Logger instance
+func NewLogger() *Logger {
+ logger := &Logger{ALogger: log.New(os.Stdout, "[negroni] ", 0), dateFormat: LoggerDefaultDateFormat}
+ logger.SetFormat(LoggerDefaultFormat)
+ return logger
+}
+
+func (l *Logger) SetFormat(format string) {
+ l.template = template.Must(template.New("negroni_parser").Parse(format))
+}
+
+func (l *Logger) SetDateFormat(format string) {
+ l.dateFormat = format
+}
+
+func (l *Logger) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
+ start := time.Now()
+
+ next(rw, r)
+
+ res := rw.(ResponseWriter)
+ log := LoggerEntry{
+ StartTime: start.Format(l.dateFormat),
+ Status: res.Status(),
+ Duration: time.Since(start),
+ Hostname: r.Host,
+ Method: r.Method,
+ Path: r.URL.Path,
+ Request: r,
+ }
+
+ buff := &bytes.Buffer{}
+ l.template.Execute(buff, log)
+ l.Printf(buff.String())
+}
diff --git a/vendor/github.com/codegangsta/negroni/negroni.go b/vendor/github.com/codegangsta/negroni/negroni.go
new file mode 100644
index 0000000..d1d7782
--- /dev/null
+++ b/vendor/github.com/codegangsta/negroni/negroni.go
@@ -0,0 +1,169 @@
+package negroni
+
+import (
+ "log"
+ "net/http"
+ "os"
+)
+
+const (
+ // DefaultAddress is used if no other is specified.
+ DefaultAddress = ":8080"
+)
+
+// Handler handler is an interface that objects can implement to be registered to serve as middleware
+// in the Negroni middleware stack.
+// ServeHTTP should yield to the next middleware in the chain by invoking the next http.HandlerFunc
+// passed in.
+//
+// If the Handler writes to the ResponseWriter, the next http.HandlerFunc should not be invoked.
+type Handler interface {
+ ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
+}
+
+// HandlerFunc is an adapter to allow the use of ordinary functions as Negroni handlers.
+// If f is a function with the appropriate signature, HandlerFunc(f) is a Handler object that calls f.
+type HandlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
+
+func (h HandlerFunc) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
+ h(rw, r, next)
+}
+
+type middleware struct {
+ handler Handler
+ next *middleware
+}
+
+func (m middleware) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
+ m.handler.ServeHTTP(rw, r, m.next.ServeHTTP)
+}
+
+// Wrap converts a http.Handler into a negroni.Handler so it can be used as a Negroni
+// middleware. The next http.HandlerFunc is automatically called after the Handler
+// is executed.
+func Wrap(handler http.Handler) Handler {
+ return HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
+ handler.ServeHTTP(rw, r)
+ next(rw, r)
+ })
+}
+
+// WrapFunc converts a http.HandlerFunc into a negroni.Handler so it can be used as a Negroni
+// middleware. The next http.HandlerFunc is automatically called after the Handler
+// is executed.
+func WrapFunc(handlerFunc http.HandlerFunc) Handler {
+ return HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
+ handlerFunc(rw, r)
+ next(rw, r)
+ })
+}
+
+// Negroni is a stack of Middleware Handlers that can be invoked as an http.Handler.
+// Negroni middleware is evaluated in the order that they are added to the stack using
+// the Use and UseHandler methods.
+type Negroni struct {
+ middleware middleware
+ handlers []Handler
+}
+
+// New returns a new Negroni instance with no middleware preconfigured.
+func New(handlers ...Handler) *Negroni {
+ return &Negroni{
+ handlers: handlers,
+ middleware: build(handlers),
+ }
+}
+
+// With returns a new Negroni instance that is a combination of the negroni
+// receiver's handlers and the provided handlers.
+func (n *Negroni) With(handlers ...Handler) *Negroni {
+ return New(
+ append(n.handlers, handlers...)...,
+ )
+}
+
+// Classic returns a new Negroni instance with the default middleware already
+// in the stack.
+//
+// Recovery - Panic Recovery Middleware
+// Logger - Request/Response Logging
+// Static - Static File Serving
+func Classic() *Negroni {
+ return New(NewRecovery(), NewLogger(), NewStatic(http.Dir("public")))
+}
+
+func (n *Negroni) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
+ n.middleware.ServeHTTP(NewResponseWriter(rw), r)
+}
+
+// Use adds a Handler onto the middleware stack. Handlers are invoked in the order they are added to a Negroni.
+func (n *Negroni) Use(handler Handler) {
+ if handler == nil {
+ panic("handler cannot be nil")
+ }
+
+ n.handlers = append(n.handlers, handler)
+ n.middleware = build(n.handlers)
+}
+
+// UseFunc adds a Negroni-style handler function onto the middleware stack.
+func (n *Negroni) UseFunc(handlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)) {
+ n.Use(HandlerFunc(handlerFunc))
+}
+
+// UseHandler adds a http.Handler onto the middleware stack. Handlers are invoked in the order they are added to a Negroni.
+func (n *Negroni) UseHandler(handler http.Handler) {
+ n.Use(Wrap(handler))
+}
+
+// UseHandlerFunc adds a http.HandlerFunc-style handler function onto the middleware stack.
+func (n *Negroni) UseHandlerFunc(handlerFunc func(rw http.ResponseWriter, r *http.Request)) {
+ n.UseHandler(http.HandlerFunc(handlerFunc))
+}
+
+// Run is a convenience function that runs the negroni stack as an HTTP
+// server. The addr string, if provided, takes the same format as http.ListenAndServe.
+// If no address is provided but the PORT environment variable is set, the PORT value is used.
+// If neither is provided, the address' value will equal the DefaultAddress constant.
+func (n *Negroni) Run(addr ...string) {
+ l := log.New(os.Stdout, "[negroni] ", 0)
+ finalAddr := detectAddress(addr...)
+ l.Printf("listening on %s", finalAddr)
+ l.Fatal(http.ListenAndServe(finalAddr, n))
+}
+
+func detectAddress(addr ...string) string {
+ if len(addr) > 0 {
+ return addr[0]
+ }
+ if port := os.Getenv("PORT"); port != "" {
+ return ":" + port
+ }
+ return DefaultAddress
+}
+
+// Returns a list of all the handlers in the current Negroni middleware chain.
+func (n *Negroni) Handlers() []Handler {
+ return n.handlers
+}
+
+func build(handlers []Handler) middleware {
+ var next middleware
+
+ if len(handlers) == 0 {
+ return voidMiddleware()
+ } else if len(handlers) > 1 {
+ next = build(handlers[1:])
+ } else {
+ next = voidMiddleware()
+ }
+
+ return middleware{handlers[0], &next}
+}
+
+func voidMiddleware() middleware {
+ return middleware{
+ HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {}),
+ &middleware{},
+ }
+}
diff --git a/vendor/github.com/codegangsta/negroni/recovery.go b/vendor/github.com/codegangsta/negroni/recovery.go
new file mode 100644
index 0000000..1a6570e
--- /dev/null
+++ b/vendor/github.com/codegangsta/negroni/recovery.go
@@ -0,0 +1,179 @@
+package negroni
+
+import (
+ "fmt"
+ "log"
+ "net/http"
+ "os"
+ "runtime"
+ "runtime/debug"
+ "text/template"
+)
+
+const (
+ panicText = "PANIC: %s\n%s"
+ panicHTML = `
+PANIC: {{.RecoveredPanic}}
+
+
+