From bdcc5134b101498c84578c73e288e1922565392c Mon Sep 17 00:00:00 2001 From: ychao_1983 Date: Thu, 26 May 2022 17:08:44 +0800 Subject: [PATCH 01/45] phone verify --- go.mod | 11 +- go.sum | 50 + modules/phone/phone.go | 62 + modules/setting/phone.go | 44 + modules/setting/setting.go | 1 + .../alibabacloud-gateway-spi/LICENSE | 201 + .../alibabacloud-gateway-spi/client/client.go | 305 ++ .../alibabacloud-go/darabonba-openapi/LICENSE | 201 + .../darabonba-openapi/client/client.go | 1623 +++++++ .../github.com/alibabacloud-go/debug/LICENSE | 201 + .../alibabacloud-go/debug/debug/assert.go | 12 + .../alibabacloud-go/debug/debug/debug.go | 36 + .../dysmsapi-20170525/v2/client/client.go | 4124 +++++++++++++++++ .../endpoint-util/service/service.go | 41 + .../alibabacloud-go/openapi-util/LICENSE | 201 + .../openapi-util/service/service.go | 635 +++ .../tea-utils/service/service.go | 462 ++ .../alibabacloud-go/tea-utils/service/util.go | 52 + .../tea-xml/service/service.go | 105 + vendor/github.com/alibabacloud-go/tea/LICENSE | 201 + .../alibabacloud-go/tea/tea/json_parser.go | 333 ++ .../github.com/alibabacloud-go/tea/tea/tea.go | 1121 +++++ .../alibabacloud-go/tea/tea/trans.go | 491 ++ .../alibabacloud-go/tea/utils/assert.go | 64 + .../alibabacloud-go/tea/utils/logger.go | 109 + .../alibabacloud-go/tea/utils/progress.go | 60 + .../github.com/aliyun/credentials-go/LICENSE | 201 + .../credentials/access_key_credential.go | 41 + .../credentials/bearer_token_credential.go | 40 + .../credentials-go/credentials/credential.go | 349 ++ .../credentials/credential_updater.go | 25 + .../credentials/ecs_ram_role.go | 136 + .../credentials/env_provider.go | 43 + .../credentials/instance_provider.go | 28 + .../credentials/profile_provider.go | 350 ++ .../credentials-go/credentials/provider.go | 13 + .../credentials/provider_chain.go | 32 + .../credentials/request/common_request.go | 59 + .../credentials/response/common_response.go | 53 + .../credentials/rsa_key_pair_credential.go | 145 + .../credentials/session_credential.go | 7 + .../credentials/sts_credential.go | 43 + .../credentials/sts_role_arn_credential.go | 163 + .../credentials/utils/runtime.go | 35 + .../credentials-go/credentials/utils/utils.go | 146 + .../github.com/clbanning/mxj/v2/.travis.yml | 4 + vendor/github.com/clbanning/mxj/v2/LICENSE | 22 + vendor/github.com/clbanning/mxj/v2/anyxml.go | 201 + .../clbanning/mxj/v2/atomFeedString.xml | 54 + vendor/github.com/clbanning/mxj/v2/doc.go | 138 + .../clbanning/mxj/v2/escapechars.go | 93 + vendor/github.com/clbanning/mxj/v2/exists.go | 9 + vendor/github.com/clbanning/mxj/v2/files.go | 287 ++ .../clbanning/mxj/v2/files_test.badjson | 2 + .../clbanning/mxj/v2/files_test.badxml | 9 + .../clbanning/mxj/v2/files_test.json | 2 + .../clbanning/mxj/v2/files_test.xml | 9 + .../clbanning/mxj/v2/files_test_dup.json | 1 + .../clbanning/mxj/v2/files_test_dup.xml | 1 + .../clbanning/mxj/v2/files_test_indent.json | 12 + .../clbanning/mxj/v2/files_test_indent.xml | 8 + vendor/github.com/clbanning/mxj/v2/go.mod | 3 + vendor/github.com/clbanning/mxj/v2/gob.go | 35 + vendor/github.com/clbanning/mxj/v2/json.go | 323 ++ .../github.com/clbanning/mxj/v2/keyvalues.go | 668 +++ .../github.com/clbanning/mxj/v2/leafnode.go | 112 + vendor/github.com/clbanning/mxj/v2/misc.go | 86 + vendor/github.com/clbanning/mxj/v2/mxj.go | 128 + vendor/github.com/clbanning/mxj/v2/newmap.go | 184 + vendor/github.com/clbanning/mxj/v2/readme.md | 207 + vendor/github.com/clbanning/mxj/v2/remove.go | 37 + vendor/github.com/clbanning/mxj/v2/rename.go | 61 + vendor/github.com/clbanning/mxj/v2/set.go | 26 + .../clbanning/mxj/v2/setfieldsep.go | 20 + .../github.com/clbanning/mxj/v2/songtext.xml | 29 + vendor/github.com/clbanning/mxj/v2/strict.go | 30 + vendor/github.com/clbanning/mxj/v2/struct.go | 54 + .../clbanning/mxj/v2/updatevalues.go | 258 ++ vendor/github.com/clbanning/mxj/v2/xml.go | 1410 ++++++ vendor/github.com/clbanning/mxj/v2/xmlseq.go | 877 ++++ vendor/github.com/clbanning/mxj/v2/xmlseq2.go | 18 + vendor/github.com/json-iterator/go/README.md | 36 +- vendor/github.com/json-iterator/go/any_str.go | 4 +- vendor/github.com/json-iterator/go/config.go | 4 +- .../json-iterator/go/iter_object.go | 4 +- .../json-iterator/go/reflect_extension.go | 2 +- .../json-iterator/go/reflect_map.go | 80 +- .../json-iterator/go/reflect_optional.go | 4 - .../go/reflect_struct_decoder.go | 22 +- vendor/github.com/json-iterator/go/stream.go | 5 +- vendor/github.com/tjfoc/gmsm/LICENSE | 201 + vendor/github.com/tjfoc/gmsm/sm3/sm3.go | 260 ++ .../yuin/goldmark/extension/typographer.go | 7 +- .../yuin/goldmark/parser/atx_heading.go | 2 +- .../golang.org/x/sync/semaphore/semaphore.go | 11 +- vendor/gopkg.in/ini.v1/Makefile | 2 +- vendor/gopkg.in/ini.v1/README.md | 8 +- vendor/gopkg.in/ini.v1/codecov.yml | 9 + vendor/gopkg.in/ini.v1/data_source.go | 2 + vendor/gopkg.in/ini.v1/file.go | 153 +- vendor/gopkg.in/ini.v1/ini.go | 16 +- vendor/gopkg.in/ini.v1/key.go | 120 +- vendor/gopkg.in/ini.v1/parser.go | 21 +- vendor/gopkg.in/ini.v1/section.go | 4 +- vendor/gopkg.in/ini.v1/struct.go | 171 +- vendor/modules.txt | 43 +- 106 files changed, 19043 insertions(+), 221 deletions(-) create mode 100644 modules/phone/phone.go create mode 100644 modules/setting/phone.go create mode 100644 vendor/github.com/alibabacloud-go/alibabacloud-gateway-spi/LICENSE create mode 100644 vendor/github.com/alibabacloud-go/alibabacloud-gateway-spi/client/client.go create mode 100644 vendor/github.com/alibabacloud-go/darabonba-openapi/LICENSE create mode 100644 vendor/github.com/alibabacloud-go/darabonba-openapi/client/client.go create mode 100644 vendor/github.com/alibabacloud-go/debug/LICENSE create mode 100644 vendor/github.com/alibabacloud-go/debug/debug/assert.go create mode 100644 vendor/github.com/alibabacloud-go/debug/debug/debug.go create mode 100644 vendor/github.com/alibabacloud-go/dysmsapi-20170525/v2/client/client.go create mode 100644 vendor/github.com/alibabacloud-go/endpoint-util/service/service.go create mode 100644 vendor/github.com/alibabacloud-go/openapi-util/LICENSE create mode 100644 vendor/github.com/alibabacloud-go/openapi-util/service/service.go create mode 100644 vendor/github.com/alibabacloud-go/tea-utils/service/service.go create mode 100644 vendor/github.com/alibabacloud-go/tea-utils/service/util.go create mode 100644 vendor/github.com/alibabacloud-go/tea-xml/service/service.go create mode 100644 vendor/github.com/alibabacloud-go/tea/LICENSE create mode 100644 vendor/github.com/alibabacloud-go/tea/tea/json_parser.go create mode 100644 vendor/github.com/alibabacloud-go/tea/tea/tea.go create mode 100644 vendor/github.com/alibabacloud-go/tea/tea/trans.go create mode 100644 vendor/github.com/alibabacloud-go/tea/utils/assert.go create mode 100644 vendor/github.com/alibabacloud-go/tea/utils/logger.go create mode 100644 vendor/github.com/alibabacloud-go/tea/utils/progress.go create mode 100644 vendor/github.com/aliyun/credentials-go/LICENSE create mode 100644 vendor/github.com/aliyun/credentials-go/credentials/access_key_credential.go create mode 100644 vendor/github.com/aliyun/credentials-go/credentials/bearer_token_credential.go create mode 100644 vendor/github.com/aliyun/credentials-go/credentials/credential.go create mode 100644 vendor/github.com/aliyun/credentials-go/credentials/credential_updater.go create mode 100644 vendor/github.com/aliyun/credentials-go/credentials/ecs_ram_role.go create mode 100644 vendor/github.com/aliyun/credentials-go/credentials/env_provider.go create mode 100644 vendor/github.com/aliyun/credentials-go/credentials/instance_provider.go create mode 100644 vendor/github.com/aliyun/credentials-go/credentials/profile_provider.go create mode 100644 vendor/github.com/aliyun/credentials-go/credentials/provider.go create mode 100644 vendor/github.com/aliyun/credentials-go/credentials/provider_chain.go create mode 100644 vendor/github.com/aliyun/credentials-go/credentials/request/common_request.go create mode 100644 vendor/github.com/aliyun/credentials-go/credentials/response/common_response.go create mode 100644 vendor/github.com/aliyun/credentials-go/credentials/rsa_key_pair_credential.go create mode 100644 vendor/github.com/aliyun/credentials-go/credentials/session_credential.go create mode 100644 vendor/github.com/aliyun/credentials-go/credentials/sts_credential.go create mode 100644 vendor/github.com/aliyun/credentials-go/credentials/sts_role_arn_credential.go create mode 100644 vendor/github.com/aliyun/credentials-go/credentials/utils/runtime.go create mode 100644 vendor/github.com/aliyun/credentials-go/credentials/utils/utils.go create mode 100644 vendor/github.com/clbanning/mxj/v2/.travis.yml create mode 100644 vendor/github.com/clbanning/mxj/v2/LICENSE create mode 100644 vendor/github.com/clbanning/mxj/v2/anyxml.go create mode 100644 vendor/github.com/clbanning/mxj/v2/atomFeedString.xml create mode 100644 vendor/github.com/clbanning/mxj/v2/doc.go create mode 100644 vendor/github.com/clbanning/mxj/v2/escapechars.go create mode 100644 vendor/github.com/clbanning/mxj/v2/exists.go create mode 100644 vendor/github.com/clbanning/mxj/v2/files.go create mode 100644 vendor/github.com/clbanning/mxj/v2/files_test.badjson create mode 100644 vendor/github.com/clbanning/mxj/v2/files_test.badxml create mode 100644 vendor/github.com/clbanning/mxj/v2/files_test.json create mode 100644 vendor/github.com/clbanning/mxj/v2/files_test.xml create mode 100644 vendor/github.com/clbanning/mxj/v2/files_test_dup.json create mode 100644 vendor/github.com/clbanning/mxj/v2/files_test_dup.xml create mode 100644 vendor/github.com/clbanning/mxj/v2/files_test_indent.json create mode 100644 vendor/github.com/clbanning/mxj/v2/files_test_indent.xml create mode 100644 vendor/github.com/clbanning/mxj/v2/go.mod create mode 100644 vendor/github.com/clbanning/mxj/v2/gob.go create mode 100644 vendor/github.com/clbanning/mxj/v2/json.go create mode 100644 vendor/github.com/clbanning/mxj/v2/keyvalues.go create mode 100644 vendor/github.com/clbanning/mxj/v2/leafnode.go create mode 100644 vendor/github.com/clbanning/mxj/v2/misc.go create mode 100644 vendor/github.com/clbanning/mxj/v2/mxj.go create mode 100644 vendor/github.com/clbanning/mxj/v2/newmap.go create mode 100644 vendor/github.com/clbanning/mxj/v2/readme.md create mode 100644 vendor/github.com/clbanning/mxj/v2/remove.go create mode 100644 vendor/github.com/clbanning/mxj/v2/rename.go create mode 100644 vendor/github.com/clbanning/mxj/v2/set.go create mode 100644 vendor/github.com/clbanning/mxj/v2/setfieldsep.go create mode 100644 vendor/github.com/clbanning/mxj/v2/songtext.xml create mode 100644 vendor/github.com/clbanning/mxj/v2/strict.go create mode 100644 vendor/github.com/clbanning/mxj/v2/struct.go create mode 100644 vendor/github.com/clbanning/mxj/v2/updatevalues.go create mode 100644 vendor/github.com/clbanning/mxj/v2/xml.go create mode 100644 vendor/github.com/clbanning/mxj/v2/xmlseq.go create mode 100644 vendor/github.com/clbanning/mxj/v2/xmlseq2.go create mode 100644 vendor/github.com/tjfoc/gmsm/LICENSE create mode 100644 vendor/github.com/tjfoc/gmsm/sm3/sm3.go create mode 100644 vendor/gopkg.in/ini.v1/codecov.yml diff --git a/go.mod b/go.mod index c1e959f8e..0a9d0226c 100755 --- a/go.mod +++ b/go.mod @@ -22,8 +22,13 @@ require ( github.com/PuerkitoBio/goquery v1.5.0 github.com/RichardKnop/machinery v1.6.9 github.com/RoaringBitmap/roaring v0.4.23 // indirect + github.com/alibabacloud-go/darabonba-openapi v0.1.18 + github.com/alibabacloud-go/dysmsapi-20170525/v2 v2.0.9 + github.com/alibabacloud-go/tea v1.1.17 + github.com/alibabacloud-go/tea-xml v1.1.2 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect github.com/blevesearch/bleve v1.0.7 + github.com/clbanning/mxj/v2 v2.5.5 // indirect github.com/couchbase/gomemcached v0.0.0-20191004160342-7b5da2ec40b2 // indirect github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d // indirect github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect @@ -112,9 +117,9 @@ require ( github.com/urfave/cli v1.22.1 github.com/xanzy/go-gitlab v0.31.0 github.com/yohcop/openid-go v1.0.0 - github.com/yuin/goldmark v1.1.27 + github.com/yuin/goldmark v1.1.30 github.com/yuin/goldmark-meta v0.0.0-20191126180153-f0638e958b60 - golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 + golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 golang.org/x/mod v0.3.0 // indirect golang.org/x/net v0.0.0-20200513185701-a91f0712d120 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d @@ -126,7 +131,7 @@ require ( gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 // indirect gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df - gopkg.in/ini.v1 v1.52.0 + gopkg.in/ini.v1 v1.56.0 gopkg.in/ldap.v3 v3.0.2 gopkg.in/macaron.v1 v1.3.9 // indirect gopkg.in/testfixtures.v2 v2.5.0 diff --git a/go.sum b/go.sum index d9dc14b64..9293fd947 100755 --- a/go.sum +++ b/go.sum @@ -78,6 +78,35 @@ github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBb github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.2/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc= +github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 h1:iC9YFYKDGEy3n/FtqJnOkZsene9olVspKmkX5A2YBEo= +github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc= +github.com/alibabacloud-go/darabonba-openapi v0.1.14/go.mod h1:w4CosR7O/kapCtEEMBm3JsQqWBU/CnZ2o0pHorsTWDI= +github.com/alibabacloud-go/darabonba-openapi v0.1.18 h1:3eUVmAr7WCJp7fgIvmCd9ZUyuwtJYbtUqJIed5eXCmk= +github.com/alibabacloud-go/darabonba-openapi v0.1.18/go.mod h1:PB4HffMhJVmAgNKNq3wYbTUlFvPgxJpTzd1F5pTuUsc= +github.com/alibabacloud-go/darabonba-string v1.0.0/go.mod h1:93cTfV3vuPhhEwGGpKKqhVW4jLe7tDpo3LUM0i0g6mA= +github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68 h1:NqugFkGxx1TXSh/pBcU00Y6bljgDPaFdh5MUSeJ7e50= +github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY= +github.com/alibabacloud-go/dysmsapi-20170525/v2 v2.0.9 h1:z+OU7LbWtQitWJ8SAn55hEQkJPCsEPJc97TvGCZV+4s= +github.com/alibabacloud-go/dysmsapi-20170525/v2 v2.0.9/go.mod h1:AT91gCNJPsemf4lHLNgWTf/RsgmpdOprWvQ3FYvtwGk= +github.com/alibabacloud-go/endpoint-util v1.1.0 h1:r/4D3VSw888XGaeNpP994zDUaxdgTSHBbVfZlzf6b5Q= +github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE= +github.com/alibabacloud-go/openapi-util v0.0.10/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws= +github.com/alibabacloud-go/openapi-util v0.0.11 h1:iYnqOPR5hyEEnNZmebGyRMkkEJRWUEjDiiaOHZ5aNhA= +github.com/alibabacloud-go/openapi-util v0.0.11/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws= +github.com/alibabacloud-go/tea v1.1.0/go.mod h1:IkGyUSX4Ba1V+k4pCtJUc6jDpZLFph9QMy2VUPTwukg= +github.com/alibabacloud-go/tea v1.1.7/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= +github.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= +github.com/alibabacloud-go/tea v1.1.11/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= +github.com/alibabacloud-go/tea v1.1.17 h1:05R5DnaJXe9sCNIe8KUgWHC/z6w/VZIwczgUwzRnul8= +github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= +github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE= +github.com/alibabacloud-go/tea-utils v1.4.3 h1:8SzwmmRrOnQ09Hf5a9GyfJc0d7Sjv6fmsZoF4UDbFjo= +github.com/alibabacloud-go/tea-utils v1.4.3/go.mod h1:KNcT0oXlZZxOXINnZBs6YvgOd5aYp9U67G+E3R8fcQw= +github.com/alibabacloud-go/tea-xml v1.1.2 h1:oLxa7JUXm2EDFzMg+7oRsYc+kutgCVwm+bZlhhmvW5M= +github.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8= +github.com/aliyun/credentials-go v1.1.2 h1:qU1vwGIBb3UJ8BwunHDRFtAhS6jnQLnde/yk0+Ih2GY= +github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw= github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o= github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= @@ -125,6 +154,8 @@ github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/chris-ramon/douceur v0.2.0 h1:IDMEdxlEUUBYBKE4z/mJnFyVXox+MjuEVDJNN27glkU= github.com/chris-ramon/douceur v0.2.0/go.mod h1:wDW5xjJdeoMm1mRt4sD4c/LbF/mWdEpRXQKjTR8nIBE= +github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E= +github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/corbym/gocrest v1.0.3 h1:gwEdq6RkTmq+09CTuM29DfKOCtZ7G7bcyxs3IZ6EVdU= github.com/corbym/gocrest v1.0.3/go.mod h1:maVFL5lbdS2PgfOQgGRWDYTeunSWQeiEgoNdTABShCs= @@ -369,6 +400,7 @@ github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORR github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= @@ -432,6 +464,8 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= @@ -673,11 +707,13 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1 github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w= github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= +github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8= github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= @@ -707,6 +743,7 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM= @@ -722,6 +759,8 @@ github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhV github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tinylib/msgp v1.1.2 h1:gWmO7n0Ys2RBEb7GPYB9Ujq8Mk5p2U08lRnmMcGy6BQ= github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= +github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM= +github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/toqueteos/trie v1.0.0 h1:8i6pXxNUXNRAqP246iibb7w/pSFquNTQ+uNfriG7vlk= github.com/toqueteos/trie v1.0.0/go.mod h1:Ywk48QhEqhU1+DwhMkJ2x7eeGxDHiGkAdc9+0DYcbsM= @@ -761,6 +800,8 @@ github.com/yuin/goldmark v1.1.7/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27 h1:nqDD4MMMQA0lmWq03Z2/myGPYLQoXtmi0rGVs95ntbo= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.30 h1:j4d4Lw3zqZelDhBksEo3BnWg9xhXRQGJPPSL6OApZjI= +github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark-meta v0.0.0-20191126180153-f0638e958b60 h1:gZucqLjL1eDzVWrXj4uiWeMbAopJlBR2mKQAsTGdPwo= github.com/yuin/goldmark-meta v0.0.0-20191126180153-f0638e958b60/go.mod h1:i9VhcIHN2PxXMbQrKqXNueok6QNONoPjNMoj9MygVL0= github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= @@ -800,9 +841,12 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190907121410-71b5226ff739/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 h1:IaQbIIB2X/Mp/DKctl6ROxz1KyMlKp4uyvL6+kQ7C88= golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw= +golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= @@ -848,6 +892,7 @@ golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120 h1:EZ3cVSzKOlJxAd8e8YAJ7no8nNypTxexh/YE/xW3ZEY= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/oauth2 v0.0.0-20180620175406-ef147856a6dd/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -868,6 +913,8 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180824143301-4910a1d54f87/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -933,6 +980,7 @@ golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200225230052-807dcd883420/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515220128-d3bf790afa53 h1:vmsb6v0zUdmUlXfwKaYrHPPRCV0lHq/IwNIf0ASGjyQ= golang.org/x/tools v0.0.0-20200515220128-d3bf790afa53/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1012,6 +1060,8 @@ gopkg.in/ini.v1 v1.44.2/go.mod h1:M3Cogqpuv0QCi3ExAY5V4uOt4qb/R3xZubo9m8lK5wg= gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.52.0 h1:j+Lt/M1oPPejkniCg1TkWE2J3Eh1oZTsHSXzMTzUXn4= gopkg.in/ini.v1 v1.52.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.56.0 h1:DPMeDvGTM54DXbPkVIZsp19fp/I2K7zwA/itHYHKo8Y= +gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ldap.v3 v3.0.2 h1:R6RBtabK6e1GO0eQKtkyOFbAHO73QesLzI2w2DZ6b9w= gopkg.in/ldap.v3 v3.0.2/go.mod h1:oxD7NyBuxchC+SgJDE1Q5Od05eGt29SDQVBmV+HYbzw= gopkg.in/macaron.v1 v1.3.9 h1:Dw+DDRYdXgQyEsPlfAfKz+UA5qVUrH3KPD7JhmZ9MFc= diff --git a/modules/phone/phone.go b/modules/phone/phone.go new file mode 100644 index 000000000..eb5b58f5a --- /dev/null +++ b/modules/phone/phone.go @@ -0,0 +1,62 @@ +package phone + +import ( + "math" + "math/rand" + "regexp" + "strconv" + "time" + + "code.gitea.io/gitea/modules/setting" + + openapi "github.com/alibabacloud-go/darabonba-openapi/client" + dysmsapi20170525 "github.com/alibabacloud-go/dysmsapi-20170525/v2/client" + util "github.com/alibabacloud-go/tea-utils/service" + "github.com/alibabacloud-go/tea/tea" +) + +func IsValidPhoneNumber(phoneNumber string) bool { + pattern := "^1[3-9]\\d{9}$" + match, _ := regexp.MatchString(pattern, phoneNumber) + return match +} + +func GenerateVerifyCode(n int) string { + min := int(math.Pow10(n - 1)) + max := int(math.Pow10(n)) + rand.Seed(time.Now().UnixNano()) + return strconv.Itoa(rand.Intn(max-min) + min) + +} + +func createClient(accessKeyId *string, accessKeySecret *string) (_result *dysmsapi20170525.Client, _err error) { + config := &openapi.Config{ + // 您的AccessKey ID + AccessKeyId: accessKeyId, + // 您的AccessKey Secret + AccessKeySecret: accessKeySecret, + } + // 访问的域名 + config.Endpoint = tea.String("dysmsapi.aliyuncs.com") + _result = &dysmsapi20170525.Client{} + _result, _err = dysmsapi20170525.NewClient(config) + return _result, _err +} + +func SendVerifyCode(phoneNumber string, verifyCode string) error { + client, _err := createClient(&setting.PhoneService.AccessKeyId, &setting.PhoneService.AccessKeySecret) + if _err != nil { + return _err + } + + sendSmsRequest := &dysmsapi20170525.SendSmsRequest{ + SignName: tea.String(setting.PhoneService.SignName), + TemplateCode: tea.String(setting.PhoneService.TemplateCode), + PhoneNumbers: tea.String(phoneNumber), + TemplateParam: tea.String("{\"code\":\"" + verifyCode + "\"}"), + } + runtime := &util.RuntimeOptions{} + // 复制代码运行请自行打印 API 的返回值 + _, _err = client.SendSmsWithOptions(sendSmsRequest, runtime) + return _err +} diff --git a/modules/setting/phone.go b/modules/setting/phone.go new file mode 100644 index 000000000..37f9d6c26 --- /dev/null +++ b/modules/setting/phone.go @@ -0,0 +1,44 @@ +package setting + +import ( + "code.gitea.io/gitea/modules/log" +) + +type Phone struct { + Enabled bool + VerifyCodeLength int + AccessKeyId string + AccessKeySecret string + SignName string + TemplateCode string + CodeTimeout int + RetryInterval int + MaxRetryTimes int +} + +var ( + // Phone verify info + PhoneService *Phone +) + +func newPhoneService() { + sec := Cfg.Section("phone") + // Check phone setting. + if !sec.Key("ENABLED").MustBool() { + return + } + + PhoneService = &Phone{ + Enabled: sec.Key("ENABLED").MustBool(), + VerifyCodeLength: sec.Key("VERIFY_CODE_LEN").MustInt(6), + AccessKeyId: sec.Key("AccessKeyId").String(), + AccessKeySecret: sec.Key("AccessKeySecret").String(), + SignName: sec.Key("SignName").String(), + TemplateCode: sec.Key("TemplateCode").String(), + CodeTimeout: sec.Key("CODE_TIMEOUT").MustInt(60 * 5), + RetryInterval: sec.Key("RETRY_INTERVAL").MustInt(60 * 2), + MaxRetryTimes: sec.Key("MAX_RETRY").MustInt(5), + } + + log.Info("Phone Service Enabled") +} diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 5c87b68c5..6bdf299af 100755 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -1519,4 +1519,5 @@ func NewServices() { newIndexerService() newTaskService() NewQueueService() + newPhoneService() } diff --git a/vendor/github.com/alibabacloud-go/alibabacloud-gateway-spi/LICENSE b/vendor/github.com/alibabacloud-go/alibabacloud-gateway-spi/LICENSE new file mode 100644 index 000000000..0c44dcefe --- /dev/null +++ b/vendor/github.com/alibabacloud-go/alibabacloud-gateway-spi/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) 2009-present, Alibaba Cloud All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/alibabacloud-go/alibabacloud-gateway-spi/client/client.go b/vendor/github.com/alibabacloud-go/alibabacloud-gateway-spi/client/client.go new file mode 100644 index 000000000..1d47c93aa --- /dev/null +++ b/vendor/github.com/alibabacloud-go/alibabacloud-gateway-spi/client/client.go @@ -0,0 +1,305 @@ +// This file is auto-generated, don't edit it. Thanks. +package client + +import ( + "io" + + "github.com/alibabacloud-go/tea/tea" + credential "github.com/aliyun/credentials-go/credentials" +) + +type InterceptorContext struct { + Request *InterceptorContextRequest `json:"request,omitempty" xml:"request,omitempty" require:"true" type:"Struct"` + Configuration *InterceptorContextConfiguration `json:"configuration,omitempty" xml:"configuration,omitempty" require:"true" type:"Struct"` + Response *InterceptorContextResponse `json:"response,omitempty" xml:"response,omitempty" require:"true" type:"Struct"` +} + +func (s InterceptorContext) String() string { + return tea.Prettify(s) +} + +func (s InterceptorContext) GoString() string { + return s.String() +} + +func (s *InterceptorContext) SetRequest(v *InterceptorContextRequest) *InterceptorContext { + s.Request = v + return s +} + +func (s *InterceptorContext) SetConfiguration(v *InterceptorContextConfiguration) *InterceptorContext { + s.Configuration = v + return s +} + +func (s *InterceptorContext) SetResponse(v *InterceptorContextResponse) *InterceptorContext { + s.Response = v + return s +} + +type InterceptorContextRequest struct { + Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty"` + Query map[string]*string `json:"query,omitempty" xml:"query,omitempty"` + Body interface{} `json:"body,omitempty" xml:"body,omitempty"` + Stream io.Reader `json:"stream,omitempty" xml:"stream,omitempty"` + HostMap map[string]*string `json:"hostMap,omitempty" xml:"hostMap,omitempty"` + Pathname *string `json:"pathname,omitempty" xml:"pathname,omitempty" require:"true"` + ProductId *string `json:"productId,omitempty" xml:"productId,omitempty" require:"true"` + Action *string `json:"action,omitempty" xml:"action,omitempty" require:"true"` + Version *string `json:"version,omitempty" xml:"version,omitempty" require:"true"` + Protocol *string `json:"protocol,omitempty" xml:"protocol,omitempty" require:"true"` + Method *string `json:"method,omitempty" xml:"method,omitempty" require:"true"` + AuthType *string `json:"authType,omitempty" xml:"authType,omitempty" require:"true"` + BodyType *string `json:"bodyType,omitempty" xml:"bodyType,omitempty" require:"true"` + ReqBodyType *string `json:"reqBodyType,omitempty" xml:"reqBodyType,omitempty" require:"true"` + Style *string `json:"style,omitempty" xml:"style,omitempty"` + Credential credential.Credential `json:"credential,omitempty" xml:"credential,omitempty" require:"true"` + SignatureVersion *string `json:"signatureVersion,omitempty" xml:"signatureVersion,omitempty"` + SignatureAlgorithm *string `json:"signatureAlgorithm,omitempty" xml:"signatureAlgorithm,omitempty"` + UserAgent *string `json:"userAgent,omitempty" xml:"userAgent,omitempty" require:"true"` +} + +func (s InterceptorContextRequest) String() string { + return tea.Prettify(s) +} + +func (s InterceptorContextRequest) GoString() string { + return s.String() +} + +func (s *InterceptorContextRequest) SetHeaders(v map[string]*string) *InterceptorContextRequest { + s.Headers = v + return s +} + +func (s *InterceptorContextRequest) SetQuery(v map[string]*string) *InterceptorContextRequest { + s.Query = v + return s +} + +func (s *InterceptorContextRequest) SetBody(v interface{}) *InterceptorContextRequest { + s.Body = v + return s +} + +func (s *InterceptorContextRequest) SetStream(v io.Reader) *InterceptorContextRequest { + s.Stream = v + return s +} + +func (s *InterceptorContextRequest) SetHostMap(v map[string]*string) *InterceptorContextRequest { + s.HostMap = v + return s +} + +func (s *InterceptorContextRequest) SetPathname(v string) *InterceptorContextRequest { + s.Pathname = &v + return s +} + +func (s *InterceptorContextRequest) SetProductId(v string) *InterceptorContextRequest { + s.ProductId = &v + return s +} + +func (s *InterceptorContextRequest) SetAction(v string) *InterceptorContextRequest { + s.Action = &v + return s +} + +func (s *InterceptorContextRequest) SetVersion(v string) *InterceptorContextRequest { + s.Version = &v + return s +} + +func (s *InterceptorContextRequest) SetProtocol(v string) *InterceptorContextRequest { + s.Protocol = &v + return s +} + +func (s *InterceptorContextRequest) SetMethod(v string) *InterceptorContextRequest { + s.Method = &v + return s +} + +func (s *InterceptorContextRequest) SetAuthType(v string) *InterceptorContextRequest { + s.AuthType = &v + return s +} + +func (s *InterceptorContextRequest) SetBodyType(v string) *InterceptorContextRequest { + s.BodyType = &v + return s +} + +func (s *InterceptorContextRequest) SetReqBodyType(v string) *InterceptorContextRequest { + s.ReqBodyType = &v + return s +} + +func (s *InterceptorContextRequest) SetStyle(v string) *InterceptorContextRequest { + s.Style = &v + return s +} + +func (s *InterceptorContextRequest) SetCredential(v credential.Credential) *InterceptorContextRequest { + s.Credential = v + return s +} + +func (s *InterceptorContextRequest) SetSignatureVersion(v string) *InterceptorContextRequest { + s.SignatureVersion = &v + return s +} + +func (s *InterceptorContextRequest) SetSignatureAlgorithm(v string) *InterceptorContextRequest { + s.SignatureAlgorithm = &v + return s +} + +func (s *InterceptorContextRequest) SetUserAgent(v string) *InterceptorContextRequest { + s.UserAgent = &v + return s +} + +type InterceptorContextConfiguration struct { + RegionId *string `json:"regionId,omitempty" xml:"regionId,omitempty" require:"true"` + Endpoint *string `json:"endpoint,omitempty" xml:"endpoint,omitempty"` + EndpointRule *string `json:"endpointRule,omitempty" xml:"endpointRule,omitempty"` + EndpointMap map[string]*string `json:"endpointMap,omitempty" xml:"endpointMap,omitempty"` + EndpointType *string `json:"endpointType,omitempty" xml:"endpointType,omitempty"` + Network *string `json:"network,omitempty" xml:"network,omitempty"` + Suffix *string `json:"suffix,omitempty" xml:"suffix,omitempty"` +} + +func (s InterceptorContextConfiguration) String() string { + return tea.Prettify(s) +} + +func (s InterceptorContextConfiguration) GoString() string { + return s.String() +} + +func (s *InterceptorContextConfiguration) SetRegionId(v string) *InterceptorContextConfiguration { + s.RegionId = &v + return s +} + +func (s *InterceptorContextConfiguration) SetEndpoint(v string) *InterceptorContextConfiguration { + s.Endpoint = &v + return s +} + +func (s *InterceptorContextConfiguration) SetEndpointRule(v string) *InterceptorContextConfiguration { + s.EndpointRule = &v + return s +} + +func (s *InterceptorContextConfiguration) SetEndpointMap(v map[string]*string) *InterceptorContextConfiguration { + s.EndpointMap = v + return s +} + +func (s *InterceptorContextConfiguration) SetEndpointType(v string) *InterceptorContextConfiguration { + s.EndpointType = &v + return s +} + +func (s *InterceptorContextConfiguration) SetNetwork(v string) *InterceptorContextConfiguration { + s.Network = &v + return s +} + +func (s *InterceptorContextConfiguration) SetSuffix(v string) *InterceptorContextConfiguration { + s.Suffix = &v + return s +} + +type InterceptorContextResponse struct { + StatusCode *int `json:"statusCode,omitempty" xml:"statusCode,omitempty"` + Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty"` + Body io.Reader `json:"body,omitempty" xml:"body,omitempty"` + DeserializedBody interface{} `json:"deserializedBody,omitempty" xml:"deserializedBody,omitempty"` +} + +func (s InterceptorContextResponse) String() string { + return tea.Prettify(s) +} + +func (s InterceptorContextResponse) GoString() string { + return s.String() +} + +func (s *InterceptorContextResponse) SetStatusCode(v int) *InterceptorContextResponse { + s.StatusCode = &v + return s +} + +func (s *InterceptorContextResponse) SetHeaders(v map[string]*string) *InterceptorContextResponse { + s.Headers = v + return s +} + +func (s *InterceptorContextResponse) SetBody(v io.Reader) *InterceptorContextResponse { + s.Body = v + return s +} + +func (s *InterceptorContextResponse) SetDeserializedBody(v interface{}) *InterceptorContextResponse { + s.DeserializedBody = v + return s +} + +type AttributeMap struct { + Attributes map[string]interface{} `json:"attributes,omitempty" xml:"attributes,omitempty" require:"true"` + Key map[string]*string `json:"key,omitempty" xml:"key,omitempty" require:"true"` +} + +func (s AttributeMap) String() string { + return tea.Prettify(s) +} + +func (s AttributeMap) GoString() string { + return s.String() +} + +func (s *AttributeMap) SetAttributes(v map[string]interface{}) *AttributeMap { + s.Attributes = v + return s +} + +func (s *AttributeMap) SetKey(v map[string]*string) *AttributeMap { + s.Key = v + return s +} + +type ClientInterface interface { + ModifyConfiguration(context *InterceptorContext, attributeMap *AttributeMap) error + ModifyRequest(context *InterceptorContext, attributeMap *AttributeMap) error + ModifyResponse(context *InterceptorContext, attributeMap *AttributeMap) error +} + +type Client struct { +} + +func NewClient() (*Client, error) { + client := new(Client) + err := client.Init() + return client, err +} + +func (client *Client) Init() (_err error) { + return nil +} + +func (client *Client) ModifyConfiguration(context *InterceptorContext, attributeMap *AttributeMap) (_err error) { + panic("No Support!") +} + +func (client *Client) ModifyRequest(context *InterceptorContext, attributeMap *AttributeMap) (_err error) { + panic("No Support!") +} + +func (client *Client) ModifyResponse(context *InterceptorContext, attributeMap *AttributeMap) (_err error) { + panic("No Support!") +} diff --git a/vendor/github.com/alibabacloud-go/darabonba-openapi/LICENSE b/vendor/github.com/alibabacloud-go/darabonba-openapi/LICENSE new file mode 100644 index 000000000..0c44dcefe --- /dev/null +++ b/vendor/github.com/alibabacloud-go/darabonba-openapi/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) 2009-present, Alibaba Cloud All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/alibabacloud-go/darabonba-openapi/client/client.go b/vendor/github.com/alibabacloud-go/darabonba-openapi/client/client.go new file mode 100644 index 000000000..6da3e1fdd --- /dev/null +++ b/vendor/github.com/alibabacloud-go/darabonba-openapi/client/client.go @@ -0,0 +1,1623 @@ +// This file is auto-generated, don't edit it. Thanks. +/** + * This is for OpenApi SDK + */ +package client + +import ( + "io" + + spi "github.com/alibabacloud-go/alibabacloud-gateway-spi/client" + openapiutil "github.com/alibabacloud-go/openapi-util/service" + util "github.com/alibabacloud-go/tea-utils/service" + xml "github.com/alibabacloud-go/tea-xml/service" + "github.com/alibabacloud-go/tea/tea" + credential "github.com/aliyun/credentials-go/credentials" +) + +/** + * Model for initing client + */ +type Config struct { + // accesskey id + AccessKeyId *string `json:"accessKeyId,omitempty" xml:"accessKeyId,omitempty"` + // accesskey secret + AccessKeySecret *string `json:"accessKeySecret,omitempty" xml:"accessKeySecret,omitempty"` + // security token + SecurityToken *string `json:"securityToken,omitempty" xml:"securityToken,omitempty"` + // http protocol + Protocol *string `json:"protocol,omitempty" xml:"protocol,omitempty"` + // http method + Method *string `json:"method,omitempty" xml:"method,omitempty"` + // region id + RegionId *string `json:"regionId,omitempty" xml:"regionId,omitempty"` + // read timeout + ReadTimeout *int `json:"readTimeout,omitempty" xml:"readTimeout,omitempty"` + // connect timeout + ConnectTimeout *int `json:"connectTimeout,omitempty" xml:"connectTimeout,omitempty"` + // http proxy + HttpProxy *string `json:"httpProxy,omitempty" xml:"httpProxy,omitempty"` + // https proxy + HttpsProxy *string `json:"httpsProxy,omitempty" xml:"httpsProxy,omitempty"` + // credential + Credential credential.Credential `json:"credential,omitempty" xml:"credential,omitempty"` + // endpoint + Endpoint *string `json:"endpoint,omitempty" xml:"endpoint,omitempty"` + // proxy white list + NoProxy *string `json:"noProxy,omitempty" xml:"noProxy,omitempty"` + // max idle conns + MaxIdleConns *int `json:"maxIdleConns,omitempty" xml:"maxIdleConns,omitempty"` + // network for endpoint + Network *string `json:"network,omitempty" xml:"network,omitempty"` + // user agent + UserAgent *string `json:"userAgent,omitempty" xml:"userAgent,omitempty"` + // suffix for endpoint + Suffix *string `json:"suffix,omitempty" xml:"suffix,omitempty"` + // socks5 proxy + Socks5Proxy *string `json:"socks5Proxy,omitempty" xml:"socks5Proxy,omitempty"` + // socks5 network + Socks5NetWork *string `json:"socks5NetWork,omitempty" xml:"socks5NetWork,omitempty"` + // endpoint type + EndpointType *string `json:"endpointType,omitempty" xml:"endpointType,omitempty"` + // OpenPlatform endpoint + OpenPlatformEndpoint *string `json:"openPlatformEndpoint,omitempty" xml:"openPlatformEndpoint,omitempty"` + // Deprecated + // credential type + Type *string `json:"type,omitempty" xml:"type,omitempty"` + // Signature Version + SignatureVersion *string `json:"signatureVersion,omitempty" xml:"signatureVersion,omitempty"` + // Signature Algorithm + SignatureAlgorithm *string `json:"signatureAlgorithm,omitempty" xml:"signatureAlgorithm,omitempty"` +} + +func (s Config) String() string { + return tea.Prettify(s) +} + +func (s Config) GoString() string { + return s.String() +} + +func (s *Config) SetAccessKeyId(v string) *Config { + s.AccessKeyId = &v + return s +} + +func (s *Config) SetAccessKeySecret(v string) *Config { + s.AccessKeySecret = &v + return s +} + +func (s *Config) SetSecurityToken(v string) *Config { + s.SecurityToken = &v + return s +} + +func (s *Config) SetProtocol(v string) *Config { + s.Protocol = &v + return s +} + +func (s *Config) SetMethod(v string) *Config { + s.Method = &v + return s +} + +func (s *Config) SetRegionId(v string) *Config { + s.RegionId = &v + return s +} + +func (s *Config) SetReadTimeout(v int) *Config { + s.ReadTimeout = &v + return s +} + +func (s *Config) SetConnectTimeout(v int) *Config { + s.ConnectTimeout = &v + return s +} + +func (s *Config) SetHttpProxy(v string) *Config { + s.HttpProxy = &v + return s +} + +func (s *Config) SetHttpsProxy(v string) *Config { + s.HttpsProxy = &v + return s +} + +func (s *Config) SetCredential(v credential.Credential) *Config { + s.Credential = v + return s +} + +func (s *Config) SetEndpoint(v string) *Config { + s.Endpoint = &v + return s +} + +func (s *Config) SetNoProxy(v string) *Config { + s.NoProxy = &v + return s +} + +func (s *Config) SetMaxIdleConns(v int) *Config { + s.MaxIdleConns = &v + return s +} + +func (s *Config) SetNetwork(v string) *Config { + s.Network = &v + return s +} + +func (s *Config) SetUserAgent(v string) *Config { + s.UserAgent = &v + return s +} + +func (s *Config) SetSuffix(v string) *Config { + s.Suffix = &v + return s +} + +func (s *Config) SetSocks5Proxy(v string) *Config { + s.Socks5Proxy = &v + return s +} + +func (s *Config) SetSocks5NetWork(v string) *Config { + s.Socks5NetWork = &v + return s +} + +func (s *Config) SetEndpointType(v string) *Config { + s.EndpointType = &v + return s +} + +func (s *Config) SetOpenPlatformEndpoint(v string) *Config { + s.OpenPlatformEndpoint = &v + return s +} + +func (s *Config) SetType(v string) *Config { + s.Type = &v + return s +} + +func (s *Config) SetSignatureVersion(v string) *Config { + s.SignatureVersion = &v + return s +} + +func (s *Config) SetSignatureAlgorithm(v string) *Config { + s.SignatureAlgorithm = &v + return s +} + +type OpenApiRequest struct { + Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty"` + Query map[string]*string `json:"query,omitempty" xml:"query,omitempty"` + Body interface{} `json:"body,omitempty" xml:"body,omitempty"` + Stream io.Reader `json:"stream,omitempty" xml:"stream,omitempty"` + HostMap map[string]*string `json:"hostMap,omitempty" xml:"hostMap,omitempty"` + EndpointOverride *string `json:"endpointOverride,omitempty" xml:"endpointOverride,omitempty"` +} + +func (s OpenApiRequest) String() string { + return tea.Prettify(s) +} + +func (s OpenApiRequest) GoString() string { + return s.String() +} + +func (s *OpenApiRequest) SetHeaders(v map[string]*string) *OpenApiRequest { + s.Headers = v + return s +} + +func (s *OpenApiRequest) SetQuery(v map[string]*string) *OpenApiRequest { + s.Query = v + return s +} + +func (s *OpenApiRequest) SetBody(v interface{}) *OpenApiRequest { + s.Body = v + return s +} + +func (s *OpenApiRequest) SetStream(v io.Reader) *OpenApiRequest { + s.Stream = v + return s +} + +func (s *OpenApiRequest) SetHostMap(v map[string]*string) *OpenApiRequest { + s.HostMap = v + return s +} + +func (s *OpenApiRequest) SetEndpointOverride(v string) *OpenApiRequest { + s.EndpointOverride = &v + return s +} + +type Params struct { + Action *string `json:"action,omitempty" xml:"action,omitempty" require:"true"` + Version *string `json:"version,omitempty" xml:"version,omitempty" require:"true"` + Protocol *string `json:"protocol,omitempty" xml:"protocol,omitempty" require:"true"` + Pathname *string `json:"pathname,omitempty" xml:"pathname,omitempty" require:"true"` + Method *string `json:"method,omitempty" xml:"method,omitempty" require:"true"` + AuthType *string `json:"authType,omitempty" xml:"authType,omitempty" require:"true"` + BodyType *string `json:"bodyType,omitempty" xml:"bodyType,omitempty" require:"true"` + ReqBodyType *string `json:"reqBodyType,omitempty" xml:"reqBodyType,omitempty" require:"true"` + Style *string `json:"style,omitempty" xml:"style,omitempty"` +} + +func (s Params) String() string { + return tea.Prettify(s) +} + +func (s Params) GoString() string { + return s.String() +} + +func (s *Params) SetAction(v string) *Params { + s.Action = &v + return s +} + +func (s *Params) SetVersion(v string) *Params { + s.Version = &v + return s +} + +func (s *Params) SetProtocol(v string) *Params { + s.Protocol = &v + return s +} + +func (s *Params) SetPathname(v string) *Params { + s.Pathname = &v + return s +} + +func (s *Params) SetMethod(v string) *Params { + s.Method = &v + return s +} + +func (s *Params) SetAuthType(v string) *Params { + s.AuthType = &v + return s +} + +func (s *Params) SetBodyType(v string) *Params { + s.BodyType = &v + return s +} + +func (s *Params) SetReqBodyType(v string) *Params { + s.ReqBodyType = &v + return s +} + +func (s *Params) SetStyle(v string) *Params { + s.Style = &v + return s +} + +type Client struct { + Endpoint *string + RegionId *string + Protocol *string + Method *string + UserAgent *string + EndpointRule *string + EndpointMap map[string]*string + Suffix *string + ReadTimeout *int + ConnectTimeout *int + HttpProxy *string + HttpsProxy *string + Socks5Proxy *string + Socks5NetWork *string + NoProxy *string + Network *string + ProductId *string + MaxIdleConns *int + EndpointType *string + OpenPlatformEndpoint *string + Credential credential.Credential + SignatureVersion *string + SignatureAlgorithm *string + Headers map[string]*string + Spi spi.ClientInterface +} + +/** + * Init client with Config + * @param config config contains the necessary information to create a client + */ +func NewClient(config *Config) (*Client, error) { + client := new(Client) + err := client.Init(config) + return client, err +} + +func (client *Client) Init(config *Config) (_err error) { + if tea.BoolValue(util.IsUnset(tea.ToMap(config))) { + _err = tea.NewSDKError(map[string]interface{}{ + "code": "ParameterMissing", + "message": "'config' can not be unset", + }) + return _err + } + + if !tea.BoolValue(util.Empty(config.AccessKeyId)) && !tea.BoolValue(util.Empty(config.AccessKeySecret)) { + if !tea.BoolValue(util.Empty(config.SecurityToken)) { + config.Type = tea.String("sts") + } else { + config.Type = tea.String("access_key") + } + + credentialConfig := &credential.Config{ + AccessKeyId: config.AccessKeyId, + Type: config.Type, + AccessKeySecret: config.AccessKeySecret, + SecurityToken: config.SecurityToken, + } + client.Credential, _err = credential.NewCredential(credentialConfig) + if _err != nil { + return _err + } + + } else if !tea.BoolValue(util.IsUnset(config.Credential)) { + client.Credential = config.Credential + } + + client.Endpoint = config.Endpoint + client.EndpointType = config.EndpointType + client.Network = config.Network + client.Suffix = config.Suffix + client.Protocol = config.Protocol + client.Method = config.Method + client.RegionId = config.RegionId + client.UserAgent = config.UserAgent + client.ReadTimeout = config.ReadTimeout + client.ConnectTimeout = config.ConnectTimeout + client.HttpProxy = config.HttpProxy + client.HttpsProxy = config.HttpsProxy + client.NoProxy = config.NoProxy + client.Socks5Proxy = config.Socks5Proxy + client.Socks5NetWork = config.Socks5NetWork + client.MaxIdleConns = config.MaxIdleConns + client.SignatureVersion = config.SignatureVersion + client.SignatureAlgorithm = config.SignatureAlgorithm + return nil +} + +/** + * Encapsulate the request and invoke the network + * @param action api name + * @param version product version + * @param protocol http or https + * @param method e.g. GET + * @param authType authorization type e.g. AK + * @param bodyType response body type e.g. String + * @param request object of OpenApiRequest + * @param runtime which controls some details of call api, such as retry times + * @return the response + */ +func (client *Client) DoRPCRequest(action *string, version *string, protocol *string, method *string, authType *string, bodyType *string, request *OpenApiRequest, runtime *util.RuntimeOptions) (_result map[string]interface{}, _err error) { + _err = tea.Validate(request) + if _err != nil { + return _result, _err + } + _err = tea.Validate(runtime) + if _err != nil { + return _result, _err + } + _runtime := map[string]interface{}{ + "timeouted": "retry", + "readTimeout": tea.IntValue(util.DefaultNumber(runtime.ReadTimeout, client.ReadTimeout)), + "connectTimeout": tea.IntValue(util.DefaultNumber(runtime.ConnectTimeout, client.ConnectTimeout)), + "httpProxy": tea.StringValue(util.DefaultString(runtime.HttpProxy, client.HttpProxy)), + "httpsProxy": tea.StringValue(util.DefaultString(runtime.HttpsProxy, client.HttpsProxy)), + "noProxy": tea.StringValue(util.DefaultString(runtime.NoProxy, client.NoProxy)), + "socks5Proxy": tea.StringValue(util.DefaultString(runtime.Socks5Proxy, client.Socks5Proxy)), + "socks5NetWork": tea.StringValue(util.DefaultString(runtime.Socks5NetWork, client.Socks5NetWork)), + "maxIdleConns": tea.IntValue(util.DefaultNumber(runtime.MaxIdleConns, client.MaxIdleConns)), + "retry": map[string]interface{}{ + "retryable": tea.BoolValue(runtime.Autoretry), + "maxAttempts": tea.IntValue(util.DefaultNumber(runtime.MaxAttempts, tea.Int(3))), + }, + "backoff": map[string]interface{}{ + "policy": tea.StringValue(util.DefaultString(runtime.BackoffPolicy, tea.String("no"))), + "period": tea.IntValue(util.DefaultNumber(runtime.BackoffPeriod, tea.Int(1))), + }, + "ignoreSSL": tea.BoolValue(runtime.IgnoreSSL), + } + + _resp := make(map[string]interface{}) + for _retryTimes := 0; tea.BoolValue(tea.AllowRetry(_runtime["retry"], tea.Int(_retryTimes))); _retryTimes++ { + if _retryTimes > 0 { + _backoffTime := tea.GetBackoffTime(_runtime["backoff"], tea.Int(_retryTimes)) + if tea.IntValue(_backoffTime) > 0 { + tea.Sleep(_backoffTime) + } + } + + _resp, _err = func() (map[string]interface{}, error) { + request_ := tea.NewRequest() + request_.Protocol = util.DefaultString(client.Protocol, protocol) + request_.Method = method + request_.Pathname = tea.String("/") + request_.Query = tea.Merge(map[string]*string{ + "Action": action, + "Format": tea.String("json"), + "Version": version, + "Timestamp": openapiutil.GetTimestamp(), + "SignatureNonce": util.GetNonce(), + }, request.Query) + headers, _err := client.GetRpcHeaders() + if _err != nil { + return _result, _err + } + + if tea.BoolValue(util.IsUnset(headers)) { + // endpoint is setted in product client + request_.Headers = map[string]*string{ + "host": client.Endpoint, + "x-acs-version": version, + "x-acs-action": action, + "user-agent": client.GetUserAgent(), + } + } else { + request_.Headers = tea.Merge(map[string]*string{ + "host": client.Endpoint, + "x-acs-version": version, + "x-acs-action": action, + "user-agent": client.GetUserAgent(), + }, headers) + } + + if !tea.BoolValue(util.IsUnset(request.Body)) { + m := util.AssertAsMap(request.Body) + tmp := util.AnyifyMapValue(openapiutil.Query(m)) + request_.Body = tea.ToReader(util.ToFormString(tmp)) + request_.Headers["content-type"] = tea.String("application/x-www-form-urlencoded") + } + + if !tea.BoolValue(util.EqualString(authType, tea.String("Anonymous"))) { + accessKeyId, _err := client.GetAccessKeyId() + if _err != nil { + return _result, _err + } + + accessKeySecret, _err := client.GetAccessKeySecret() + if _err != nil { + return _result, _err + } + + securityToken, _err := client.GetSecurityToken() + if _err != nil { + return _result, _err + } + + if !tea.BoolValue(util.Empty(securityToken)) { + request_.Query["SecurityToken"] = securityToken + } + + request_.Query["SignatureMethod"] = tea.String("HMAC-SHA1") + request_.Query["SignatureVersion"] = tea.String("1.0") + request_.Query["AccessKeyId"] = accessKeyId + var t map[string]interface{} + if !tea.BoolValue(util.IsUnset(request.Body)) { + t = util.AssertAsMap(request.Body) + } + + signedParam := tea.Merge(request_.Query, + openapiutil.Query(t)) + request_.Query["Signature"] = openapiutil.GetRPCSignature(signedParam, request_.Method, accessKeySecret) + } + + response_, _err := tea.DoRequest(request_, _runtime) + if _err != nil { + return _result, _err + } + if tea.BoolValue(util.Is4xx(response_.StatusCode)) || tea.BoolValue(util.Is5xx(response_.StatusCode)) { + _res, _err := util.ReadAsJSON(response_.Body) + if _err != nil { + return _result, _err + } + + err := util.AssertAsMap(_res) + requestId := DefaultAny(err["RequestId"], err["requestId"]) + _err = tea.NewSDKError(map[string]interface{}{ + "code": tea.ToString(DefaultAny(err["Code"], err["code"])), + "message": "code: " + tea.ToString(tea.IntValue(response_.StatusCode)) + ", " + tea.ToString(DefaultAny(err["Message"], err["message"])) + " request id: " + tea.ToString(requestId), + "data": err, + }) + return _result, _err + } + + if tea.BoolValue(util.EqualString(bodyType, tea.String("binary"))) { + resp := map[string]interface{}{ + "body": response_.Body, + "headers": response_.Headers, + } + _result = resp + return _result, _err + } else if tea.BoolValue(util.EqualString(bodyType, tea.String("byte"))) { + byt, _err := util.ReadAsBytes(response_.Body) + if _err != nil { + return _result, _err + } + + _result = make(map[string]interface{}) + _err = tea.Convert(map[string]interface{}{ + "body": byt, + "headers": response_.Headers, + }, &_result) + return _result, _err + } else if tea.BoolValue(util.EqualString(bodyType, tea.String("string"))) { + str, _err := util.ReadAsString(response_.Body) + if _err != nil { + return _result, _err + } + + _result = make(map[string]interface{}) + _err = tea.Convert(map[string]interface{}{ + "body": tea.StringValue(str), + "headers": response_.Headers, + }, &_result) + return _result, _err + } else if tea.BoolValue(util.EqualString(bodyType, tea.String("json"))) { + obj, _err := util.ReadAsJSON(response_.Body) + if _err != nil { + return _result, _err + } + + res := util.AssertAsMap(obj) + _result = make(map[string]interface{}) + _err = tea.Convert(map[string]interface{}{ + "body": res, + "headers": response_.Headers, + }, &_result) + return _result, _err + } else if tea.BoolValue(util.EqualString(bodyType, tea.String("array"))) { + arr, _err := util.ReadAsJSON(response_.Body) + if _err != nil { + return _result, _err + } + + _result = make(map[string]interface{}) + _err = tea.Convert(map[string]interface{}{ + "body": arr, + "headers": response_.Headers, + }, &_result) + return _result, _err + } else { + _result = make(map[string]interface{}) + _err = tea.Convert(map[string]map[string]*string{ + "headers": response_.Headers, + }, &_result) + return _result, _err + } + + }() + if !tea.BoolValue(tea.Retryable(_err)) { + break + } + } + + return _resp, _err +} + +/** + * Encapsulate the request and invoke the network + * @param action api name + * @param version product version + * @param protocol http or https + * @param method e.g. GET + * @param authType authorization type e.g. AK + * @param pathname pathname of every api + * @param bodyType response body type e.g. String + * @param request object of OpenApiRequest + * @param runtime which controls some details of call api, such as retry times + * @return the response + */ +func (client *Client) DoROARequest(action *string, version *string, protocol *string, method *string, authType *string, pathname *string, bodyType *string, request *OpenApiRequest, runtime *util.RuntimeOptions) (_result map[string]interface{}, _err error) { + _err = tea.Validate(request) + if _err != nil { + return _result, _err + } + _err = tea.Validate(runtime) + if _err != nil { + return _result, _err + } + _runtime := map[string]interface{}{ + "timeouted": "retry", + "readTimeout": tea.IntValue(util.DefaultNumber(runtime.ReadTimeout, client.ReadTimeout)), + "connectTimeout": tea.IntValue(util.DefaultNumber(runtime.ConnectTimeout, client.ConnectTimeout)), + "httpProxy": tea.StringValue(util.DefaultString(runtime.HttpProxy, client.HttpProxy)), + "httpsProxy": tea.StringValue(util.DefaultString(runtime.HttpsProxy, client.HttpsProxy)), + "noProxy": tea.StringValue(util.DefaultString(runtime.NoProxy, client.NoProxy)), + "socks5Proxy": tea.StringValue(util.DefaultString(runtime.Socks5Proxy, client.Socks5Proxy)), + "socks5NetWork": tea.StringValue(util.DefaultString(runtime.Socks5NetWork, client.Socks5NetWork)), + "maxIdleConns": tea.IntValue(util.DefaultNumber(runtime.MaxIdleConns, client.MaxIdleConns)), + "retry": map[string]interface{}{ + "retryable": tea.BoolValue(runtime.Autoretry), + "maxAttempts": tea.IntValue(util.DefaultNumber(runtime.MaxAttempts, tea.Int(3))), + }, + "backoff": map[string]interface{}{ + "policy": tea.StringValue(util.DefaultString(runtime.BackoffPolicy, tea.String("no"))), + "period": tea.IntValue(util.DefaultNumber(runtime.BackoffPeriod, tea.Int(1))), + }, + "ignoreSSL": tea.BoolValue(runtime.IgnoreSSL), + } + + _resp := make(map[string]interface{}) + for _retryTimes := 0; tea.BoolValue(tea.AllowRetry(_runtime["retry"], tea.Int(_retryTimes))); _retryTimes++ { + if _retryTimes > 0 { + _backoffTime := tea.GetBackoffTime(_runtime["backoff"], tea.Int(_retryTimes)) + if tea.IntValue(_backoffTime) > 0 { + tea.Sleep(_backoffTime) + } + } + + _resp, _err = func() (map[string]interface{}, error) { + request_ := tea.NewRequest() + request_.Protocol = util.DefaultString(client.Protocol, protocol) + request_.Method = method + request_.Pathname = pathname + request_.Headers = tea.Merge(map[string]*string{ + "date": util.GetDateUTCString(), + "host": client.Endpoint, + "accept": tea.String("application/json"), + "x-acs-signature-nonce": util.GetNonce(), + "x-acs-signature-method": tea.String("HMAC-SHA1"), + "x-acs-signature-version": tea.String("1.0"), + "x-acs-version": version, + "x-acs-action": action, + "user-agent": util.GetUserAgent(client.UserAgent), + }, request.Headers) + if !tea.BoolValue(util.IsUnset(request.Body)) { + request_.Body = tea.ToReader(util.ToJSONString(request.Body)) + request_.Headers["content-type"] = tea.String("application/json; charset=utf-8") + } + + if !tea.BoolValue(util.IsUnset(request.Query)) { + request_.Query = request.Query + } + + if !tea.BoolValue(util.EqualString(authType, tea.String("Anonymous"))) { + accessKeyId, _err := client.GetAccessKeyId() + if _err != nil { + return _result, _err + } + + accessKeySecret, _err := client.GetAccessKeySecret() + if _err != nil { + return _result, _err + } + + securityToken, _err := client.GetSecurityToken() + if _err != nil { + return _result, _err + } + + if !tea.BoolValue(util.Empty(securityToken)) { + request_.Headers["x-acs-accesskey-id"] = accessKeyId + request_.Headers["x-acs-security-token"] = securityToken + } + + stringToSign := openapiutil.GetStringToSign(request_) + request_.Headers["authorization"] = tea.String("acs " + tea.StringValue(accessKeyId) + ":" + tea.StringValue(openapiutil.GetROASignature(stringToSign, accessKeySecret))) + } + + response_, _err := tea.DoRequest(request_, _runtime) + if _err != nil { + return _result, _err + } + if tea.BoolValue(util.EqualNumber(response_.StatusCode, tea.Int(204))) { + _result = make(map[string]interface{}) + _err = tea.Convert(map[string]map[string]*string{ + "headers": response_.Headers, + }, &_result) + return _result, _err + } + + if tea.BoolValue(util.Is4xx(response_.StatusCode)) || tea.BoolValue(util.Is5xx(response_.StatusCode)) { + _res, _err := util.ReadAsJSON(response_.Body) + if _err != nil { + return _result, _err + } + + err := util.AssertAsMap(_res) + requestId := DefaultAny(err["RequestId"], err["requestId"]) + requestId = DefaultAny(requestId, err["requestid"]) + _err = tea.NewSDKError(map[string]interface{}{ + "code": tea.ToString(DefaultAny(err["Code"], err["code"])), + "message": "code: " + tea.ToString(tea.IntValue(response_.StatusCode)) + ", " + tea.ToString(DefaultAny(err["Message"], err["message"])) + " request id: " + tea.ToString(requestId), + "data": err, + }) + return _result, _err + } + + if tea.BoolValue(util.EqualString(bodyType, tea.String("binary"))) { + resp := map[string]interface{}{ + "body": response_.Body, + "headers": response_.Headers, + } + _result = resp + return _result, _err + } else if tea.BoolValue(util.EqualString(bodyType, tea.String("byte"))) { + byt, _err := util.ReadAsBytes(response_.Body) + if _err != nil { + return _result, _err + } + + _result = make(map[string]interface{}) + _err = tea.Convert(map[string]interface{}{ + "body": byt, + "headers": response_.Headers, + }, &_result) + return _result, _err + } else if tea.BoolValue(util.EqualString(bodyType, tea.String("string"))) { + str, _err := util.ReadAsString(response_.Body) + if _err != nil { + return _result, _err + } + + _result = make(map[string]interface{}) + _err = tea.Convert(map[string]interface{}{ + "body": tea.StringValue(str), + "headers": response_.Headers, + }, &_result) + return _result, _err + } else if tea.BoolValue(util.EqualString(bodyType, tea.String("json"))) { + obj, _err := util.ReadAsJSON(response_.Body) + if _err != nil { + return _result, _err + } + + res := util.AssertAsMap(obj) + _result = make(map[string]interface{}) + _err = tea.Convert(map[string]interface{}{ + "body": res, + "headers": response_.Headers, + }, &_result) + return _result, _err + } else if tea.BoolValue(util.EqualString(bodyType, tea.String("array"))) { + arr, _err := util.ReadAsJSON(response_.Body) + if _err != nil { + return _result, _err + } + + _result = make(map[string]interface{}) + _err = tea.Convert(map[string]interface{}{ + "body": arr, + "headers": response_.Headers, + }, &_result) + return _result, _err + } else { + _result = make(map[string]interface{}) + _err = tea.Convert(map[string]map[string]*string{ + "headers": response_.Headers, + }, &_result) + return _result, _err + } + + }() + if !tea.BoolValue(tea.Retryable(_err)) { + break + } + } + + return _resp, _err +} + +/** + * Encapsulate the request and invoke the network with form body + * @param action api name + * @param version product version + * @param protocol http or https + * @param method e.g. GET + * @param authType authorization type e.g. AK + * @param pathname pathname of every api + * @param bodyType response body type e.g. String + * @param request object of OpenApiRequest + * @param runtime which controls some details of call api, such as retry times + * @return the response + */ +func (client *Client) DoROARequestWithForm(action *string, version *string, protocol *string, method *string, authType *string, pathname *string, bodyType *string, request *OpenApiRequest, runtime *util.RuntimeOptions) (_result map[string]interface{}, _err error) { + _err = tea.Validate(request) + if _err != nil { + return _result, _err + } + _err = tea.Validate(runtime) + if _err != nil { + return _result, _err + } + _runtime := map[string]interface{}{ + "timeouted": "retry", + "readTimeout": tea.IntValue(util.DefaultNumber(runtime.ReadTimeout, client.ReadTimeout)), + "connectTimeout": tea.IntValue(util.DefaultNumber(runtime.ConnectTimeout, client.ConnectTimeout)), + "httpProxy": tea.StringValue(util.DefaultString(runtime.HttpProxy, client.HttpProxy)), + "httpsProxy": tea.StringValue(util.DefaultString(runtime.HttpsProxy, client.HttpsProxy)), + "noProxy": tea.StringValue(util.DefaultString(runtime.NoProxy, client.NoProxy)), + "socks5Proxy": tea.StringValue(util.DefaultString(runtime.Socks5Proxy, client.Socks5Proxy)), + "socks5NetWork": tea.StringValue(util.DefaultString(runtime.Socks5NetWork, client.Socks5NetWork)), + "maxIdleConns": tea.IntValue(util.DefaultNumber(runtime.MaxIdleConns, client.MaxIdleConns)), + "retry": map[string]interface{}{ + "retryable": tea.BoolValue(runtime.Autoretry), + "maxAttempts": tea.IntValue(util.DefaultNumber(runtime.MaxAttempts, tea.Int(3))), + }, + "backoff": map[string]interface{}{ + "policy": tea.StringValue(util.DefaultString(runtime.BackoffPolicy, tea.String("no"))), + "period": tea.IntValue(util.DefaultNumber(runtime.BackoffPeriod, tea.Int(1))), + }, + "ignoreSSL": tea.BoolValue(runtime.IgnoreSSL), + } + + _resp := make(map[string]interface{}) + for _retryTimes := 0; tea.BoolValue(tea.AllowRetry(_runtime["retry"], tea.Int(_retryTimes))); _retryTimes++ { + if _retryTimes > 0 { + _backoffTime := tea.GetBackoffTime(_runtime["backoff"], tea.Int(_retryTimes)) + if tea.IntValue(_backoffTime) > 0 { + tea.Sleep(_backoffTime) + } + } + + _resp, _err = func() (map[string]interface{}, error) { + request_ := tea.NewRequest() + request_.Protocol = util.DefaultString(client.Protocol, protocol) + request_.Method = method + request_.Pathname = pathname + request_.Headers = tea.Merge(map[string]*string{ + "date": util.GetDateUTCString(), + "host": client.Endpoint, + "accept": tea.String("application/json"), + "x-acs-signature-nonce": util.GetNonce(), + "x-acs-signature-method": tea.String("HMAC-SHA1"), + "x-acs-signature-version": tea.String("1.0"), + "x-acs-version": version, + "x-acs-action": action, + "user-agent": util.GetUserAgent(client.UserAgent), + }, request.Headers) + if !tea.BoolValue(util.IsUnset(request.Body)) { + m := util.AssertAsMap(request.Body) + request_.Body = tea.ToReader(openapiutil.ToForm(m)) + request_.Headers["content-type"] = tea.String("application/x-www-form-urlencoded") + } + + if !tea.BoolValue(util.IsUnset(request.Query)) { + request_.Query = request.Query + } + + if !tea.BoolValue(util.EqualString(authType, tea.String("Anonymous"))) { + accessKeyId, _err := client.GetAccessKeyId() + if _err != nil { + return _result, _err + } + + accessKeySecret, _err := client.GetAccessKeySecret() + if _err != nil { + return _result, _err + } + + securityToken, _err := client.GetSecurityToken() + if _err != nil { + return _result, _err + } + + if !tea.BoolValue(util.Empty(securityToken)) { + request_.Headers["x-acs-accesskey-id"] = accessKeyId + request_.Headers["x-acs-security-token"] = securityToken + } + + stringToSign := openapiutil.GetStringToSign(request_) + request_.Headers["authorization"] = tea.String("acs " + tea.StringValue(accessKeyId) + ":" + tea.StringValue(openapiutil.GetROASignature(stringToSign, accessKeySecret))) + } + + response_, _err := tea.DoRequest(request_, _runtime) + if _err != nil { + return _result, _err + } + if tea.BoolValue(util.EqualNumber(response_.StatusCode, tea.Int(204))) { + _result = make(map[string]interface{}) + _err = tea.Convert(map[string]map[string]*string{ + "headers": response_.Headers, + }, &_result) + return _result, _err + } + + if tea.BoolValue(util.Is4xx(response_.StatusCode)) || tea.BoolValue(util.Is5xx(response_.StatusCode)) { + _res, _err := util.ReadAsJSON(response_.Body) + if _err != nil { + return _result, _err + } + + err := util.AssertAsMap(_res) + _err = tea.NewSDKError(map[string]interface{}{ + "code": tea.ToString(DefaultAny(err["Code"], err["code"])), + "message": "code: " + tea.ToString(tea.IntValue(response_.StatusCode)) + ", " + tea.ToString(DefaultAny(err["Message"], err["message"])) + " request id: " + tea.ToString(DefaultAny(err["RequestId"], err["requestId"])), + "data": err, + }) + return _result, _err + } + + if tea.BoolValue(util.EqualString(bodyType, tea.String("binary"))) { + resp := map[string]interface{}{ + "body": response_.Body, + "headers": response_.Headers, + } + _result = resp + return _result, _err + } else if tea.BoolValue(util.EqualString(bodyType, tea.String("byte"))) { + byt, _err := util.ReadAsBytes(response_.Body) + if _err != nil { + return _result, _err + } + + _result = make(map[string]interface{}) + _err = tea.Convert(map[string]interface{}{ + "body": byt, + "headers": response_.Headers, + }, &_result) + return _result, _err + } else if tea.BoolValue(util.EqualString(bodyType, tea.String("string"))) { + str, _err := util.ReadAsString(response_.Body) + if _err != nil { + return _result, _err + } + + _result = make(map[string]interface{}) + _err = tea.Convert(map[string]interface{}{ + "body": tea.StringValue(str), + "headers": response_.Headers, + }, &_result) + return _result, _err + } else if tea.BoolValue(util.EqualString(bodyType, tea.String("json"))) { + obj, _err := util.ReadAsJSON(response_.Body) + if _err != nil { + return _result, _err + } + + res := util.AssertAsMap(obj) + _result = make(map[string]interface{}) + _err = tea.Convert(map[string]interface{}{ + "body": res, + "headers": response_.Headers, + }, &_result) + return _result, _err + } else if tea.BoolValue(util.EqualString(bodyType, tea.String("array"))) { + arr, _err := util.ReadAsJSON(response_.Body) + if _err != nil { + return _result, _err + } + + _result = make(map[string]interface{}) + _err = tea.Convert(map[string]interface{}{ + "body": arr, + "headers": response_.Headers, + }, &_result) + return _result, _err + } else { + _result = make(map[string]interface{}) + _err = tea.Convert(map[string]map[string]*string{ + "headers": response_.Headers, + }, &_result) + return _result, _err + } + + }() + if !tea.BoolValue(tea.Retryable(_err)) { + break + } + } + + return _resp, _err +} + +/** + * Encapsulate the request and invoke the network + * @param action api name + * @param version product version + * @param protocol http or https + * @param method e.g. GET + * @param authType authorization type e.g. AK + * @param bodyType response body type e.g. String + * @param request object of OpenApiRequest + * @param runtime which controls some details of call api, such as retry times + * @return the response + */ +func (client *Client) DoRequest(params *Params, request *OpenApiRequest, runtime *util.RuntimeOptions) (_result map[string]interface{}, _err error) { + _err = tea.Validate(params) + if _err != nil { + return _result, _err + } + _err = tea.Validate(request) + if _err != nil { + return _result, _err + } + _err = tea.Validate(runtime) + if _err != nil { + return _result, _err + } + _runtime := map[string]interface{}{ + "timeouted": "retry", + "readTimeout": tea.IntValue(util.DefaultNumber(runtime.ReadTimeout, client.ReadTimeout)), + "connectTimeout": tea.IntValue(util.DefaultNumber(runtime.ConnectTimeout, client.ConnectTimeout)), + "httpProxy": tea.StringValue(util.DefaultString(runtime.HttpProxy, client.HttpProxy)), + "httpsProxy": tea.StringValue(util.DefaultString(runtime.HttpsProxy, client.HttpsProxy)), + "noProxy": tea.StringValue(util.DefaultString(runtime.NoProxy, client.NoProxy)), + "socks5Proxy": tea.StringValue(util.DefaultString(runtime.Socks5Proxy, client.Socks5Proxy)), + "socks5NetWork": tea.StringValue(util.DefaultString(runtime.Socks5NetWork, client.Socks5NetWork)), + "maxIdleConns": tea.IntValue(util.DefaultNumber(runtime.MaxIdleConns, client.MaxIdleConns)), + "retry": map[string]interface{}{ + "retryable": tea.BoolValue(runtime.Autoretry), + "maxAttempts": tea.IntValue(util.DefaultNumber(runtime.MaxAttempts, tea.Int(3))), + }, + "backoff": map[string]interface{}{ + "policy": tea.StringValue(util.DefaultString(runtime.BackoffPolicy, tea.String("no"))), + "period": tea.IntValue(util.DefaultNumber(runtime.BackoffPeriod, tea.Int(1))), + }, + "ignoreSSL": tea.BoolValue(runtime.IgnoreSSL), + } + + _resp := make(map[string]interface{}) + for _retryTimes := 0; tea.BoolValue(tea.AllowRetry(_runtime["retry"], tea.Int(_retryTimes))); _retryTimes++ { + if _retryTimes > 0 { + _backoffTime := tea.GetBackoffTime(_runtime["backoff"], tea.Int(_retryTimes)) + if tea.IntValue(_backoffTime) > 0 { + tea.Sleep(_backoffTime) + } + } + + _resp, _err = func() (map[string]interface{}, error) { + request_ := tea.NewRequest() + request_.Protocol = util.DefaultString(client.Protocol, params.Protocol) + request_.Method = params.Method + request_.Pathname = params.Pathname + request_.Query = request.Query + // endpoint is setted in product client + request_.Headers = tea.Merge(map[string]*string{ + "host": client.Endpoint, + "x-acs-version": params.Version, + "x-acs-action": params.Action, + "user-agent": client.GetUserAgent(), + "x-acs-date": openapiutil.GetTimestamp(), + "x-acs-signature-nonce": util.GetNonce(), + "accept": tea.String("application/json"), + }, request.Headers) + if tea.BoolValue(util.EqualString(params.Style, tea.String("RPC"))) { + headers, _err := client.GetRpcHeaders() + if _err != nil { + return _result, _err + } + + if !tea.BoolValue(util.IsUnset(headers)) { + request_.Headers = tea.Merge(request_.Headers, + headers) + } + + } + + signatureAlgorithm := util.DefaultString(client.SignatureAlgorithm, tea.String("ACS3-HMAC-SHA256")) + hashedRequestPayload := openapiutil.HexEncode(openapiutil.Hash(util.ToBytes(tea.String("")), signatureAlgorithm)) + if !tea.BoolValue(util.IsUnset(request.Stream)) { + tmp, _err := util.ReadAsBytes(request.Stream) + if _err != nil { + return _result, _err + } + + hashedRequestPayload = openapiutil.HexEncode(openapiutil.Hash(tmp, signatureAlgorithm)) + request_.Body = tea.ToReader(tmp) + request_.Headers["content-type"] = tea.String("application/octet-stream") + } else { + if !tea.BoolValue(util.IsUnset(request.Body)) { + if tea.BoolValue(util.EqualString(params.ReqBodyType, tea.String("json"))) { + jsonObj := util.ToJSONString(request.Body) + hashedRequestPayload = openapiutil.HexEncode(openapiutil.Hash(util.ToBytes(jsonObj), signatureAlgorithm)) + request_.Body = tea.ToReader(jsonObj) + request_.Headers["content-type"] = tea.String("application/json; charset=utf-8") + } else { + m := util.AssertAsMap(request.Body) + formObj := openapiutil.ToForm(m) + hashedRequestPayload = openapiutil.HexEncode(openapiutil.Hash(util.ToBytes(formObj), signatureAlgorithm)) + request_.Body = tea.ToReader(formObj) + request_.Headers["content-type"] = tea.String("application/x-www-form-urlencoded") + } + + } + + } + + request_.Headers["x-acs-content-sha256"] = hashedRequestPayload + if !tea.BoolValue(util.EqualString(params.AuthType, tea.String("Anonymous"))) { + authType, _err := client.GetType() + if _err != nil { + return _result, _err + } + + if tea.BoolValue(util.EqualString(authType, tea.String("bearer"))) { + bearerToken, _err := client.GetBearerToken() + if _err != nil { + return _result, _err + } + + request_.Headers["x-acs-bearer-token"] = bearerToken + } else { + accessKeyId, _err := client.GetAccessKeyId() + if _err != nil { + return _result, _err + } + + accessKeySecret, _err := client.GetAccessKeySecret() + if _err != nil { + return _result, _err + } + + securityToken, _err := client.GetSecurityToken() + if _err != nil { + return _result, _err + } + + if !tea.BoolValue(util.Empty(securityToken)) { + request_.Headers["x-acs-accesskey-id"] = accessKeyId + request_.Headers["x-acs-security-token"] = securityToken + } + + request_.Headers["Authorization"] = openapiutil.GetAuthorization(request_, signatureAlgorithm, hashedRequestPayload, accessKeyId, accessKeySecret) + } + + } + + response_, _err := tea.DoRequest(request_, _runtime) + if _err != nil { + return _result, _err + } + if tea.BoolValue(util.Is4xx(response_.StatusCode)) || tea.BoolValue(util.Is5xx(response_.StatusCode)) { + err := map[string]interface{}{} + if !tea.BoolValue(util.IsUnset(response_.Headers["content-type"])) && tea.BoolValue(util.EqualString(response_.Headers["content-type"], tea.String("text/xml;charset=utf-8"))) { + _str, _err := util.ReadAsString(response_.Body) + if _err != nil { + return _result, _err + } + + respMap := xml.ParseXml(_str, nil) + err = util.AssertAsMap(respMap["Error"]) + } else { + _res, _err := util.ReadAsJSON(response_.Body) + if _err != nil { + return _result, _err + } + + err = util.AssertAsMap(_res) + } + + err["statusCode"] = response_.StatusCode + _err = tea.NewSDKError(map[string]interface{}{ + "code": tea.ToString(DefaultAny(err["Code"], err["code"])), + "message": "code: " + tea.ToString(tea.IntValue(response_.StatusCode)) + ", " + tea.ToString(DefaultAny(err["Message"], err["message"])) + " request id: " + tea.ToString(DefaultAny(err["RequestId"], err["requestId"])), + "data": err, + }) + return _result, _err + } + + if tea.BoolValue(util.EqualString(params.BodyType, tea.String("binary"))) { + resp := map[string]interface{}{ + "body": response_.Body, + "headers": response_.Headers, + } + _result = resp + return _result, _err + } else if tea.BoolValue(util.EqualString(params.BodyType, tea.String("byte"))) { + byt, _err := util.ReadAsBytes(response_.Body) + if _err != nil { + return _result, _err + } + + _result = make(map[string]interface{}) + _err = tea.Convert(map[string]interface{}{ + "body": byt, + "headers": response_.Headers, + }, &_result) + return _result, _err + } else if tea.BoolValue(util.EqualString(params.BodyType, tea.String("string"))) { + str, _err := util.ReadAsString(response_.Body) + if _err != nil { + return _result, _err + } + + _result = make(map[string]interface{}) + _err = tea.Convert(map[string]interface{}{ + "body": tea.StringValue(str), + "headers": response_.Headers, + }, &_result) + return _result, _err + } else if tea.BoolValue(util.EqualString(params.BodyType, tea.String("json"))) { + obj, _err := util.ReadAsJSON(response_.Body) + if _err != nil { + return _result, _err + } + + res := util.AssertAsMap(obj) + _result = make(map[string]interface{}) + _err = tea.Convert(map[string]interface{}{ + "body": res, + "headers": response_.Headers, + }, &_result) + return _result, _err + } else if tea.BoolValue(util.EqualString(params.BodyType, tea.String("array"))) { + arr, _err := util.ReadAsJSON(response_.Body) + if _err != nil { + return _result, _err + } + + _result = make(map[string]interface{}) + _err = tea.Convert(map[string]interface{}{ + "body": arr, + "headers": response_.Headers, + }, &_result) + return _result, _err + } else { + _result = make(map[string]interface{}) + _err = tea.Convert(map[string]map[string]*string{ + "headers": response_.Headers, + }, &_result) + return _result, _err + } + + }() + if !tea.BoolValue(tea.Retryable(_err)) { + break + } + } + + return _resp, _err +} + +/** + * Encapsulate the request and invoke the network + * @param action api name + * @param version product version + * @param protocol http or https + * @param method e.g. GET + * @param authType authorization type e.g. AK + * @param bodyType response body type e.g. String + * @param request object of OpenApiRequest + * @param runtime which controls some details of call api, such as retry times + * @return the response + */ +func (client *Client) Execute(params *Params, request *OpenApiRequest, runtime *util.RuntimeOptions) (_result map[string]interface{}, _err error) { + _err = tea.Validate(params) + if _err != nil { + return _result, _err + } + _err = tea.Validate(request) + if _err != nil { + return _result, _err + } + _err = tea.Validate(runtime) + if _err != nil { + return _result, _err + } + _runtime := map[string]interface{}{ + "timeouted": "retry", + "readTimeout": tea.IntValue(util.DefaultNumber(runtime.ReadTimeout, client.ReadTimeout)), + "connectTimeout": tea.IntValue(util.DefaultNumber(runtime.ConnectTimeout, client.ConnectTimeout)), + "httpProxy": tea.StringValue(util.DefaultString(runtime.HttpProxy, client.HttpProxy)), + "httpsProxy": tea.StringValue(util.DefaultString(runtime.HttpsProxy, client.HttpsProxy)), + "noProxy": tea.StringValue(util.DefaultString(runtime.NoProxy, client.NoProxy)), + "socks5Proxy": tea.StringValue(util.DefaultString(runtime.Socks5Proxy, client.Socks5Proxy)), + "socks5NetWork": tea.StringValue(util.DefaultString(runtime.Socks5NetWork, client.Socks5NetWork)), + "maxIdleConns": tea.IntValue(util.DefaultNumber(runtime.MaxIdleConns, client.MaxIdleConns)), + "retry": map[string]interface{}{ + "retryable": tea.BoolValue(runtime.Autoretry), + "maxAttempts": tea.IntValue(util.DefaultNumber(runtime.MaxAttempts, tea.Int(3))), + }, + "backoff": map[string]interface{}{ + "policy": tea.StringValue(util.DefaultString(runtime.BackoffPolicy, tea.String("no"))), + "period": tea.IntValue(util.DefaultNumber(runtime.BackoffPeriod, tea.Int(1))), + }, + "ignoreSSL": tea.BoolValue(runtime.IgnoreSSL), + } + + _resp := make(map[string]interface{}) + for _retryTimes := 0; tea.BoolValue(tea.AllowRetry(_runtime["retry"], tea.Int(_retryTimes))); _retryTimes++ { + if _retryTimes > 0 { + _backoffTime := tea.GetBackoffTime(_runtime["backoff"], tea.Int(_retryTimes)) + if tea.IntValue(_backoffTime) > 0 { + tea.Sleep(_backoffTime) + } + } + + _resp, _err = func() (map[string]interface{}, error) { + request_ := tea.NewRequest() + // spi = new Gateway();//Gateway implements SPI,这一步在产品 SDK 中实例化 + headers, _err := client.GetRpcHeaders() + if _err != nil { + return _result, _err + } + + requestContext := &spi.InterceptorContextRequest{ + Headers: tea.Merge(request.Headers, + headers), + Query: request.Query, + Body: request.Body, + Stream: request.Stream, + HostMap: request.HostMap, + Pathname: params.Pathname, + ProductId: client.ProductId, + Action: params.Action, + Version: params.Version, + Protocol: util.DefaultString(client.Protocol, params.Protocol), + Method: util.DefaultString(client.Method, params.Method), + AuthType: params.AuthType, + BodyType: params.BodyType, + ReqBodyType: params.ReqBodyType, + Style: params.Style, + Credential: client.Credential, + SignatureVersion: client.SignatureVersion, + SignatureAlgorithm: client.SignatureAlgorithm, + UserAgent: client.GetUserAgent(), + } + configurationContext := &spi.InterceptorContextConfiguration{ + RegionId: client.RegionId, + Endpoint: util.DefaultString(request.EndpointOverride, client.Endpoint), + EndpointRule: client.EndpointRule, + EndpointMap: client.EndpointMap, + EndpointType: client.EndpointType, + Network: client.Network, + Suffix: client.Suffix, + } + interceptorContext := &spi.InterceptorContext{ + Request: requestContext, + Configuration: configurationContext, + } + attributeMap := &spi.AttributeMap{} + // 1. spi.modifyConfiguration(context: SPI.InterceptorContext, attributeMap: SPI.AttributeMap); + _err = client.Spi.ModifyConfiguration(interceptorContext, attributeMap) + if _err != nil { + return _result, _err + } + // 2. spi.modifyRequest(context: SPI.InterceptorContext, attributeMap: SPI.AttributeMap); + _err = client.Spi.ModifyRequest(interceptorContext, attributeMap) + if _err != nil { + return _result, _err + } + request_.Protocol = interceptorContext.Request.Protocol + request_.Method = interceptorContext.Request.Method + request_.Pathname = interceptorContext.Request.Pathname + request_.Query = interceptorContext.Request.Query + request_.Body = interceptorContext.Request.Stream + request_.Headers = interceptorContext.Request.Headers + response_, _err := tea.DoRequest(request_, _runtime) + if _err != nil { + return _result, _err + } + responseContext := &spi.InterceptorContextResponse{ + StatusCode: response_.StatusCode, + Headers: response_.Headers, + Body: response_.Body, + } + interceptorContext.Response = responseContext + // 3. spi.modifyResponse(context: SPI.InterceptorContext, attributeMap: SPI.AttributeMap); + _err = client.Spi.ModifyResponse(interceptorContext, attributeMap) + if _err != nil { + return _result, _err + } + _result = make(map[string]interface{}) + _err = tea.Convert(map[string]interface{}{ + "headers": interceptorContext.Response.Headers, + "body": interceptorContext.Response.DeserializedBody, + }, &_result) + return _result, _err + }() + if !tea.BoolValue(tea.Retryable(_err)) { + break + } + } + + return _resp, _err +} + +func (client *Client) CallApi(params *Params, request *OpenApiRequest, runtime *util.RuntimeOptions) (_result map[string]interface{}, _err error) { + if tea.BoolValue(util.IsUnset(tea.ToMap(params))) { + _err = tea.NewSDKError(map[string]interface{}{ + "code": "ParameterMissing", + "message": "'params' can not be unset", + }) + return _result, _err + } + + if tea.BoolValue(util.IsUnset(client.SignatureAlgorithm)) || !tea.BoolValue(util.EqualString(client.SignatureAlgorithm, tea.String("v2"))) { + _result = make(map[string]interface{}) + _body, _err := client.DoRequest(params, request, runtime) + if _err != nil { + return _result, _err + } + _result = _body + return _result, _err + } else if tea.BoolValue(util.EqualString(params.Style, tea.String("ROA"))) && tea.BoolValue(util.EqualString(params.ReqBodyType, tea.String("json"))) { + _result = make(map[string]interface{}) + _body, _err := client.DoROARequest(params.Action, params.Version, params.Protocol, params.Method, params.AuthType, params.Pathname, params.BodyType, request, runtime) + if _err != nil { + return _result, _err + } + _result = _body + return _result, _err + } else if tea.BoolValue(util.EqualString(params.Style, tea.String("ROA"))) { + _result = make(map[string]interface{}) + _body, _err := client.DoROARequestWithForm(params.Action, params.Version, params.Protocol, params.Method, params.AuthType, params.Pathname, params.BodyType, request, runtime) + if _err != nil { + return _result, _err + } + _result = _body + return _result, _err + } else { + _result = make(map[string]interface{}) + _body, _err := client.DoRPCRequest(params.Action, params.Version, params.Protocol, params.Method, params.AuthType, params.BodyType, request, runtime) + if _err != nil { + return _result, _err + } + _result = _body + return _result, _err + } + +} + +/** + * Get user agent + * @return user agent + */ +func (client *Client) GetUserAgent() (_result *string) { + userAgent := util.GetUserAgent(client.UserAgent) + _result = userAgent + return _result +} + +/** + * Get accesskey id by using credential + * @return accesskey id + */ +func (client *Client) GetAccessKeyId() (_result *string, _err error) { + if tea.BoolValue(util.IsUnset(client.Credential)) { + _result = tea.String("") + return _result, _err + } + + accessKeyId, _err := client.Credential.GetAccessKeyId() + if _err != nil { + return _result, _err + } + + _result = accessKeyId + return _result, _err +} + +/** + * Get accesskey secret by using credential + * @return accesskey secret + */ +func (client *Client) GetAccessKeySecret() (_result *string, _err error) { + if tea.BoolValue(util.IsUnset(client.Credential)) { + _result = tea.String("") + return _result, _err + } + + secret, _err := client.Credential.GetAccessKeySecret() + if _err != nil { + return _result, _err + } + + _result = secret + return _result, _err +} + +/** + * Get security token by using credential + * @return security token + */ +func (client *Client) GetSecurityToken() (_result *string, _err error) { + if tea.BoolValue(util.IsUnset(client.Credential)) { + _result = tea.String("") + return _result, _err + } + + token, _err := client.Credential.GetSecurityToken() + if _err != nil { + return _result, _err + } + + _result = token + return _result, _err +} + +/** + * Get bearer token by credential + * @return bearer token + */ +func (client *Client) GetBearerToken() (_result *string, _err error) { + if tea.BoolValue(util.IsUnset(client.Credential)) { + _result = tea.String("") + return _result, _err + } + + token := client.Credential.GetBearerToken() + _result = token + return _result, _err +} + +/** + * Get credential type by credential + * @return credential type e.g. access_key + */ +func (client *Client) GetType() (_result *string, _err error) { + if tea.BoolValue(util.IsUnset(client.Credential)) { + _result = tea.String("") + return _result, _err + } + + authType := client.Credential.GetType() + _result = authType + return _result, _err +} + +/** + * If inputValue is not null, return it or return defaultValue + * @param inputValue users input value + * @param defaultValue default value + * @return the final result + */ +func DefaultAny(inputValue interface{}, defaultValue interface{}) (_result interface{}) { + if tea.BoolValue(util.IsUnset(inputValue)) { + _result = defaultValue + return _result + } + + _result = inputValue + return _result +} + +/** + * If the endpointRule and config.endpoint are empty, throw error + * @param config config contains the necessary information to create a client + */ +func (client *Client) CheckConfig(config *Config) (_err error) { + if tea.BoolValue(util.Empty(client.EndpointRule)) && tea.BoolValue(util.Empty(config.Endpoint)) { + _err = tea.NewSDKError(map[string]interface{}{ + "code": "ParameterMissing", + "message": "'config.endpoint' can not be empty", + }) + return _err + } + + return _err +} + +/** + * set RPC header for debug + * @param headers headers for debug, this header can be used only once. + */ +func (client *Client) SetRpcHeaders(headers map[string]*string) (_err error) { + client.Headers = headers + return _err +} + +/** + * get RPC header for debug + */ +func (client *Client) GetRpcHeaders() (_result map[string]*string, _err error) { + headers := client.Headers + client.Headers = nil + _result = headers + return _result, _err +} diff --git a/vendor/github.com/alibabacloud-go/debug/LICENSE b/vendor/github.com/alibabacloud-go/debug/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/vendor/github.com/alibabacloud-go/debug/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/alibabacloud-go/debug/debug/assert.go b/vendor/github.com/alibabacloud-go/debug/debug/assert.go new file mode 100644 index 000000000..6fca15a63 --- /dev/null +++ b/vendor/github.com/alibabacloud-go/debug/debug/assert.go @@ -0,0 +1,12 @@ +package debug + +import ( + "reflect" + "testing" +) + +func assertEqual(t *testing.T, a, b interface{}) { + if !reflect.DeepEqual(a, b) { + t.Errorf("%v != %v", a, b) + } +} diff --git a/vendor/github.com/alibabacloud-go/debug/debug/debug.go b/vendor/github.com/alibabacloud-go/debug/debug/debug.go new file mode 100644 index 000000000..c977cb8c3 --- /dev/null +++ b/vendor/github.com/alibabacloud-go/debug/debug/debug.go @@ -0,0 +1,36 @@ +package debug + +import ( + "fmt" + "os" + "strings" +) + +type Debug func(format string, v ...interface{}) + +var hookGetEnv = func() string { + return os.Getenv("DEBUG") +} + +var hookPrint = func(input string) { + fmt.Println(input) +} + +func Init(flag string) Debug { + enable := false + + env := hookGetEnv() + parts := strings.Split(env, ",") + for _, part := range parts { + if part == flag { + enable = true + break + } + } + + return func(format string, v ...interface{}) { + if enable { + hookPrint(fmt.Sprintf(format, v...)) + } + } +} diff --git a/vendor/github.com/alibabacloud-go/dysmsapi-20170525/v2/client/client.go b/vendor/github.com/alibabacloud-go/dysmsapi-20170525/v2/client/client.go new file mode 100644 index 000000000..a5f0a894a --- /dev/null +++ b/vendor/github.com/alibabacloud-go/dysmsapi-20170525/v2/client/client.go @@ -0,0 +1,4124 @@ +// This file is auto-generated, don't edit it. Thanks. +/** + * + */ +package client + +import ( + openapi "github.com/alibabacloud-go/darabonba-openapi/client" + endpointutil "github.com/alibabacloud-go/endpoint-util/service" + openapiutil "github.com/alibabacloud-go/openapi-util/service" + util "github.com/alibabacloud-go/tea-utils/service" + "github.com/alibabacloud-go/tea/tea" +) + +type AddShortUrlRequest struct { + EffectiveDays *string `json:"EffectiveDays,omitempty" xml:"EffectiveDays,omitempty"` + OwnerId *int64 `json:"OwnerId,omitempty" xml:"OwnerId,omitempty"` + ResourceOwnerAccount *string `json:"ResourceOwnerAccount,omitempty" xml:"ResourceOwnerAccount,omitempty"` + ResourceOwnerId *int64 `json:"ResourceOwnerId,omitempty" xml:"ResourceOwnerId,omitempty"` + ShortUrlName *string `json:"ShortUrlName,omitempty" xml:"ShortUrlName,omitempty"` + SourceUrl *string `json:"SourceUrl,omitempty" xml:"SourceUrl,omitempty"` +} + +func (s AddShortUrlRequest) String() string { + return tea.Prettify(s) +} + +func (s AddShortUrlRequest) GoString() string { + return s.String() +} + +func (s *AddShortUrlRequest) SetEffectiveDays(v string) *AddShortUrlRequest { + s.EffectiveDays = &v + return s +} + +func (s *AddShortUrlRequest) SetOwnerId(v int64) *AddShortUrlRequest { + s.OwnerId = &v + return s +} + +func (s *AddShortUrlRequest) SetResourceOwnerAccount(v string) *AddShortUrlRequest { + s.ResourceOwnerAccount = &v + return s +} + +func (s *AddShortUrlRequest) SetResourceOwnerId(v int64) *AddShortUrlRequest { + s.ResourceOwnerId = &v + return s +} + +func (s *AddShortUrlRequest) SetShortUrlName(v string) *AddShortUrlRequest { + s.ShortUrlName = &v + return s +} + +func (s *AddShortUrlRequest) SetSourceUrl(v string) *AddShortUrlRequest { + s.SourceUrl = &v + return s +} + +type AddShortUrlResponseBody struct { + Code *string `json:"Code,omitempty" xml:"Code,omitempty"` + Data *AddShortUrlResponseBodyData `json:"Data,omitempty" xml:"Data,omitempty" type:"Struct"` + Message *string `json:"Message,omitempty" xml:"Message,omitempty"` + RequestId *string `json:"RequestId,omitempty" xml:"RequestId,omitempty"` +} + +func (s AddShortUrlResponseBody) String() string { + return tea.Prettify(s) +} + +func (s AddShortUrlResponseBody) GoString() string { + return s.String() +} + +func (s *AddShortUrlResponseBody) SetCode(v string) *AddShortUrlResponseBody { + s.Code = &v + return s +} + +func (s *AddShortUrlResponseBody) SetData(v *AddShortUrlResponseBodyData) *AddShortUrlResponseBody { + s.Data = v + return s +} + +func (s *AddShortUrlResponseBody) SetMessage(v string) *AddShortUrlResponseBody { + s.Message = &v + return s +} + +func (s *AddShortUrlResponseBody) SetRequestId(v string) *AddShortUrlResponseBody { + s.RequestId = &v + return s +} + +type AddShortUrlResponseBodyData struct { + ExpireDate *string `json:"ExpireDate,omitempty" xml:"ExpireDate,omitempty"` + ShortUrl *string `json:"ShortUrl,omitempty" xml:"ShortUrl,omitempty"` + SourceUrl *string `json:"SourceUrl,omitempty" xml:"SourceUrl,omitempty"` +} + +func (s AddShortUrlResponseBodyData) String() string { + return tea.Prettify(s) +} + +func (s AddShortUrlResponseBodyData) GoString() string { + return s.String() +} + +func (s *AddShortUrlResponseBodyData) SetExpireDate(v string) *AddShortUrlResponseBodyData { + s.ExpireDate = &v + return s +} + +func (s *AddShortUrlResponseBodyData) SetShortUrl(v string) *AddShortUrlResponseBodyData { + s.ShortUrl = &v + return s +} + +func (s *AddShortUrlResponseBodyData) SetSourceUrl(v string) *AddShortUrlResponseBodyData { + s.SourceUrl = &v + return s +} + +type AddShortUrlResponse struct { + Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty" require:"true"` + Body *AddShortUrlResponseBody `json:"body,omitempty" xml:"body,omitempty" require:"true"` +} + +func (s AddShortUrlResponse) String() string { + return tea.Prettify(s) +} + +func (s AddShortUrlResponse) GoString() string { + return s.String() +} + +func (s *AddShortUrlResponse) SetHeaders(v map[string]*string) *AddShortUrlResponse { + s.Headers = v + return s +} + +func (s *AddShortUrlResponse) SetBody(v *AddShortUrlResponseBody) *AddShortUrlResponse { + s.Body = v + return s +} + +type AddSmsSignRequest struct { + OwnerId *int64 `json:"OwnerId,omitempty" xml:"OwnerId,omitempty"` + Remark *string `json:"Remark,omitempty" xml:"Remark,omitempty"` + ResourceOwnerAccount *string `json:"ResourceOwnerAccount,omitempty" xml:"ResourceOwnerAccount,omitempty"` + ResourceOwnerId *int64 `json:"ResourceOwnerId,omitempty" xml:"ResourceOwnerId,omitempty"` + SignFileList []*AddSmsSignRequestSignFileList `json:"SignFileList,omitempty" xml:"SignFileList,omitempty" type:"Repeated"` + SignName *string `json:"SignName,omitempty" xml:"SignName,omitempty"` + SignSource *int32 `json:"SignSource,omitempty" xml:"SignSource,omitempty"` +} + +func (s AddSmsSignRequest) String() string { + return tea.Prettify(s) +} + +func (s AddSmsSignRequest) GoString() string { + return s.String() +} + +func (s *AddSmsSignRequest) SetOwnerId(v int64) *AddSmsSignRequest { + s.OwnerId = &v + return s +} + +func (s *AddSmsSignRequest) SetRemark(v string) *AddSmsSignRequest { + s.Remark = &v + return s +} + +func (s *AddSmsSignRequest) SetResourceOwnerAccount(v string) *AddSmsSignRequest { + s.ResourceOwnerAccount = &v + return s +} + +func (s *AddSmsSignRequest) SetResourceOwnerId(v int64) *AddSmsSignRequest { + s.ResourceOwnerId = &v + return s +} + +func (s *AddSmsSignRequest) SetSignFileList(v []*AddSmsSignRequestSignFileList) *AddSmsSignRequest { + s.SignFileList = v + return s +} + +func (s *AddSmsSignRequest) SetSignName(v string) *AddSmsSignRequest { + s.SignName = &v + return s +} + +func (s *AddSmsSignRequest) SetSignSource(v int32) *AddSmsSignRequest { + s.SignSource = &v + return s +} + +type AddSmsSignRequestSignFileList struct { + FileContents *string `json:"FileContents,omitempty" xml:"FileContents,omitempty"` + FileSuffix *string `json:"FileSuffix,omitempty" xml:"FileSuffix,omitempty"` +} + +func (s AddSmsSignRequestSignFileList) String() string { + return tea.Prettify(s) +} + +func (s AddSmsSignRequestSignFileList) GoString() string { + return s.String() +} + +func (s *AddSmsSignRequestSignFileList) SetFileContents(v string) *AddSmsSignRequestSignFileList { + s.FileContents = &v + return s +} + +func (s *AddSmsSignRequestSignFileList) SetFileSuffix(v string) *AddSmsSignRequestSignFileList { + s.FileSuffix = &v + return s +} + +type AddSmsSignResponseBody struct { + Code *string `json:"Code,omitempty" xml:"Code,omitempty"` + Message *string `json:"Message,omitempty" xml:"Message,omitempty"` + RequestId *string `json:"RequestId,omitempty" xml:"RequestId,omitempty"` + SignName *string `json:"SignName,omitempty" xml:"SignName,omitempty"` +} + +func (s AddSmsSignResponseBody) String() string { + return tea.Prettify(s) +} + +func (s AddSmsSignResponseBody) GoString() string { + return s.String() +} + +func (s *AddSmsSignResponseBody) SetCode(v string) *AddSmsSignResponseBody { + s.Code = &v + return s +} + +func (s *AddSmsSignResponseBody) SetMessage(v string) *AddSmsSignResponseBody { + s.Message = &v + return s +} + +func (s *AddSmsSignResponseBody) SetRequestId(v string) *AddSmsSignResponseBody { + s.RequestId = &v + return s +} + +func (s *AddSmsSignResponseBody) SetSignName(v string) *AddSmsSignResponseBody { + s.SignName = &v + return s +} + +type AddSmsSignResponse struct { + Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty" require:"true"` + Body *AddSmsSignResponseBody `json:"body,omitempty" xml:"body,omitempty" require:"true"` +} + +func (s AddSmsSignResponse) String() string { + return tea.Prettify(s) +} + +func (s AddSmsSignResponse) GoString() string { + return s.String() +} + +func (s *AddSmsSignResponse) SetHeaders(v map[string]*string) *AddSmsSignResponse { + s.Headers = v + return s +} + +func (s *AddSmsSignResponse) SetBody(v *AddSmsSignResponseBody) *AddSmsSignResponse { + s.Body = v + return s +} + +type AddSmsTemplateRequest struct { + OwnerId *int64 `json:"OwnerId,omitempty" xml:"OwnerId,omitempty"` + Remark *string `json:"Remark,omitempty" xml:"Remark,omitempty"` + ResourceOwnerAccount *string `json:"ResourceOwnerAccount,omitempty" xml:"ResourceOwnerAccount,omitempty"` + ResourceOwnerId *int64 `json:"ResourceOwnerId,omitempty" xml:"ResourceOwnerId,omitempty"` + TemplateContent *string `json:"TemplateContent,omitempty" xml:"TemplateContent,omitempty"` + TemplateName *string `json:"TemplateName,omitempty" xml:"TemplateName,omitempty"` + TemplateType *int32 `json:"TemplateType,omitempty" xml:"TemplateType,omitempty"` +} + +func (s AddSmsTemplateRequest) String() string { + return tea.Prettify(s) +} + +func (s AddSmsTemplateRequest) GoString() string { + return s.String() +} + +func (s *AddSmsTemplateRequest) SetOwnerId(v int64) *AddSmsTemplateRequest { + s.OwnerId = &v + return s +} + +func (s *AddSmsTemplateRequest) SetRemark(v string) *AddSmsTemplateRequest { + s.Remark = &v + return s +} + +func (s *AddSmsTemplateRequest) SetResourceOwnerAccount(v string) *AddSmsTemplateRequest { + s.ResourceOwnerAccount = &v + return s +} + +func (s *AddSmsTemplateRequest) SetResourceOwnerId(v int64) *AddSmsTemplateRequest { + s.ResourceOwnerId = &v + return s +} + +func (s *AddSmsTemplateRequest) SetTemplateContent(v string) *AddSmsTemplateRequest { + s.TemplateContent = &v + return s +} + +func (s *AddSmsTemplateRequest) SetTemplateName(v string) *AddSmsTemplateRequest { + s.TemplateName = &v + return s +} + +func (s *AddSmsTemplateRequest) SetTemplateType(v int32) *AddSmsTemplateRequest { + s.TemplateType = &v + return s +} + +type AddSmsTemplateResponseBody struct { + Code *string `json:"Code,omitempty" xml:"Code,omitempty"` + Message *string `json:"Message,omitempty" xml:"Message,omitempty"` + RequestId *string `json:"RequestId,omitempty" xml:"RequestId,omitempty"` + TemplateCode *string `json:"TemplateCode,omitempty" xml:"TemplateCode,omitempty"` +} + +func (s AddSmsTemplateResponseBody) String() string { + return tea.Prettify(s) +} + +func (s AddSmsTemplateResponseBody) GoString() string { + return s.String() +} + +func (s *AddSmsTemplateResponseBody) SetCode(v string) *AddSmsTemplateResponseBody { + s.Code = &v + return s +} + +func (s *AddSmsTemplateResponseBody) SetMessage(v string) *AddSmsTemplateResponseBody { + s.Message = &v + return s +} + +func (s *AddSmsTemplateResponseBody) SetRequestId(v string) *AddSmsTemplateResponseBody { + s.RequestId = &v + return s +} + +func (s *AddSmsTemplateResponseBody) SetTemplateCode(v string) *AddSmsTemplateResponseBody { + s.TemplateCode = &v + return s +} + +type AddSmsTemplateResponse struct { + Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty" require:"true"` + Body *AddSmsTemplateResponseBody `json:"body,omitempty" xml:"body,omitempty" require:"true"` +} + +func (s AddSmsTemplateResponse) String() string { + return tea.Prettify(s) +} + +func (s AddSmsTemplateResponse) GoString() string { + return s.String() +} + +func (s *AddSmsTemplateResponse) SetHeaders(v map[string]*string) *AddSmsTemplateResponse { + s.Headers = v + return s +} + +func (s *AddSmsTemplateResponse) SetBody(v *AddSmsTemplateResponseBody) *AddSmsTemplateResponse { + s.Body = v + return s +} + +type DeleteShortUrlRequest struct { + OwnerId *int64 `json:"OwnerId,omitempty" xml:"OwnerId,omitempty"` + ResourceOwnerAccount *string `json:"ResourceOwnerAccount,omitempty" xml:"ResourceOwnerAccount,omitempty"` + ResourceOwnerId *int64 `json:"ResourceOwnerId,omitempty" xml:"ResourceOwnerId,omitempty"` + SourceUrl *string `json:"SourceUrl,omitempty" xml:"SourceUrl,omitempty"` +} + +func (s DeleteShortUrlRequest) String() string { + return tea.Prettify(s) +} + +func (s DeleteShortUrlRequest) GoString() string { + return s.String() +} + +func (s *DeleteShortUrlRequest) SetOwnerId(v int64) *DeleteShortUrlRequest { + s.OwnerId = &v + return s +} + +func (s *DeleteShortUrlRequest) SetResourceOwnerAccount(v string) *DeleteShortUrlRequest { + s.ResourceOwnerAccount = &v + return s +} + +func (s *DeleteShortUrlRequest) SetResourceOwnerId(v int64) *DeleteShortUrlRequest { + s.ResourceOwnerId = &v + return s +} + +func (s *DeleteShortUrlRequest) SetSourceUrl(v string) *DeleteShortUrlRequest { + s.SourceUrl = &v + return s +} + +type DeleteShortUrlResponseBody struct { + Code *string `json:"Code,omitempty" xml:"Code,omitempty"` + Message *string `json:"Message,omitempty" xml:"Message,omitempty"` + RequestId *string `json:"RequestId,omitempty" xml:"RequestId,omitempty"` +} + +func (s DeleteShortUrlResponseBody) String() string { + return tea.Prettify(s) +} + +func (s DeleteShortUrlResponseBody) GoString() string { + return s.String() +} + +func (s *DeleteShortUrlResponseBody) SetCode(v string) *DeleteShortUrlResponseBody { + s.Code = &v + return s +} + +func (s *DeleteShortUrlResponseBody) SetMessage(v string) *DeleteShortUrlResponseBody { + s.Message = &v + return s +} + +func (s *DeleteShortUrlResponseBody) SetRequestId(v string) *DeleteShortUrlResponseBody { + s.RequestId = &v + return s +} + +type DeleteShortUrlResponse struct { + Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty" require:"true"` + Body *DeleteShortUrlResponseBody `json:"body,omitempty" xml:"body,omitempty" require:"true"` +} + +func (s DeleteShortUrlResponse) String() string { + return tea.Prettify(s) +} + +func (s DeleteShortUrlResponse) GoString() string { + return s.String() +} + +func (s *DeleteShortUrlResponse) SetHeaders(v map[string]*string) *DeleteShortUrlResponse { + s.Headers = v + return s +} + +func (s *DeleteShortUrlResponse) SetBody(v *DeleteShortUrlResponseBody) *DeleteShortUrlResponse { + s.Body = v + return s +} + +type DeleteSmsSignRequest struct { + OwnerId *int64 `json:"OwnerId,omitempty" xml:"OwnerId,omitempty"` + ResourceOwnerAccount *string `json:"ResourceOwnerAccount,omitempty" xml:"ResourceOwnerAccount,omitempty"` + ResourceOwnerId *int64 `json:"ResourceOwnerId,omitempty" xml:"ResourceOwnerId,omitempty"` + SignName *string `json:"SignName,omitempty" xml:"SignName,omitempty"` +} + +func (s DeleteSmsSignRequest) String() string { + return tea.Prettify(s) +} + +func (s DeleteSmsSignRequest) GoString() string { + return s.String() +} + +func (s *DeleteSmsSignRequest) SetOwnerId(v int64) *DeleteSmsSignRequest { + s.OwnerId = &v + return s +} + +func (s *DeleteSmsSignRequest) SetResourceOwnerAccount(v string) *DeleteSmsSignRequest { + s.ResourceOwnerAccount = &v + return s +} + +func (s *DeleteSmsSignRequest) SetResourceOwnerId(v int64) *DeleteSmsSignRequest { + s.ResourceOwnerId = &v + return s +} + +func (s *DeleteSmsSignRequest) SetSignName(v string) *DeleteSmsSignRequest { + s.SignName = &v + return s +} + +type DeleteSmsSignResponseBody struct { + Code *string `json:"Code,omitempty" xml:"Code,omitempty"` + Message *string `json:"Message,omitempty" xml:"Message,omitempty"` + RequestId *string `json:"RequestId,omitempty" xml:"RequestId,omitempty"` + SignName *string `json:"SignName,omitempty" xml:"SignName,omitempty"` +} + +func (s DeleteSmsSignResponseBody) String() string { + return tea.Prettify(s) +} + +func (s DeleteSmsSignResponseBody) GoString() string { + return s.String() +} + +func (s *DeleteSmsSignResponseBody) SetCode(v string) *DeleteSmsSignResponseBody { + s.Code = &v + return s +} + +func (s *DeleteSmsSignResponseBody) SetMessage(v string) *DeleteSmsSignResponseBody { + s.Message = &v + return s +} + +func (s *DeleteSmsSignResponseBody) SetRequestId(v string) *DeleteSmsSignResponseBody { + s.RequestId = &v + return s +} + +func (s *DeleteSmsSignResponseBody) SetSignName(v string) *DeleteSmsSignResponseBody { + s.SignName = &v + return s +} + +type DeleteSmsSignResponse struct { + Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty" require:"true"` + Body *DeleteSmsSignResponseBody `json:"body,omitempty" xml:"body,omitempty" require:"true"` +} + +func (s DeleteSmsSignResponse) String() string { + return tea.Prettify(s) +} + +func (s DeleteSmsSignResponse) GoString() string { + return s.String() +} + +func (s *DeleteSmsSignResponse) SetHeaders(v map[string]*string) *DeleteSmsSignResponse { + s.Headers = v + return s +} + +func (s *DeleteSmsSignResponse) SetBody(v *DeleteSmsSignResponseBody) *DeleteSmsSignResponse { + s.Body = v + return s +} + +type DeleteSmsTemplateRequest struct { + OwnerId *int64 `json:"OwnerId,omitempty" xml:"OwnerId,omitempty"` + ResourceOwnerAccount *string `json:"ResourceOwnerAccount,omitempty" xml:"ResourceOwnerAccount,omitempty"` + ResourceOwnerId *int64 `json:"ResourceOwnerId,omitempty" xml:"ResourceOwnerId,omitempty"` + TemplateCode *string `json:"TemplateCode,omitempty" xml:"TemplateCode,omitempty"` +} + +func (s DeleteSmsTemplateRequest) String() string { + return tea.Prettify(s) +} + +func (s DeleteSmsTemplateRequest) GoString() string { + return s.String() +} + +func (s *DeleteSmsTemplateRequest) SetOwnerId(v int64) *DeleteSmsTemplateRequest { + s.OwnerId = &v + return s +} + +func (s *DeleteSmsTemplateRequest) SetResourceOwnerAccount(v string) *DeleteSmsTemplateRequest { + s.ResourceOwnerAccount = &v + return s +} + +func (s *DeleteSmsTemplateRequest) SetResourceOwnerId(v int64) *DeleteSmsTemplateRequest { + s.ResourceOwnerId = &v + return s +} + +func (s *DeleteSmsTemplateRequest) SetTemplateCode(v string) *DeleteSmsTemplateRequest { + s.TemplateCode = &v + return s +} + +type DeleteSmsTemplateResponseBody struct { + Code *string `json:"Code,omitempty" xml:"Code,omitempty"` + Message *string `json:"Message,omitempty" xml:"Message,omitempty"` + RequestId *string `json:"RequestId,omitempty" xml:"RequestId,omitempty"` + TemplateCode *string `json:"TemplateCode,omitempty" xml:"TemplateCode,omitempty"` +} + +func (s DeleteSmsTemplateResponseBody) String() string { + return tea.Prettify(s) +} + +func (s DeleteSmsTemplateResponseBody) GoString() string { + return s.String() +} + +func (s *DeleteSmsTemplateResponseBody) SetCode(v string) *DeleteSmsTemplateResponseBody { + s.Code = &v + return s +} + +func (s *DeleteSmsTemplateResponseBody) SetMessage(v string) *DeleteSmsTemplateResponseBody { + s.Message = &v + return s +} + +func (s *DeleteSmsTemplateResponseBody) SetRequestId(v string) *DeleteSmsTemplateResponseBody { + s.RequestId = &v + return s +} + +func (s *DeleteSmsTemplateResponseBody) SetTemplateCode(v string) *DeleteSmsTemplateResponseBody { + s.TemplateCode = &v + return s +} + +type DeleteSmsTemplateResponse struct { + Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty" require:"true"` + Body *DeleteSmsTemplateResponseBody `json:"body,omitempty" xml:"body,omitempty" require:"true"` +} + +func (s DeleteSmsTemplateResponse) String() string { + return tea.Prettify(s) +} + +func (s DeleteSmsTemplateResponse) GoString() string { + return s.String() +} + +func (s *DeleteSmsTemplateResponse) SetHeaders(v map[string]*string) *DeleteSmsTemplateResponse { + s.Headers = v + return s +} + +func (s *DeleteSmsTemplateResponse) SetBody(v *DeleteSmsTemplateResponseBody) *DeleteSmsTemplateResponse { + s.Body = v + return s +} + +type ListTagResourcesRequest struct { + NextToken *string `json:"NextToken,omitempty" xml:"NextToken,omitempty"` + OwnerId *int64 `json:"OwnerId,omitempty" xml:"OwnerId,omitempty"` + PageSize *int32 `json:"PageSize,omitempty" xml:"PageSize,omitempty"` + ProdCode *string `json:"ProdCode,omitempty" xml:"ProdCode,omitempty"` + RegionId *string `json:"RegionId,omitempty" xml:"RegionId,omitempty"` + ResourceId []*string `json:"ResourceId,omitempty" xml:"ResourceId,omitempty" type:"Repeated"` + ResourceOwnerAccount *string `json:"ResourceOwnerAccount,omitempty" xml:"ResourceOwnerAccount,omitempty"` + ResourceOwnerId *int64 `json:"ResourceOwnerId,omitempty" xml:"ResourceOwnerId,omitempty"` + ResourceType *string `json:"ResourceType,omitempty" xml:"ResourceType,omitempty"` + Tag []*ListTagResourcesRequestTag `json:"Tag,omitempty" xml:"Tag,omitempty" type:"Repeated"` +} + +func (s ListTagResourcesRequest) String() string { + return tea.Prettify(s) +} + +func (s ListTagResourcesRequest) GoString() string { + return s.String() +} + +func (s *ListTagResourcesRequest) SetNextToken(v string) *ListTagResourcesRequest { + s.NextToken = &v + return s +} + +func (s *ListTagResourcesRequest) SetOwnerId(v int64) *ListTagResourcesRequest { + s.OwnerId = &v + return s +} + +func (s *ListTagResourcesRequest) SetPageSize(v int32) *ListTagResourcesRequest { + s.PageSize = &v + return s +} + +func (s *ListTagResourcesRequest) SetProdCode(v string) *ListTagResourcesRequest { + s.ProdCode = &v + return s +} + +func (s *ListTagResourcesRequest) SetRegionId(v string) *ListTagResourcesRequest { + s.RegionId = &v + return s +} + +func (s *ListTagResourcesRequest) SetResourceId(v []*string) *ListTagResourcesRequest { + s.ResourceId = v + return s +} + +func (s *ListTagResourcesRequest) SetResourceOwnerAccount(v string) *ListTagResourcesRequest { + s.ResourceOwnerAccount = &v + return s +} + +func (s *ListTagResourcesRequest) SetResourceOwnerId(v int64) *ListTagResourcesRequest { + s.ResourceOwnerId = &v + return s +} + +func (s *ListTagResourcesRequest) SetResourceType(v string) *ListTagResourcesRequest { + s.ResourceType = &v + return s +} + +func (s *ListTagResourcesRequest) SetTag(v []*ListTagResourcesRequestTag) *ListTagResourcesRequest { + s.Tag = v + return s +} + +type ListTagResourcesRequestTag struct { + Key *string `json:"Key,omitempty" xml:"Key,omitempty"` + Value *string `json:"Value,omitempty" xml:"Value,omitempty"` +} + +func (s ListTagResourcesRequestTag) String() string { + return tea.Prettify(s) +} + +func (s ListTagResourcesRequestTag) GoString() string { + return s.String() +} + +func (s *ListTagResourcesRequestTag) SetKey(v string) *ListTagResourcesRequestTag { + s.Key = &v + return s +} + +func (s *ListTagResourcesRequestTag) SetValue(v string) *ListTagResourcesRequestTag { + s.Value = &v + return s +} + +type ListTagResourcesResponseBody struct { + Code *string `json:"Code,omitempty" xml:"Code,omitempty"` + NextToken *string `json:"NextToken,omitempty" xml:"NextToken,omitempty"` + RequestId *string `json:"RequestId,omitempty" xml:"RequestId,omitempty"` + TagResources *ListTagResourcesResponseBodyTagResources `json:"TagResources,omitempty" xml:"TagResources,omitempty" type:"Struct"` +} + +func (s ListTagResourcesResponseBody) String() string { + return tea.Prettify(s) +} + +func (s ListTagResourcesResponseBody) GoString() string { + return s.String() +} + +func (s *ListTagResourcesResponseBody) SetCode(v string) *ListTagResourcesResponseBody { + s.Code = &v + return s +} + +func (s *ListTagResourcesResponseBody) SetNextToken(v string) *ListTagResourcesResponseBody { + s.NextToken = &v + return s +} + +func (s *ListTagResourcesResponseBody) SetRequestId(v string) *ListTagResourcesResponseBody { + s.RequestId = &v + return s +} + +func (s *ListTagResourcesResponseBody) SetTagResources(v *ListTagResourcesResponseBodyTagResources) *ListTagResourcesResponseBody { + s.TagResources = v + return s +} + +type ListTagResourcesResponseBodyTagResources struct { + TagResource []*ListTagResourcesResponseBodyTagResourcesTagResource `json:"TagResource,omitempty" xml:"TagResource,omitempty" type:"Repeated"` +} + +func (s ListTagResourcesResponseBodyTagResources) String() string { + return tea.Prettify(s) +} + +func (s ListTagResourcesResponseBodyTagResources) GoString() string { + return s.String() +} + +func (s *ListTagResourcesResponseBodyTagResources) SetTagResource(v []*ListTagResourcesResponseBodyTagResourcesTagResource) *ListTagResourcesResponseBodyTagResources { + s.TagResource = v + return s +} + +type ListTagResourcesResponseBodyTagResourcesTagResource struct { + ResourceId *string `json:"ResourceId,omitempty" xml:"ResourceId,omitempty"` + ResourceType *string `json:"ResourceType,omitempty" xml:"ResourceType,omitempty"` + TagKey *string `json:"TagKey,omitempty" xml:"TagKey,omitempty"` + TagValue *string `json:"TagValue,omitempty" xml:"TagValue,omitempty"` +} + +func (s ListTagResourcesResponseBodyTagResourcesTagResource) String() string { + return tea.Prettify(s) +} + +func (s ListTagResourcesResponseBodyTagResourcesTagResource) GoString() string { + return s.String() +} + +func (s *ListTagResourcesResponseBodyTagResourcesTagResource) SetResourceId(v string) *ListTagResourcesResponseBodyTagResourcesTagResource { + s.ResourceId = &v + return s +} + +func (s *ListTagResourcesResponseBodyTagResourcesTagResource) SetResourceType(v string) *ListTagResourcesResponseBodyTagResourcesTagResource { + s.ResourceType = &v + return s +} + +func (s *ListTagResourcesResponseBodyTagResourcesTagResource) SetTagKey(v string) *ListTagResourcesResponseBodyTagResourcesTagResource { + s.TagKey = &v + return s +} + +func (s *ListTagResourcesResponseBodyTagResourcesTagResource) SetTagValue(v string) *ListTagResourcesResponseBodyTagResourcesTagResource { + s.TagValue = &v + return s +} + +type ListTagResourcesResponse struct { + Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty" require:"true"` + Body *ListTagResourcesResponseBody `json:"body,omitempty" xml:"body,omitempty" require:"true"` +} + +func (s ListTagResourcesResponse) String() string { + return tea.Prettify(s) +} + +func (s ListTagResourcesResponse) GoString() string { + return s.String() +} + +func (s *ListTagResourcesResponse) SetHeaders(v map[string]*string) *ListTagResourcesResponse { + s.Headers = v + return s +} + +func (s *ListTagResourcesResponse) SetBody(v *ListTagResourcesResponseBody) *ListTagResourcesResponse { + s.Body = v + return s +} + +type ModifySmsSignRequest struct { + OwnerId *int64 `json:"OwnerId,omitempty" xml:"OwnerId,omitempty"` + Remark *string `json:"Remark,omitempty" xml:"Remark,omitempty"` + ResourceOwnerAccount *string `json:"ResourceOwnerAccount,omitempty" xml:"ResourceOwnerAccount,omitempty"` + ResourceOwnerId *int64 `json:"ResourceOwnerId,omitempty" xml:"ResourceOwnerId,omitempty"` + SignFileList []*ModifySmsSignRequestSignFileList `json:"SignFileList,omitempty" xml:"SignFileList,omitempty" type:"Repeated"` + SignName *string `json:"SignName,omitempty" xml:"SignName,omitempty"` + SignSource *int32 `json:"SignSource,omitempty" xml:"SignSource,omitempty"` +} + +func (s ModifySmsSignRequest) String() string { + return tea.Prettify(s) +} + +func (s ModifySmsSignRequest) GoString() string { + return s.String() +} + +func (s *ModifySmsSignRequest) SetOwnerId(v int64) *ModifySmsSignRequest { + s.OwnerId = &v + return s +} + +func (s *ModifySmsSignRequest) SetRemark(v string) *ModifySmsSignRequest { + s.Remark = &v + return s +} + +func (s *ModifySmsSignRequest) SetResourceOwnerAccount(v string) *ModifySmsSignRequest { + s.ResourceOwnerAccount = &v + return s +} + +func (s *ModifySmsSignRequest) SetResourceOwnerId(v int64) *ModifySmsSignRequest { + s.ResourceOwnerId = &v + return s +} + +func (s *ModifySmsSignRequest) SetSignFileList(v []*ModifySmsSignRequestSignFileList) *ModifySmsSignRequest { + s.SignFileList = v + return s +} + +func (s *ModifySmsSignRequest) SetSignName(v string) *ModifySmsSignRequest { + s.SignName = &v + return s +} + +func (s *ModifySmsSignRequest) SetSignSource(v int32) *ModifySmsSignRequest { + s.SignSource = &v + return s +} + +type ModifySmsSignRequestSignFileList struct { + FileContents *string `json:"FileContents,omitempty" xml:"FileContents,omitempty"` + FileSuffix *string `json:"FileSuffix,omitempty" xml:"FileSuffix,omitempty"` +} + +func (s ModifySmsSignRequestSignFileList) String() string { + return tea.Prettify(s) +} + +func (s ModifySmsSignRequestSignFileList) GoString() string { + return s.String() +} + +func (s *ModifySmsSignRequestSignFileList) SetFileContents(v string) *ModifySmsSignRequestSignFileList { + s.FileContents = &v + return s +} + +func (s *ModifySmsSignRequestSignFileList) SetFileSuffix(v string) *ModifySmsSignRequestSignFileList { + s.FileSuffix = &v + return s +} + +type ModifySmsSignResponseBody struct { + Code *string `json:"Code,omitempty" xml:"Code,omitempty"` + Message *string `json:"Message,omitempty" xml:"Message,omitempty"` + RequestId *string `json:"RequestId,omitempty" xml:"RequestId,omitempty"` + SignName *string `json:"SignName,omitempty" xml:"SignName,omitempty"` +} + +func (s ModifySmsSignResponseBody) String() string { + return tea.Prettify(s) +} + +func (s ModifySmsSignResponseBody) GoString() string { + return s.String() +} + +func (s *ModifySmsSignResponseBody) SetCode(v string) *ModifySmsSignResponseBody { + s.Code = &v + return s +} + +func (s *ModifySmsSignResponseBody) SetMessage(v string) *ModifySmsSignResponseBody { + s.Message = &v + return s +} + +func (s *ModifySmsSignResponseBody) SetRequestId(v string) *ModifySmsSignResponseBody { + s.RequestId = &v + return s +} + +func (s *ModifySmsSignResponseBody) SetSignName(v string) *ModifySmsSignResponseBody { + s.SignName = &v + return s +} + +type ModifySmsSignResponse struct { + Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty" require:"true"` + Body *ModifySmsSignResponseBody `json:"body,omitempty" xml:"body,omitempty" require:"true"` +} + +func (s ModifySmsSignResponse) String() string { + return tea.Prettify(s) +} + +func (s ModifySmsSignResponse) GoString() string { + return s.String() +} + +func (s *ModifySmsSignResponse) SetHeaders(v map[string]*string) *ModifySmsSignResponse { + s.Headers = v + return s +} + +func (s *ModifySmsSignResponse) SetBody(v *ModifySmsSignResponseBody) *ModifySmsSignResponse { + s.Body = v + return s +} + +type ModifySmsTemplateRequest struct { + OwnerId *int64 `json:"OwnerId,omitempty" xml:"OwnerId,omitempty"` + Remark *string `json:"Remark,omitempty" xml:"Remark,omitempty"` + ResourceOwnerAccount *string `json:"ResourceOwnerAccount,omitempty" xml:"ResourceOwnerAccount,omitempty"` + ResourceOwnerId *int64 `json:"ResourceOwnerId,omitempty" xml:"ResourceOwnerId,omitempty"` + TemplateCode *string `json:"TemplateCode,omitempty" xml:"TemplateCode,omitempty"` + TemplateContent *string `json:"TemplateContent,omitempty" xml:"TemplateContent,omitempty"` + TemplateName *string `json:"TemplateName,omitempty" xml:"TemplateName,omitempty"` + TemplateType *int32 `json:"TemplateType,omitempty" xml:"TemplateType,omitempty"` +} + +func (s ModifySmsTemplateRequest) String() string { + return tea.Prettify(s) +} + +func (s ModifySmsTemplateRequest) GoString() string { + return s.String() +} + +func (s *ModifySmsTemplateRequest) SetOwnerId(v int64) *ModifySmsTemplateRequest { + s.OwnerId = &v + return s +} + +func (s *ModifySmsTemplateRequest) SetRemark(v string) *ModifySmsTemplateRequest { + s.Remark = &v + return s +} + +func (s *ModifySmsTemplateRequest) SetResourceOwnerAccount(v string) *ModifySmsTemplateRequest { + s.ResourceOwnerAccount = &v + return s +} + +func (s *ModifySmsTemplateRequest) SetResourceOwnerId(v int64) *ModifySmsTemplateRequest { + s.ResourceOwnerId = &v + return s +} + +func (s *ModifySmsTemplateRequest) SetTemplateCode(v string) *ModifySmsTemplateRequest { + s.TemplateCode = &v + return s +} + +func (s *ModifySmsTemplateRequest) SetTemplateContent(v string) *ModifySmsTemplateRequest { + s.TemplateContent = &v + return s +} + +func (s *ModifySmsTemplateRequest) SetTemplateName(v string) *ModifySmsTemplateRequest { + s.TemplateName = &v + return s +} + +func (s *ModifySmsTemplateRequest) SetTemplateType(v int32) *ModifySmsTemplateRequest { + s.TemplateType = &v + return s +} + +type ModifySmsTemplateResponseBody struct { + Code *string `json:"Code,omitempty" xml:"Code,omitempty"` + Message *string `json:"Message,omitempty" xml:"Message,omitempty"` + RequestId *string `json:"RequestId,omitempty" xml:"RequestId,omitempty"` + TemplateCode *string `json:"TemplateCode,omitempty" xml:"TemplateCode,omitempty"` +} + +func (s ModifySmsTemplateResponseBody) String() string { + return tea.Prettify(s) +} + +func (s ModifySmsTemplateResponseBody) GoString() string { + return s.String() +} + +func (s *ModifySmsTemplateResponseBody) SetCode(v string) *ModifySmsTemplateResponseBody { + s.Code = &v + return s +} + +func (s *ModifySmsTemplateResponseBody) SetMessage(v string) *ModifySmsTemplateResponseBody { + s.Message = &v + return s +} + +func (s *ModifySmsTemplateResponseBody) SetRequestId(v string) *ModifySmsTemplateResponseBody { + s.RequestId = &v + return s +} + +func (s *ModifySmsTemplateResponseBody) SetTemplateCode(v string) *ModifySmsTemplateResponseBody { + s.TemplateCode = &v + return s +} + +type ModifySmsTemplateResponse struct { + Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty" require:"true"` + Body *ModifySmsTemplateResponseBody `json:"body,omitempty" xml:"body,omitempty" require:"true"` +} + +func (s ModifySmsTemplateResponse) String() string { + return tea.Prettify(s) +} + +func (s ModifySmsTemplateResponse) GoString() string { + return s.String() +} + +func (s *ModifySmsTemplateResponse) SetHeaders(v map[string]*string) *ModifySmsTemplateResponse { + s.Headers = v + return s +} + +func (s *ModifySmsTemplateResponse) SetBody(v *ModifySmsTemplateResponseBody) *ModifySmsTemplateResponse { + s.Body = v + return s +} + +type QuerySendDetailsRequest struct { + BizId *string `json:"BizId,omitempty" xml:"BizId,omitempty"` + CurrentPage *int64 `json:"CurrentPage,omitempty" xml:"CurrentPage,omitempty"` + OwnerId *int64 `json:"OwnerId,omitempty" xml:"OwnerId,omitempty"` + PageSize *int64 `json:"PageSize,omitempty" xml:"PageSize,omitempty"` + PhoneNumber *string `json:"PhoneNumber,omitempty" xml:"PhoneNumber,omitempty"` + ResourceOwnerAccount *string `json:"ResourceOwnerAccount,omitempty" xml:"ResourceOwnerAccount,omitempty"` + ResourceOwnerId *int64 `json:"ResourceOwnerId,omitempty" xml:"ResourceOwnerId,omitempty"` + SendDate *string `json:"SendDate,omitempty" xml:"SendDate,omitempty"` +} + +func (s QuerySendDetailsRequest) String() string { + return tea.Prettify(s) +} + +func (s QuerySendDetailsRequest) GoString() string { + return s.String() +} + +func (s *QuerySendDetailsRequest) SetBizId(v string) *QuerySendDetailsRequest { + s.BizId = &v + return s +} + +func (s *QuerySendDetailsRequest) SetCurrentPage(v int64) *QuerySendDetailsRequest { + s.CurrentPage = &v + return s +} + +func (s *QuerySendDetailsRequest) SetOwnerId(v int64) *QuerySendDetailsRequest { + s.OwnerId = &v + return s +} + +func (s *QuerySendDetailsRequest) SetPageSize(v int64) *QuerySendDetailsRequest { + s.PageSize = &v + return s +} + +func (s *QuerySendDetailsRequest) SetPhoneNumber(v string) *QuerySendDetailsRequest { + s.PhoneNumber = &v + return s +} + +func (s *QuerySendDetailsRequest) SetResourceOwnerAccount(v string) *QuerySendDetailsRequest { + s.ResourceOwnerAccount = &v + return s +} + +func (s *QuerySendDetailsRequest) SetResourceOwnerId(v int64) *QuerySendDetailsRequest { + s.ResourceOwnerId = &v + return s +} + +func (s *QuerySendDetailsRequest) SetSendDate(v string) *QuerySendDetailsRequest { + s.SendDate = &v + return s +} + +type QuerySendDetailsResponseBody struct { + Code *string `json:"Code,omitempty" xml:"Code,omitempty"` + Message *string `json:"Message,omitempty" xml:"Message,omitempty"` + RequestId *string `json:"RequestId,omitempty" xml:"RequestId,omitempty"` + SmsSendDetailDTOs *QuerySendDetailsResponseBodySmsSendDetailDTOs `json:"SmsSendDetailDTOs,omitempty" xml:"SmsSendDetailDTOs,omitempty" type:"Struct"` + TotalCount *string `json:"TotalCount,omitempty" xml:"TotalCount,omitempty"` +} + +func (s QuerySendDetailsResponseBody) String() string { + return tea.Prettify(s) +} + +func (s QuerySendDetailsResponseBody) GoString() string { + return s.String() +} + +func (s *QuerySendDetailsResponseBody) SetCode(v string) *QuerySendDetailsResponseBody { + s.Code = &v + return s +} + +func (s *QuerySendDetailsResponseBody) SetMessage(v string) *QuerySendDetailsResponseBody { + s.Message = &v + return s +} + +func (s *QuerySendDetailsResponseBody) SetRequestId(v string) *QuerySendDetailsResponseBody { + s.RequestId = &v + return s +} + +func (s *QuerySendDetailsResponseBody) SetSmsSendDetailDTOs(v *QuerySendDetailsResponseBodySmsSendDetailDTOs) *QuerySendDetailsResponseBody { + s.SmsSendDetailDTOs = v + return s +} + +func (s *QuerySendDetailsResponseBody) SetTotalCount(v string) *QuerySendDetailsResponseBody { + s.TotalCount = &v + return s +} + +type QuerySendDetailsResponseBodySmsSendDetailDTOs struct { + SmsSendDetailDTO []*QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO `json:"SmsSendDetailDTO,omitempty" xml:"SmsSendDetailDTO,omitempty" type:"Repeated"` +} + +func (s QuerySendDetailsResponseBodySmsSendDetailDTOs) String() string { + return tea.Prettify(s) +} + +func (s QuerySendDetailsResponseBodySmsSendDetailDTOs) GoString() string { + return s.String() +} + +func (s *QuerySendDetailsResponseBodySmsSendDetailDTOs) SetSmsSendDetailDTO(v []*QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO) *QuerySendDetailsResponseBodySmsSendDetailDTOs { + s.SmsSendDetailDTO = v + return s +} + +type QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO struct { + Content *string `json:"Content,omitempty" xml:"Content,omitempty"` + ErrCode *string `json:"ErrCode,omitempty" xml:"ErrCode,omitempty"` + OutId *string `json:"OutId,omitempty" xml:"OutId,omitempty"` + PhoneNum *string `json:"PhoneNum,omitempty" xml:"PhoneNum,omitempty"` + ReceiveDate *string `json:"ReceiveDate,omitempty" xml:"ReceiveDate,omitempty"` + SendDate *string `json:"SendDate,omitempty" xml:"SendDate,omitempty"` + SendStatus *int64 `json:"SendStatus,omitempty" xml:"SendStatus,omitempty"` + TemplateCode *string `json:"TemplateCode,omitempty" xml:"TemplateCode,omitempty"` +} + +func (s QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO) String() string { + return tea.Prettify(s) +} + +func (s QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO) GoString() string { + return s.String() +} + +func (s *QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO) SetContent(v string) *QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO { + s.Content = &v + return s +} + +func (s *QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO) SetErrCode(v string) *QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO { + s.ErrCode = &v + return s +} + +func (s *QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO) SetOutId(v string) *QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO { + s.OutId = &v + return s +} + +func (s *QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO) SetPhoneNum(v string) *QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO { + s.PhoneNum = &v + return s +} + +func (s *QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO) SetReceiveDate(v string) *QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO { + s.ReceiveDate = &v + return s +} + +func (s *QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO) SetSendDate(v string) *QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO { + s.SendDate = &v + return s +} + +func (s *QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO) SetSendStatus(v int64) *QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO { + s.SendStatus = &v + return s +} + +func (s *QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO) SetTemplateCode(v string) *QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO { + s.TemplateCode = &v + return s +} + +type QuerySendDetailsResponse struct { + Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty" require:"true"` + Body *QuerySendDetailsResponseBody `json:"body,omitempty" xml:"body,omitempty" require:"true"` +} + +func (s QuerySendDetailsResponse) String() string { + return tea.Prettify(s) +} + +func (s QuerySendDetailsResponse) GoString() string { + return s.String() +} + +func (s *QuerySendDetailsResponse) SetHeaders(v map[string]*string) *QuerySendDetailsResponse { + s.Headers = v + return s +} + +func (s *QuerySendDetailsResponse) SetBody(v *QuerySendDetailsResponseBody) *QuerySendDetailsResponse { + s.Body = v + return s +} + +type QuerySendStatisticsRequest struct { + EndDate *string `json:"EndDate,omitempty" xml:"EndDate,omitempty"` + IsGlobe *int32 `json:"IsGlobe,omitempty" xml:"IsGlobe,omitempty"` + OwnerId *int64 `json:"OwnerId,omitempty" xml:"OwnerId,omitempty"` + PageIndex *int32 `json:"PageIndex,omitempty" xml:"PageIndex,omitempty"` + PageSize *int32 `json:"PageSize,omitempty" xml:"PageSize,omitempty"` + ResourceOwnerAccount *string `json:"ResourceOwnerAccount,omitempty" xml:"ResourceOwnerAccount,omitempty"` + ResourceOwnerId *int64 `json:"ResourceOwnerId,omitempty" xml:"ResourceOwnerId,omitempty"` + StartDate *string `json:"StartDate,omitempty" xml:"StartDate,omitempty"` +} + +func (s QuerySendStatisticsRequest) String() string { + return tea.Prettify(s) +} + +func (s QuerySendStatisticsRequest) GoString() string { + return s.String() +} + +func (s *QuerySendStatisticsRequest) SetEndDate(v string) *QuerySendStatisticsRequest { + s.EndDate = &v + return s +} + +func (s *QuerySendStatisticsRequest) SetIsGlobe(v int32) *QuerySendStatisticsRequest { + s.IsGlobe = &v + return s +} + +func (s *QuerySendStatisticsRequest) SetOwnerId(v int64) *QuerySendStatisticsRequest { + s.OwnerId = &v + return s +} + +func (s *QuerySendStatisticsRequest) SetPageIndex(v int32) *QuerySendStatisticsRequest { + s.PageIndex = &v + return s +} + +func (s *QuerySendStatisticsRequest) SetPageSize(v int32) *QuerySendStatisticsRequest { + s.PageSize = &v + return s +} + +func (s *QuerySendStatisticsRequest) SetResourceOwnerAccount(v string) *QuerySendStatisticsRequest { + s.ResourceOwnerAccount = &v + return s +} + +func (s *QuerySendStatisticsRequest) SetResourceOwnerId(v int64) *QuerySendStatisticsRequest { + s.ResourceOwnerId = &v + return s +} + +func (s *QuerySendStatisticsRequest) SetStartDate(v string) *QuerySendStatisticsRequest { + s.StartDate = &v + return s +} + +type QuerySendStatisticsResponseBody struct { + Code *string `json:"Code,omitempty" xml:"Code,omitempty"` + Data *QuerySendStatisticsResponseBodyData `json:"Data,omitempty" xml:"Data,omitempty" type:"Struct"` + Message *string `json:"Message,omitempty" xml:"Message,omitempty"` + RequestId *string `json:"RequestId,omitempty" xml:"RequestId,omitempty"` +} + +func (s QuerySendStatisticsResponseBody) String() string { + return tea.Prettify(s) +} + +func (s QuerySendStatisticsResponseBody) GoString() string { + return s.String() +} + +func (s *QuerySendStatisticsResponseBody) SetCode(v string) *QuerySendStatisticsResponseBody { + s.Code = &v + return s +} + +func (s *QuerySendStatisticsResponseBody) SetData(v *QuerySendStatisticsResponseBodyData) *QuerySendStatisticsResponseBody { + s.Data = v + return s +} + +func (s *QuerySendStatisticsResponseBody) SetMessage(v string) *QuerySendStatisticsResponseBody { + s.Message = &v + return s +} + +func (s *QuerySendStatisticsResponseBody) SetRequestId(v string) *QuerySendStatisticsResponseBody { + s.RequestId = &v + return s +} + +type QuerySendStatisticsResponseBodyData struct { + TargetList []*QuerySendStatisticsResponseBodyDataTargetList `json:"TargetList,omitempty" xml:"TargetList,omitempty" type:"Repeated"` + TotalSize *int64 `json:"TotalSize,omitempty" xml:"TotalSize,omitempty"` +} + +func (s QuerySendStatisticsResponseBodyData) String() string { + return tea.Prettify(s) +} + +func (s QuerySendStatisticsResponseBodyData) GoString() string { + return s.String() +} + +func (s *QuerySendStatisticsResponseBodyData) SetTargetList(v []*QuerySendStatisticsResponseBodyDataTargetList) *QuerySendStatisticsResponseBodyData { + s.TargetList = v + return s +} + +func (s *QuerySendStatisticsResponseBodyData) SetTotalSize(v int64) *QuerySendStatisticsResponseBodyData { + s.TotalSize = &v + return s +} + +type QuerySendStatisticsResponseBodyDataTargetList struct { + NoRespondedCount *int64 `json:"NoRespondedCount,omitempty" xml:"NoRespondedCount,omitempty"` + RespondedFailCount *int64 `json:"RespondedFailCount,omitempty" xml:"RespondedFailCount,omitempty"` + RespondedSuccessCount *int64 `json:"RespondedSuccessCount,omitempty" xml:"RespondedSuccessCount,omitempty"` + SendDate *string `json:"SendDate,omitempty" xml:"SendDate,omitempty"` + TotalCount *int64 `json:"TotalCount,omitempty" xml:"TotalCount,omitempty"` +} + +func (s QuerySendStatisticsResponseBodyDataTargetList) String() string { + return tea.Prettify(s) +} + +func (s QuerySendStatisticsResponseBodyDataTargetList) GoString() string { + return s.String() +} + +func (s *QuerySendStatisticsResponseBodyDataTargetList) SetNoRespondedCount(v int64) *QuerySendStatisticsResponseBodyDataTargetList { + s.NoRespondedCount = &v + return s +} + +func (s *QuerySendStatisticsResponseBodyDataTargetList) SetRespondedFailCount(v int64) *QuerySendStatisticsResponseBodyDataTargetList { + s.RespondedFailCount = &v + return s +} + +func (s *QuerySendStatisticsResponseBodyDataTargetList) SetRespondedSuccessCount(v int64) *QuerySendStatisticsResponseBodyDataTargetList { + s.RespondedSuccessCount = &v + return s +} + +func (s *QuerySendStatisticsResponseBodyDataTargetList) SetSendDate(v string) *QuerySendStatisticsResponseBodyDataTargetList { + s.SendDate = &v + return s +} + +func (s *QuerySendStatisticsResponseBodyDataTargetList) SetTotalCount(v int64) *QuerySendStatisticsResponseBodyDataTargetList { + s.TotalCount = &v + return s +} + +type QuerySendStatisticsResponse struct { + Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty" require:"true"` + Body *QuerySendStatisticsResponseBody `json:"body,omitempty" xml:"body,omitempty" require:"true"` +} + +func (s QuerySendStatisticsResponse) String() string { + return tea.Prettify(s) +} + +func (s QuerySendStatisticsResponse) GoString() string { + return s.String() +} + +func (s *QuerySendStatisticsResponse) SetHeaders(v map[string]*string) *QuerySendStatisticsResponse { + s.Headers = v + return s +} + +func (s *QuerySendStatisticsResponse) SetBody(v *QuerySendStatisticsResponseBody) *QuerySendStatisticsResponse { + s.Body = v + return s +} + +type QueryShortUrlRequest struct { + OwnerId *int64 `json:"OwnerId,omitempty" xml:"OwnerId,omitempty"` + ResourceOwnerAccount *string `json:"ResourceOwnerAccount,omitempty" xml:"ResourceOwnerAccount,omitempty"` + ResourceOwnerId *int64 `json:"ResourceOwnerId,omitempty" xml:"ResourceOwnerId,omitempty"` + ShortUrl *string `json:"ShortUrl,omitempty" xml:"ShortUrl,omitempty"` +} + +func (s QueryShortUrlRequest) String() string { + return tea.Prettify(s) +} + +func (s QueryShortUrlRequest) GoString() string { + return s.String() +} + +func (s *QueryShortUrlRequest) SetOwnerId(v int64) *QueryShortUrlRequest { + s.OwnerId = &v + return s +} + +func (s *QueryShortUrlRequest) SetResourceOwnerAccount(v string) *QueryShortUrlRequest { + s.ResourceOwnerAccount = &v + return s +} + +func (s *QueryShortUrlRequest) SetResourceOwnerId(v int64) *QueryShortUrlRequest { + s.ResourceOwnerId = &v + return s +} + +func (s *QueryShortUrlRequest) SetShortUrl(v string) *QueryShortUrlRequest { + s.ShortUrl = &v + return s +} + +type QueryShortUrlResponseBody struct { + Code *string `json:"Code,omitempty" xml:"Code,omitempty"` + Data *QueryShortUrlResponseBodyData `json:"Data,omitempty" xml:"Data,omitempty" type:"Struct"` + Message *string `json:"Message,omitempty" xml:"Message,omitempty"` + RequestId *string `json:"RequestId,omitempty" xml:"RequestId,omitempty"` +} + +func (s QueryShortUrlResponseBody) String() string { + return tea.Prettify(s) +} + +func (s QueryShortUrlResponseBody) GoString() string { + return s.String() +} + +func (s *QueryShortUrlResponseBody) SetCode(v string) *QueryShortUrlResponseBody { + s.Code = &v + return s +} + +func (s *QueryShortUrlResponseBody) SetData(v *QueryShortUrlResponseBodyData) *QueryShortUrlResponseBody { + s.Data = v + return s +} + +func (s *QueryShortUrlResponseBody) SetMessage(v string) *QueryShortUrlResponseBody { + s.Message = &v + return s +} + +func (s *QueryShortUrlResponseBody) SetRequestId(v string) *QueryShortUrlResponseBody { + s.RequestId = &v + return s +} + +type QueryShortUrlResponseBodyData struct { + CreateDate *string `json:"CreateDate,omitempty" xml:"CreateDate,omitempty"` + ExpireDate *string `json:"ExpireDate,omitempty" xml:"ExpireDate,omitempty"` + PageViewCount *string `json:"PageViewCount,omitempty" xml:"PageViewCount,omitempty"` + ShortUrl *string `json:"ShortUrl,omitempty" xml:"ShortUrl,omitempty"` + ShortUrlName *string `json:"ShortUrlName,omitempty" xml:"ShortUrlName,omitempty"` + ShortUrlStatus *string `json:"ShortUrlStatus,omitempty" xml:"ShortUrlStatus,omitempty"` + SourceUrl *string `json:"SourceUrl,omitempty" xml:"SourceUrl,omitempty"` + UniqueVisitorCount *string `json:"UniqueVisitorCount,omitempty" xml:"UniqueVisitorCount,omitempty"` +} + +func (s QueryShortUrlResponseBodyData) String() string { + return tea.Prettify(s) +} + +func (s QueryShortUrlResponseBodyData) GoString() string { + return s.String() +} + +func (s *QueryShortUrlResponseBodyData) SetCreateDate(v string) *QueryShortUrlResponseBodyData { + s.CreateDate = &v + return s +} + +func (s *QueryShortUrlResponseBodyData) SetExpireDate(v string) *QueryShortUrlResponseBodyData { + s.ExpireDate = &v + return s +} + +func (s *QueryShortUrlResponseBodyData) SetPageViewCount(v string) *QueryShortUrlResponseBodyData { + s.PageViewCount = &v + return s +} + +func (s *QueryShortUrlResponseBodyData) SetShortUrl(v string) *QueryShortUrlResponseBodyData { + s.ShortUrl = &v + return s +} + +func (s *QueryShortUrlResponseBodyData) SetShortUrlName(v string) *QueryShortUrlResponseBodyData { + s.ShortUrlName = &v + return s +} + +func (s *QueryShortUrlResponseBodyData) SetShortUrlStatus(v string) *QueryShortUrlResponseBodyData { + s.ShortUrlStatus = &v + return s +} + +func (s *QueryShortUrlResponseBodyData) SetSourceUrl(v string) *QueryShortUrlResponseBodyData { + s.SourceUrl = &v + return s +} + +func (s *QueryShortUrlResponseBodyData) SetUniqueVisitorCount(v string) *QueryShortUrlResponseBodyData { + s.UniqueVisitorCount = &v + return s +} + +type QueryShortUrlResponse struct { + Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty" require:"true"` + Body *QueryShortUrlResponseBody `json:"body,omitempty" xml:"body,omitempty" require:"true"` +} + +func (s QueryShortUrlResponse) String() string { + return tea.Prettify(s) +} + +func (s QueryShortUrlResponse) GoString() string { + return s.String() +} + +func (s *QueryShortUrlResponse) SetHeaders(v map[string]*string) *QueryShortUrlResponse { + s.Headers = v + return s +} + +func (s *QueryShortUrlResponse) SetBody(v *QueryShortUrlResponseBody) *QueryShortUrlResponse { + s.Body = v + return s +} + +type QuerySmsSignRequest struct { + OwnerId *int64 `json:"OwnerId,omitempty" xml:"OwnerId,omitempty"` + ResourceOwnerAccount *string `json:"ResourceOwnerAccount,omitempty" xml:"ResourceOwnerAccount,omitempty"` + ResourceOwnerId *int64 `json:"ResourceOwnerId,omitempty" xml:"ResourceOwnerId,omitempty"` + SignName *string `json:"SignName,omitempty" xml:"SignName,omitempty"` +} + +func (s QuerySmsSignRequest) String() string { + return tea.Prettify(s) +} + +func (s QuerySmsSignRequest) GoString() string { + return s.String() +} + +func (s *QuerySmsSignRequest) SetOwnerId(v int64) *QuerySmsSignRequest { + s.OwnerId = &v + return s +} + +func (s *QuerySmsSignRequest) SetResourceOwnerAccount(v string) *QuerySmsSignRequest { + s.ResourceOwnerAccount = &v + return s +} + +func (s *QuerySmsSignRequest) SetResourceOwnerId(v int64) *QuerySmsSignRequest { + s.ResourceOwnerId = &v + return s +} + +func (s *QuerySmsSignRequest) SetSignName(v string) *QuerySmsSignRequest { + s.SignName = &v + return s +} + +type QuerySmsSignResponseBody struct { + Code *string `json:"Code,omitempty" xml:"Code,omitempty"` + CreateDate *string `json:"CreateDate,omitempty" xml:"CreateDate,omitempty"` + Message *string `json:"Message,omitempty" xml:"Message,omitempty"` + Reason *string `json:"Reason,omitempty" xml:"Reason,omitempty"` + RequestId *string `json:"RequestId,omitempty" xml:"RequestId,omitempty"` + SignName *string `json:"SignName,omitempty" xml:"SignName,omitempty"` + SignStatus *int32 `json:"SignStatus,omitempty" xml:"SignStatus,omitempty"` +} + +func (s QuerySmsSignResponseBody) String() string { + return tea.Prettify(s) +} + +func (s QuerySmsSignResponseBody) GoString() string { + return s.String() +} + +func (s *QuerySmsSignResponseBody) SetCode(v string) *QuerySmsSignResponseBody { + s.Code = &v + return s +} + +func (s *QuerySmsSignResponseBody) SetCreateDate(v string) *QuerySmsSignResponseBody { + s.CreateDate = &v + return s +} + +func (s *QuerySmsSignResponseBody) SetMessage(v string) *QuerySmsSignResponseBody { + s.Message = &v + return s +} + +func (s *QuerySmsSignResponseBody) SetReason(v string) *QuerySmsSignResponseBody { + s.Reason = &v + return s +} + +func (s *QuerySmsSignResponseBody) SetRequestId(v string) *QuerySmsSignResponseBody { + s.RequestId = &v + return s +} + +func (s *QuerySmsSignResponseBody) SetSignName(v string) *QuerySmsSignResponseBody { + s.SignName = &v + return s +} + +func (s *QuerySmsSignResponseBody) SetSignStatus(v int32) *QuerySmsSignResponseBody { + s.SignStatus = &v + return s +} + +type QuerySmsSignResponse struct { + Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty" require:"true"` + Body *QuerySmsSignResponseBody `json:"body,omitempty" xml:"body,omitempty" require:"true"` +} + +func (s QuerySmsSignResponse) String() string { + return tea.Prettify(s) +} + +func (s QuerySmsSignResponse) GoString() string { + return s.String() +} + +func (s *QuerySmsSignResponse) SetHeaders(v map[string]*string) *QuerySmsSignResponse { + s.Headers = v + return s +} + +func (s *QuerySmsSignResponse) SetBody(v *QuerySmsSignResponseBody) *QuerySmsSignResponse { + s.Body = v + return s +} + +type QuerySmsSignListRequest struct { + OwnerId *int64 `json:"OwnerId,omitempty" xml:"OwnerId,omitempty"` + PageIndex *int32 `json:"PageIndex,omitempty" xml:"PageIndex,omitempty"` + PageSize *int32 `json:"PageSize,omitempty" xml:"PageSize,omitempty"` + ResourceOwnerAccount *string `json:"ResourceOwnerAccount,omitempty" xml:"ResourceOwnerAccount,omitempty"` + ResourceOwnerId *int64 `json:"ResourceOwnerId,omitempty" xml:"ResourceOwnerId,omitempty"` +} + +func (s QuerySmsSignListRequest) String() string { + return tea.Prettify(s) +} + +func (s QuerySmsSignListRequest) GoString() string { + return s.String() +} + +func (s *QuerySmsSignListRequest) SetOwnerId(v int64) *QuerySmsSignListRequest { + s.OwnerId = &v + return s +} + +func (s *QuerySmsSignListRequest) SetPageIndex(v int32) *QuerySmsSignListRequest { + s.PageIndex = &v + return s +} + +func (s *QuerySmsSignListRequest) SetPageSize(v int32) *QuerySmsSignListRequest { + s.PageSize = &v + return s +} + +func (s *QuerySmsSignListRequest) SetResourceOwnerAccount(v string) *QuerySmsSignListRequest { + s.ResourceOwnerAccount = &v + return s +} + +func (s *QuerySmsSignListRequest) SetResourceOwnerId(v int64) *QuerySmsSignListRequest { + s.ResourceOwnerId = &v + return s +} + +type QuerySmsSignListResponseBody struct { + Code *string `json:"Code,omitempty" xml:"Code,omitempty"` + Message *string `json:"Message,omitempty" xml:"Message,omitempty"` + RequestId *string `json:"RequestId,omitempty" xml:"RequestId,omitempty"` + SmsSignList []*QuerySmsSignListResponseBodySmsSignList `json:"SmsSignList,omitempty" xml:"SmsSignList,omitempty" type:"Repeated"` +} + +func (s QuerySmsSignListResponseBody) String() string { + return tea.Prettify(s) +} + +func (s QuerySmsSignListResponseBody) GoString() string { + return s.String() +} + +func (s *QuerySmsSignListResponseBody) SetCode(v string) *QuerySmsSignListResponseBody { + s.Code = &v + return s +} + +func (s *QuerySmsSignListResponseBody) SetMessage(v string) *QuerySmsSignListResponseBody { + s.Message = &v + return s +} + +func (s *QuerySmsSignListResponseBody) SetRequestId(v string) *QuerySmsSignListResponseBody { + s.RequestId = &v + return s +} + +func (s *QuerySmsSignListResponseBody) SetSmsSignList(v []*QuerySmsSignListResponseBodySmsSignList) *QuerySmsSignListResponseBody { + s.SmsSignList = v + return s +} + +type QuerySmsSignListResponseBodySmsSignList struct { + AuditStatus *string `json:"AuditStatus,omitempty" xml:"AuditStatus,omitempty"` + BusinessType *string `json:"BusinessType,omitempty" xml:"BusinessType,omitempty"` + CreateDate *string `json:"CreateDate,omitempty" xml:"CreateDate,omitempty"` + OrderId *string `json:"OrderId,omitempty" xml:"OrderId,omitempty"` + Reason *QuerySmsSignListResponseBodySmsSignListReason `json:"Reason,omitempty" xml:"Reason,omitempty" type:"Struct"` + SignName *string `json:"SignName,omitempty" xml:"SignName,omitempty"` +} + +func (s QuerySmsSignListResponseBodySmsSignList) String() string { + return tea.Prettify(s) +} + +func (s QuerySmsSignListResponseBodySmsSignList) GoString() string { + return s.String() +} + +func (s *QuerySmsSignListResponseBodySmsSignList) SetAuditStatus(v string) *QuerySmsSignListResponseBodySmsSignList { + s.AuditStatus = &v + return s +} + +func (s *QuerySmsSignListResponseBodySmsSignList) SetBusinessType(v string) *QuerySmsSignListResponseBodySmsSignList { + s.BusinessType = &v + return s +} + +func (s *QuerySmsSignListResponseBodySmsSignList) SetCreateDate(v string) *QuerySmsSignListResponseBodySmsSignList { + s.CreateDate = &v + return s +} + +func (s *QuerySmsSignListResponseBodySmsSignList) SetOrderId(v string) *QuerySmsSignListResponseBodySmsSignList { + s.OrderId = &v + return s +} + +func (s *QuerySmsSignListResponseBodySmsSignList) SetReason(v *QuerySmsSignListResponseBodySmsSignListReason) *QuerySmsSignListResponseBodySmsSignList { + s.Reason = v + return s +} + +func (s *QuerySmsSignListResponseBodySmsSignList) SetSignName(v string) *QuerySmsSignListResponseBodySmsSignList { + s.SignName = &v + return s +} + +type QuerySmsSignListResponseBodySmsSignListReason struct { + RejectDate *string `json:"RejectDate,omitempty" xml:"RejectDate,omitempty"` + RejectInfo *string `json:"RejectInfo,omitempty" xml:"RejectInfo,omitempty"` + RejectSubInfo *string `json:"RejectSubInfo,omitempty" xml:"RejectSubInfo,omitempty"` +} + +func (s QuerySmsSignListResponseBodySmsSignListReason) String() string { + return tea.Prettify(s) +} + +func (s QuerySmsSignListResponseBodySmsSignListReason) GoString() string { + return s.String() +} + +func (s *QuerySmsSignListResponseBodySmsSignListReason) SetRejectDate(v string) *QuerySmsSignListResponseBodySmsSignListReason { + s.RejectDate = &v + return s +} + +func (s *QuerySmsSignListResponseBodySmsSignListReason) SetRejectInfo(v string) *QuerySmsSignListResponseBodySmsSignListReason { + s.RejectInfo = &v + return s +} + +func (s *QuerySmsSignListResponseBodySmsSignListReason) SetRejectSubInfo(v string) *QuerySmsSignListResponseBodySmsSignListReason { + s.RejectSubInfo = &v + return s +} + +type QuerySmsSignListResponse struct { + Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty" require:"true"` + Body *QuerySmsSignListResponseBody `json:"body,omitempty" xml:"body,omitempty" require:"true"` +} + +func (s QuerySmsSignListResponse) String() string { + return tea.Prettify(s) +} + +func (s QuerySmsSignListResponse) GoString() string { + return s.String() +} + +func (s *QuerySmsSignListResponse) SetHeaders(v map[string]*string) *QuerySmsSignListResponse { + s.Headers = v + return s +} + +func (s *QuerySmsSignListResponse) SetBody(v *QuerySmsSignListResponseBody) *QuerySmsSignListResponse { + s.Body = v + return s +} + +type QuerySmsTemplateRequest struct { + OwnerId *int64 `json:"OwnerId,omitempty" xml:"OwnerId,omitempty"` + ResourceOwnerAccount *string `json:"ResourceOwnerAccount,omitempty" xml:"ResourceOwnerAccount,omitempty"` + ResourceOwnerId *int64 `json:"ResourceOwnerId,omitempty" xml:"ResourceOwnerId,omitempty"` + TemplateCode *string `json:"TemplateCode,omitempty" xml:"TemplateCode,omitempty"` +} + +func (s QuerySmsTemplateRequest) String() string { + return tea.Prettify(s) +} + +func (s QuerySmsTemplateRequest) GoString() string { + return s.String() +} + +func (s *QuerySmsTemplateRequest) SetOwnerId(v int64) *QuerySmsTemplateRequest { + s.OwnerId = &v + return s +} + +func (s *QuerySmsTemplateRequest) SetResourceOwnerAccount(v string) *QuerySmsTemplateRequest { + s.ResourceOwnerAccount = &v + return s +} + +func (s *QuerySmsTemplateRequest) SetResourceOwnerId(v int64) *QuerySmsTemplateRequest { + s.ResourceOwnerId = &v + return s +} + +func (s *QuerySmsTemplateRequest) SetTemplateCode(v string) *QuerySmsTemplateRequest { + s.TemplateCode = &v + return s +} + +type QuerySmsTemplateResponseBody struct { + Code *string `json:"Code,omitempty" xml:"Code,omitempty"` + CreateDate *string `json:"CreateDate,omitempty" xml:"CreateDate,omitempty"` + Message *string `json:"Message,omitempty" xml:"Message,omitempty"` + Reason *string `json:"Reason,omitempty" xml:"Reason,omitempty"` + RequestId *string `json:"RequestId,omitempty" xml:"RequestId,omitempty"` + TemplateCode *string `json:"TemplateCode,omitempty" xml:"TemplateCode,omitempty"` + TemplateContent *string `json:"TemplateContent,omitempty" xml:"TemplateContent,omitempty"` + TemplateName *string `json:"TemplateName,omitempty" xml:"TemplateName,omitempty"` + TemplateStatus *int32 `json:"TemplateStatus,omitempty" xml:"TemplateStatus,omitempty"` + TemplateType *int32 `json:"TemplateType,omitempty" xml:"TemplateType,omitempty"` +} + +func (s QuerySmsTemplateResponseBody) String() string { + return tea.Prettify(s) +} + +func (s QuerySmsTemplateResponseBody) GoString() string { + return s.String() +} + +func (s *QuerySmsTemplateResponseBody) SetCode(v string) *QuerySmsTemplateResponseBody { + s.Code = &v + return s +} + +func (s *QuerySmsTemplateResponseBody) SetCreateDate(v string) *QuerySmsTemplateResponseBody { + s.CreateDate = &v + return s +} + +func (s *QuerySmsTemplateResponseBody) SetMessage(v string) *QuerySmsTemplateResponseBody { + s.Message = &v + return s +} + +func (s *QuerySmsTemplateResponseBody) SetReason(v string) *QuerySmsTemplateResponseBody { + s.Reason = &v + return s +} + +func (s *QuerySmsTemplateResponseBody) SetRequestId(v string) *QuerySmsTemplateResponseBody { + s.RequestId = &v + return s +} + +func (s *QuerySmsTemplateResponseBody) SetTemplateCode(v string) *QuerySmsTemplateResponseBody { + s.TemplateCode = &v + return s +} + +func (s *QuerySmsTemplateResponseBody) SetTemplateContent(v string) *QuerySmsTemplateResponseBody { + s.TemplateContent = &v + return s +} + +func (s *QuerySmsTemplateResponseBody) SetTemplateName(v string) *QuerySmsTemplateResponseBody { + s.TemplateName = &v + return s +} + +func (s *QuerySmsTemplateResponseBody) SetTemplateStatus(v int32) *QuerySmsTemplateResponseBody { + s.TemplateStatus = &v + return s +} + +func (s *QuerySmsTemplateResponseBody) SetTemplateType(v int32) *QuerySmsTemplateResponseBody { + s.TemplateType = &v + return s +} + +type QuerySmsTemplateResponse struct { + Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty" require:"true"` + Body *QuerySmsTemplateResponseBody `json:"body,omitempty" xml:"body,omitempty" require:"true"` +} + +func (s QuerySmsTemplateResponse) String() string { + return tea.Prettify(s) +} + +func (s QuerySmsTemplateResponse) GoString() string { + return s.String() +} + +func (s *QuerySmsTemplateResponse) SetHeaders(v map[string]*string) *QuerySmsTemplateResponse { + s.Headers = v + return s +} + +func (s *QuerySmsTemplateResponse) SetBody(v *QuerySmsTemplateResponseBody) *QuerySmsTemplateResponse { + s.Body = v + return s +} + +type QuerySmsTemplateListRequest struct { + OwnerId *int64 `json:"OwnerId,omitempty" xml:"OwnerId,omitempty"` + PageIndex *int32 `json:"PageIndex,omitempty" xml:"PageIndex,omitempty"` + PageSize *int32 `json:"PageSize,omitempty" xml:"PageSize,omitempty"` + ResourceOwnerAccount *string `json:"ResourceOwnerAccount,omitempty" xml:"ResourceOwnerAccount,omitempty"` + ResourceOwnerId *int64 `json:"ResourceOwnerId,omitempty" xml:"ResourceOwnerId,omitempty"` +} + +func (s QuerySmsTemplateListRequest) String() string { + return tea.Prettify(s) +} + +func (s QuerySmsTemplateListRequest) GoString() string { + return s.String() +} + +func (s *QuerySmsTemplateListRequest) SetOwnerId(v int64) *QuerySmsTemplateListRequest { + s.OwnerId = &v + return s +} + +func (s *QuerySmsTemplateListRequest) SetPageIndex(v int32) *QuerySmsTemplateListRequest { + s.PageIndex = &v + return s +} + +func (s *QuerySmsTemplateListRequest) SetPageSize(v int32) *QuerySmsTemplateListRequest { + s.PageSize = &v + return s +} + +func (s *QuerySmsTemplateListRequest) SetResourceOwnerAccount(v string) *QuerySmsTemplateListRequest { + s.ResourceOwnerAccount = &v + return s +} + +func (s *QuerySmsTemplateListRequest) SetResourceOwnerId(v int64) *QuerySmsTemplateListRequest { + s.ResourceOwnerId = &v + return s +} + +type QuerySmsTemplateListResponseBody struct { + Code *string `json:"Code,omitempty" xml:"Code,omitempty"` + Message *string `json:"Message,omitempty" xml:"Message,omitempty"` + RequestId *string `json:"RequestId,omitempty" xml:"RequestId,omitempty"` + SmsTemplateList []*QuerySmsTemplateListResponseBodySmsTemplateList `json:"SmsTemplateList,omitempty" xml:"SmsTemplateList,omitempty" type:"Repeated"` +} + +func (s QuerySmsTemplateListResponseBody) String() string { + return tea.Prettify(s) +} + +func (s QuerySmsTemplateListResponseBody) GoString() string { + return s.String() +} + +func (s *QuerySmsTemplateListResponseBody) SetCode(v string) *QuerySmsTemplateListResponseBody { + s.Code = &v + return s +} + +func (s *QuerySmsTemplateListResponseBody) SetMessage(v string) *QuerySmsTemplateListResponseBody { + s.Message = &v + return s +} + +func (s *QuerySmsTemplateListResponseBody) SetRequestId(v string) *QuerySmsTemplateListResponseBody { + s.RequestId = &v + return s +} + +func (s *QuerySmsTemplateListResponseBody) SetSmsTemplateList(v []*QuerySmsTemplateListResponseBodySmsTemplateList) *QuerySmsTemplateListResponseBody { + s.SmsTemplateList = v + return s +} + +type QuerySmsTemplateListResponseBodySmsTemplateList struct { + AuditStatus *string `json:"AuditStatus,omitempty" xml:"AuditStatus,omitempty"` + CreateDate *string `json:"CreateDate,omitempty" xml:"CreateDate,omitempty"` + OrderId *string `json:"OrderId,omitempty" xml:"OrderId,omitempty"` + Reason *QuerySmsTemplateListResponseBodySmsTemplateListReason `json:"Reason,omitempty" xml:"Reason,omitempty" type:"Struct"` + TemplateCode *string `json:"TemplateCode,omitempty" xml:"TemplateCode,omitempty"` + TemplateContent *string `json:"TemplateContent,omitempty" xml:"TemplateContent,omitempty"` + TemplateName *string `json:"TemplateName,omitempty" xml:"TemplateName,omitempty"` + TemplateType *int32 `json:"TemplateType,omitempty" xml:"TemplateType,omitempty"` +} + +func (s QuerySmsTemplateListResponseBodySmsTemplateList) String() string { + return tea.Prettify(s) +} + +func (s QuerySmsTemplateListResponseBodySmsTemplateList) GoString() string { + return s.String() +} + +func (s *QuerySmsTemplateListResponseBodySmsTemplateList) SetAuditStatus(v string) *QuerySmsTemplateListResponseBodySmsTemplateList { + s.AuditStatus = &v + return s +} + +func (s *QuerySmsTemplateListResponseBodySmsTemplateList) SetCreateDate(v string) *QuerySmsTemplateListResponseBodySmsTemplateList { + s.CreateDate = &v + return s +} + +func (s *QuerySmsTemplateListResponseBodySmsTemplateList) SetOrderId(v string) *QuerySmsTemplateListResponseBodySmsTemplateList { + s.OrderId = &v + return s +} + +func (s *QuerySmsTemplateListResponseBodySmsTemplateList) SetReason(v *QuerySmsTemplateListResponseBodySmsTemplateListReason) *QuerySmsTemplateListResponseBodySmsTemplateList { + s.Reason = v + return s +} + +func (s *QuerySmsTemplateListResponseBodySmsTemplateList) SetTemplateCode(v string) *QuerySmsTemplateListResponseBodySmsTemplateList { + s.TemplateCode = &v + return s +} + +func (s *QuerySmsTemplateListResponseBodySmsTemplateList) SetTemplateContent(v string) *QuerySmsTemplateListResponseBodySmsTemplateList { + s.TemplateContent = &v + return s +} + +func (s *QuerySmsTemplateListResponseBodySmsTemplateList) SetTemplateName(v string) *QuerySmsTemplateListResponseBodySmsTemplateList { + s.TemplateName = &v + return s +} + +func (s *QuerySmsTemplateListResponseBodySmsTemplateList) SetTemplateType(v int32) *QuerySmsTemplateListResponseBodySmsTemplateList { + s.TemplateType = &v + return s +} + +type QuerySmsTemplateListResponseBodySmsTemplateListReason struct { + RejectDate *string `json:"RejectDate,omitempty" xml:"RejectDate,omitempty"` + RejectInfo *string `json:"RejectInfo,omitempty" xml:"RejectInfo,omitempty"` + RejectSubInfo *string `json:"RejectSubInfo,omitempty" xml:"RejectSubInfo,omitempty"` +} + +func (s QuerySmsTemplateListResponseBodySmsTemplateListReason) String() string { + return tea.Prettify(s) +} + +func (s QuerySmsTemplateListResponseBodySmsTemplateListReason) GoString() string { + return s.String() +} + +func (s *QuerySmsTemplateListResponseBodySmsTemplateListReason) SetRejectDate(v string) *QuerySmsTemplateListResponseBodySmsTemplateListReason { + s.RejectDate = &v + return s +} + +func (s *QuerySmsTemplateListResponseBodySmsTemplateListReason) SetRejectInfo(v string) *QuerySmsTemplateListResponseBodySmsTemplateListReason { + s.RejectInfo = &v + return s +} + +func (s *QuerySmsTemplateListResponseBodySmsTemplateListReason) SetRejectSubInfo(v string) *QuerySmsTemplateListResponseBodySmsTemplateListReason { + s.RejectSubInfo = &v + return s +} + +type QuerySmsTemplateListResponse struct { + Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty" require:"true"` + Body *QuerySmsTemplateListResponseBody `json:"body,omitempty" xml:"body,omitempty" require:"true"` +} + +func (s QuerySmsTemplateListResponse) String() string { + return tea.Prettify(s) +} + +func (s QuerySmsTemplateListResponse) GoString() string { + return s.String() +} + +func (s *QuerySmsTemplateListResponse) SetHeaders(v map[string]*string) *QuerySmsTemplateListResponse { + s.Headers = v + return s +} + +func (s *QuerySmsTemplateListResponse) SetBody(v *QuerySmsTemplateListResponseBody) *QuerySmsTemplateListResponse { + s.Body = v + return s +} + +type SendBatchSmsRequest struct { + OwnerId *int64 `json:"OwnerId,omitempty" xml:"OwnerId,omitempty"` + PhoneNumberJson *string `json:"PhoneNumberJson,omitempty" xml:"PhoneNumberJson,omitempty"` + ResourceOwnerAccount *string `json:"ResourceOwnerAccount,omitempty" xml:"ResourceOwnerAccount,omitempty"` + ResourceOwnerId *int64 `json:"ResourceOwnerId,omitempty" xml:"ResourceOwnerId,omitempty"` + SignNameJson *string `json:"SignNameJson,omitempty" xml:"SignNameJson,omitempty"` + SmsUpExtendCodeJson *string `json:"SmsUpExtendCodeJson,omitempty" xml:"SmsUpExtendCodeJson,omitempty"` + TemplateCode *string `json:"TemplateCode,omitempty" xml:"TemplateCode,omitempty"` + TemplateParamJson *string `json:"TemplateParamJson,omitempty" xml:"TemplateParamJson,omitempty"` +} + +func (s SendBatchSmsRequest) String() string { + return tea.Prettify(s) +} + +func (s SendBatchSmsRequest) GoString() string { + return s.String() +} + +func (s *SendBatchSmsRequest) SetOwnerId(v int64) *SendBatchSmsRequest { + s.OwnerId = &v + return s +} + +func (s *SendBatchSmsRequest) SetPhoneNumberJson(v string) *SendBatchSmsRequest { + s.PhoneNumberJson = &v + return s +} + +func (s *SendBatchSmsRequest) SetResourceOwnerAccount(v string) *SendBatchSmsRequest { + s.ResourceOwnerAccount = &v + return s +} + +func (s *SendBatchSmsRequest) SetResourceOwnerId(v int64) *SendBatchSmsRequest { + s.ResourceOwnerId = &v + return s +} + +func (s *SendBatchSmsRequest) SetSignNameJson(v string) *SendBatchSmsRequest { + s.SignNameJson = &v + return s +} + +func (s *SendBatchSmsRequest) SetSmsUpExtendCodeJson(v string) *SendBatchSmsRequest { + s.SmsUpExtendCodeJson = &v + return s +} + +func (s *SendBatchSmsRequest) SetTemplateCode(v string) *SendBatchSmsRequest { + s.TemplateCode = &v + return s +} + +func (s *SendBatchSmsRequest) SetTemplateParamJson(v string) *SendBatchSmsRequest { + s.TemplateParamJson = &v + return s +} + +type SendBatchSmsResponseBody struct { + BizId *string `json:"BizId,omitempty" xml:"BizId,omitempty"` + Code *string `json:"Code,omitempty" xml:"Code,omitempty"` + Message *string `json:"Message,omitempty" xml:"Message,omitempty"` + RequestId *string `json:"RequestId,omitempty" xml:"RequestId,omitempty"` +} + +func (s SendBatchSmsResponseBody) String() string { + return tea.Prettify(s) +} + +func (s SendBatchSmsResponseBody) GoString() string { + return s.String() +} + +func (s *SendBatchSmsResponseBody) SetBizId(v string) *SendBatchSmsResponseBody { + s.BizId = &v + return s +} + +func (s *SendBatchSmsResponseBody) SetCode(v string) *SendBatchSmsResponseBody { + s.Code = &v + return s +} + +func (s *SendBatchSmsResponseBody) SetMessage(v string) *SendBatchSmsResponseBody { + s.Message = &v + return s +} + +func (s *SendBatchSmsResponseBody) SetRequestId(v string) *SendBatchSmsResponseBody { + s.RequestId = &v + return s +} + +type SendBatchSmsResponse struct { + Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty" require:"true"` + Body *SendBatchSmsResponseBody `json:"body,omitempty" xml:"body,omitempty" require:"true"` +} + +func (s SendBatchSmsResponse) String() string { + return tea.Prettify(s) +} + +func (s SendBatchSmsResponse) GoString() string { + return s.String() +} + +func (s *SendBatchSmsResponse) SetHeaders(v map[string]*string) *SendBatchSmsResponse { + s.Headers = v + return s +} + +func (s *SendBatchSmsResponse) SetBody(v *SendBatchSmsResponseBody) *SendBatchSmsResponse { + s.Body = v + return s +} + +type SendSmsRequest struct { + OutId *string `json:"OutId,omitempty" xml:"OutId,omitempty"` + OwnerId *int64 `json:"OwnerId,omitempty" xml:"OwnerId,omitempty"` + PhoneNumbers *string `json:"PhoneNumbers,omitempty" xml:"PhoneNumbers,omitempty"` + ResourceOwnerAccount *string `json:"ResourceOwnerAccount,omitempty" xml:"ResourceOwnerAccount,omitempty"` + ResourceOwnerId *int64 `json:"ResourceOwnerId,omitempty" xml:"ResourceOwnerId,omitempty"` + SignName *string `json:"SignName,omitempty" xml:"SignName,omitempty"` + SmsUpExtendCode *string `json:"SmsUpExtendCode,omitempty" xml:"SmsUpExtendCode,omitempty"` + TemplateCode *string `json:"TemplateCode,omitempty" xml:"TemplateCode,omitempty"` + TemplateParam *string `json:"TemplateParam,omitempty" xml:"TemplateParam,omitempty"` +} + +func (s SendSmsRequest) String() string { + return tea.Prettify(s) +} + +func (s SendSmsRequest) GoString() string { + return s.String() +} + +func (s *SendSmsRequest) SetOutId(v string) *SendSmsRequest { + s.OutId = &v + return s +} + +func (s *SendSmsRequest) SetOwnerId(v int64) *SendSmsRequest { + s.OwnerId = &v + return s +} + +func (s *SendSmsRequest) SetPhoneNumbers(v string) *SendSmsRequest { + s.PhoneNumbers = &v + return s +} + +func (s *SendSmsRequest) SetResourceOwnerAccount(v string) *SendSmsRequest { + s.ResourceOwnerAccount = &v + return s +} + +func (s *SendSmsRequest) SetResourceOwnerId(v int64) *SendSmsRequest { + s.ResourceOwnerId = &v + return s +} + +func (s *SendSmsRequest) SetSignName(v string) *SendSmsRequest { + s.SignName = &v + return s +} + +func (s *SendSmsRequest) SetSmsUpExtendCode(v string) *SendSmsRequest { + s.SmsUpExtendCode = &v + return s +} + +func (s *SendSmsRequest) SetTemplateCode(v string) *SendSmsRequest { + s.TemplateCode = &v + return s +} + +func (s *SendSmsRequest) SetTemplateParam(v string) *SendSmsRequest { + s.TemplateParam = &v + return s +} + +type SendSmsResponseBody struct { + BizId *string `json:"BizId,omitempty" xml:"BizId,omitempty"` + Code *string `json:"Code,omitempty" xml:"Code,omitempty"` + Message *string `json:"Message,omitempty" xml:"Message,omitempty"` + RequestId *string `json:"RequestId,omitempty" xml:"RequestId,omitempty"` +} + +func (s SendSmsResponseBody) String() string { + return tea.Prettify(s) +} + +func (s SendSmsResponseBody) GoString() string { + return s.String() +} + +func (s *SendSmsResponseBody) SetBizId(v string) *SendSmsResponseBody { + s.BizId = &v + return s +} + +func (s *SendSmsResponseBody) SetCode(v string) *SendSmsResponseBody { + s.Code = &v + return s +} + +func (s *SendSmsResponseBody) SetMessage(v string) *SendSmsResponseBody { + s.Message = &v + return s +} + +func (s *SendSmsResponseBody) SetRequestId(v string) *SendSmsResponseBody { + s.RequestId = &v + return s +} + +type SendSmsResponse struct { + Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty" require:"true"` + Body *SendSmsResponseBody `json:"body,omitempty" xml:"body,omitempty" require:"true"` +} + +func (s SendSmsResponse) String() string { + return tea.Prettify(s) +} + +func (s SendSmsResponse) GoString() string { + return s.String() +} + +func (s *SendSmsResponse) SetHeaders(v map[string]*string) *SendSmsResponse { + s.Headers = v + return s +} + +func (s *SendSmsResponse) SetBody(v *SendSmsResponseBody) *SendSmsResponse { + s.Body = v + return s +} + +type TagResourcesRequest struct { + OwnerId *int64 `json:"OwnerId,omitempty" xml:"OwnerId,omitempty"` + ProdCode *string `json:"ProdCode,omitempty" xml:"ProdCode,omitempty"` + RegionId *string `json:"RegionId,omitempty" xml:"RegionId,omitempty"` + ResourceId []*string `json:"ResourceId,omitempty" xml:"ResourceId,omitempty" type:"Repeated"` + ResourceOwnerAccount *string `json:"ResourceOwnerAccount,omitempty" xml:"ResourceOwnerAccount,omitempty"` + ResourceOwnerId *int64 `json:"ResourceOwnerId,omitempty" xml:"ResourceOwnerId,omitempty"` + ResourceType *string `json:"ResourceType,omitempty" xml:"ResourceType,omitempty"` + Tag []*TagResourcesRequestTag `json:"Tag,omitempty" xml:"Tag,omitempty" type:"Repeated"` +} + +func (s TagResourcesRequest) String() string { + return tea.Prettify(s) +} + +func (s TagResourcesRequest) GoString() string { + return s.String() +} + +func (s *TagResourcesRequest) SetOwnerId(v int64) *TagResourcesRequest { + s.OwnerId = &v + return s +} + +func (s *TagResourcesRequest) SetProdCode(v string) *TagResourcesRequest { + s.ProdCode = &v + return s +} + +func (s *TagResourcesRequest) SetRegionId(v string) *TagResourcesRequest { + s.RegionId = &v + return s +} + +func (s *TagResourcesRequest) SetResourceId(v []*string) *TagResourcesRequest { + s.ResourceId = v + return s +} + +func (s *TagResourcesRequest) SetResourceOwnerAccount(v string) *TagResourcesRequest { + s.ResourceOwnerAccount = &v + return s +} + +func (s *TagResourcesRequest) SetResourceOwnerId(v int64) *TagResourcesRequest { + s.ResourceOwnerId = &v + return s +} + +func (s *TagResourcesRequest) SetResourceType(v string) *TagResourcesRequest { + s.ResourceType = &v + return s +} + +func (s *TagResourcesRequest) SetTag(v []*TagResourcesRequestTag) *TagResourcesRequest { + s.Tag = v + return s +} + +type TagResourcesRequestTag struct { + Key *string `json:"Key,omitempty" xml:"Key,omitempty"` + Value *string `json:"Value,omitempty" xml:"Value,omitempty"` +} + +func (s TagResourcesRequestTag) String() string { + return tea.Prettify(s) +} + +func (s TagResourcesRequestTag) GoString() string { + return s.String() +} + +func (s *TagResourcesRequestTag) SetKey(v string) *TagResourcesRequestTag { + s.Key = &v + return s +} + +func (s *TagResourcesRequestTag) SetValue(v string) *TagResourcesRequestTag { + s.Value = &v + return s +} + +type TagResourcesResponseBody struct { + Code *string `json:"Code,omitempty" xml:"Code,omitempty"` + Data *string `json:"Data,omitempty" xml:"Data,omitempty"` + RequestId *string `json:"RequestId,omitempty" xml:"RequestId,omitempty"` +} + +func (s TagResourcesResponseBody) String() string { + return tea.Prettify(s) +} + +func (s TagResourcesResponseBody) GoString() string { + return s.String() +} + +func (s *TagResourcesResponseBody) SetCode(v string) *TagResourcesResponseBody { + s.Code = &v + return s +} + +func (s *TagResourcesResponseBody) SetData(v string) *TagResourcesResponseBody { + s.Data = &v + return s +} + +func (s *TagResourcesResponseBody) SetRequestId(v string) *TagResourcesResponseBody { + s.RequestId = &v + return s +} + +type TagResourcesResponse struct { + Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty" require:"true"` + Body *TagResourcesResponseBody `json:"body,omitempty" xml:"body,omitempty" require:"true"` +} + +func (s TagResourcesResponse) String() string { + return tea.Prettify(s) +} + +func (s TagResourcesResponse) GoString() string { + return s.String() +} + +func (s *TagResourcesResponse) SetHeaders(v map[string]*string) *TagResourcesResponse { + s.Headers = v + return s +} + +func (s *TagResourcesResponse) SetBody(v *TagResourcesResponseBody) *TagResourcesResponse { + s.Body = v + return s +} + +type UntagResourcesRequest struct { + All *bool `json:"All,omitempty" xml:"All,omitempty"` + OwnerId *int64 `json:"OwnerId,omitempty" xml:"OwnerId,omitempty"` + ProdCode *string `json:"ProdCode,omitempty" xml:"ProdCode,omitempty"` + RegionId *string `json:"RegionId,omitempty" xml:"RegionId,omitempty"` + ResourceId []*string `json:"ResourceId,omitempty" xml:"ResourceId,omitempty" type:"Repeated"` + ResourceOwnerAccount *string `json:"ResourceOwnerAccount,omitempty" xml:"ResourceOwnerAccount,omitempty"` + ResourceOwnerId *int64 `json:"ResourceOwnerId,omitempty" xml:"ResourceOwnerId,omitempty"` + ResourceType *string `json:"ResourceType,omitempty" xml:"ResourceType,omitempty"` + TagKey []*string `json:"TagKey,omitempty" xml:"TagKey,omitempty" type:"Repeated"` +} + +func (s UntagResourcesRequest) String() string { + return tea.Prettify(s) +} + +func (s UntagResourcesRequest) GoString() string { + return s.String() +} + +func (s *UntagResourcesRequest) SetAll(v bool) *UntagResourcesRequest { + s.All = &v + return s +} + +func (s *UntagResourcesRequest) SetOwnerId(v int64) *UntagResourcesRequest { + s.OwnerId = &v + return s +} + +func (s *UntagResourcesRequest) SetProdCode(v string) *UntagResourcesRequest { + s.ProdCode = &v + return s +} + +func (s *UntagResourcesRequest) SetRegionId(v string) *UntagResourcesRequest { + s.RegionId = &v + return s +} + +func (s *UntagResourcesRequest) SetResourceId(v []*string) *UntagResourcesRequest { + s.ResourceId = v + return s +} + +func (s *UntagResourcesRequest) SetResourceOwnerAccount(v string) *UntagResourcesRequest { + s.ResourceOwnerAccount = &v + return s +} + +func (s *UntagResourcesRequest) SetResourceOwnerId(v int64) *UntagResourcesRequest { + s.ResourceOwnerId = &v + return s +} + +func (s *UntagResourcesRequest) SetResourceType(v string) *UntagResourcesRequest { + s.ResourceType = &v + return s +} + +func (s *UntagResourcesRequest) SetTagKey(v []*string) *UntagResourcesRequest { + s.TagKey = v + return s +} + +type UntagResourcesResponseBody struct { + Code *string `json:"Code,omitempty" xml:"Code,omitempty"` + Data *string `json:"Data,omitempty" xml:"Data,omitempty"` + RequestId *string `json:"RequestId,omitempty" xml:"RequestId,omitempty"` +} + +func (s UntagResourcesResponseBody) String() string { + return tea.Prettify(s) +} + +func (s UntagResourcesResponseBody) GoString() string { + return s.String() +} + +func (s *UntagResourcesResponseBody) SetCode(v string) *UntagResourcesResponseBody { + s.Code = &v + return s +} + +func (s *UntagResourcesResponseBody) SetData(v string) *UntagResourcesResponseBody { + s.Data = &v + return s +} + +func (s *UntagResourcesResponseBody) SetRequestId(v string) *UntagResourcesResponseBody { + s.RequestId = &v + return s +} + +type UntagResourcesResponse struct { + Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty" require:"true"` + Body *UntagResourcesResponseBody `json:"body,omitempty" xml:"body,omitempty" require:"true"` +} + +func (s UntagResourcesResponse) String() string { + return tea.Prettify(s) +} + +func (s UntagResourcesResponse) GoString() string { + return s.String() +} + +func (s *UntagResourcesResponse) SetHeaders(v map[string]*string) *UntagResourcesResponse { + s.Headers = v + return s +} + +func (s *UntagResourcesResponse) SetBody(v *UntagResourcesResponseBody) *UntagResourcesResponse { + s.Body = v + return s +} + +type Client struct { + openapi.Client +} + +func NewClient(config *openapi.Config) (*Client, error) { + client := new(Client) + err := client.Init(config) + return client, err +} + +func (client *Client) Init(config *openapi.Config) (_err error) { + _err = client.Client.Init(config) + if _err != nil { + return _err + } + client.EndpointRule = tea.String("central") + client.EndpointMap = map[string]*string{ + "ap-southeast-1": tea.String("dysmsapi.ap-southeast-1.aliyuncs.com"), + "ap-southeast-5": tea.String("dysmsapi-xman.ap-southeast-5.aliyuncs.com"), + "cn-beijing": tea.String("dysmsapi-proxy.cn-beijing.aliyuncs.com"), + "cn-hongkong": tea.String("dysmsapi-xman.cn-hongkong.aliyuncs.com"), + } + _err = client.CheckConfig(config) + if _err != nil { + return _err + } + client.Endpoint, _err = client.GetEndpoint(tea.String("dysmsapi"), client.RegionId, client.EndpointRule, client.Network, client.Suffix, client.EndpointMap, client.Endpoint) + if _err != nil { + return _err + } + + return nil +} + +func (client *Client) GetEndpoint(productId *string, regionId *string, endpointRule *string, network *string, suffix *string, endpointMap map[string]*string, endpoint *string) (_result *string, _err error) { + if !tea.BoolValue(util.Empty(endpoint)) { + _result = endpoint + return _result, _err + } + + if !tea.BoolValue(util.IsUnset(endpointMap)) && !tea.BoolValue(util.Empty(endpointMap[tea.StringValue(regionId)])) { + _result = endpointMap[tea.StringValue(regionId)] + return _result, _err + } + + _body, _err := endpointutil.GetEndpointRules(productId, regionId, endpointRule, network, suffix) + if _err != nil { + return _result, _err + } + _result = _body + return _result, _err +} + +func (client *Client) AddShortUrlWithOptions(request *AddShortUrlRequest, runtime *util.RuntimeOptions) (_result *AddShortUrlResponse, _err error) { + _err = util.ValidateModel(request) + if _err != nil { + return _result, _err + } + query := map[string]interface{}{} + if !tea.BoolValue(util.IsUnset(request.OwnerId)) { + query["OwnerId"] = request.OwnerId + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerAccount)) { + query["ResourceOwnerAccount"] = request.ResourceOwnerAccount + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerId)) { + query["ResourceOwnerId"] = request.ResourceOwnerId + } + + body := map[string]interface{}{} + if !tea.BoolValue(util.IsUnset(request.EffectiveDays)) { + body["EffectiveDays"] = request.EffectiveDays + } + + if !tea.BoolValue(util.IsUnset(request.ShortUrlName)) { + body["ShortUrlName"] = request.ShortUrlName + } + + if !tea.BoolValue(util.IsUnset(request.SourceUrl)) { + body["SourceUrl"] = request.SourceUrl + } + + req := &openapi.OpenApiRequest{ + Query: openapiutil.Query(query), + Body: openapiutil.ParseToMap(body), + } + params := &openapi.Params{ + Action: tea.String("AddShortUrl"), + Version: tea.String("2017-05-25"), + Protocol: tea.String("HTTPS"), + Pathname: tea.String("/"), + Method: tea.String("POST"), + AuthType: tea.String("AK"), + Style: tea.String("RPC"), + ReqBodyType: tea.String("formData"), + BodyType: tea.String("json"), + } + _result = &AddShortUrlResponse{} + _body, _err := client.CallApi(params, req, runtime) + if _err != nil { + return _result, _err + } + _err = tea.Convert(_body, &_result) + return _result, _err +} + +func (client *Client) AddShortUrl(request *AddShortUrlRequest) (_result *AddShortUrlResponse, _err error) { + runtime := &util.RuntimeOptions{} + _result = &AddShortUrlResponse{} + _body, _err := client.AddShortUrlWithOptions(request, runtime) + if _err != nil { + return _result, _err + } + _result = _body + return _result, _err +} + +func (client *Client) AddSmsSignWithOptions(request *AddSmsSignRequest, runtime *util.RuntimeOptions) (_result *AddSmsSignResponse, _err error) { + _err = util.ValidateModel(request) + if _err != nil { + return _result, _err + } + query := map[string]interface{}{} + if !tea.BoolValue(util.IsUnset(request.OwnerId)) { + query["OwnerId"] = request.OwnerId + } + + if !tea.BoolValue(util.IsUnset(request.Remark)) { + query["Remark"] = request.Remark + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerAccount)) { + query["ResourceOwnerAccount"] = request.ResourceOwnerAccount + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerId)) { + query["ResourceOwnerId"] = request.ResourceOwnerId + } + + if !tea.BoolValue(util.IsUnset(request.SignName)) { + query["SignName"] = request.SignName + } + + if !tea.BoolValue(util.IsUnset(request.SignSource)) { + query["SignSource"] = request.SignSource + } + + body := map[string]interface{}{} + if !tea.BoolValue(util.IsUnset(request.SignFileList)) { + body["SignFileList"] = request.SignFileList + } + + req := &openapi.OpenApiRequest{ + Query: openapiutil.Query(query), + Body: openapiutil.ParseToMap(body), + } + params := &openapi.Params{ + Action: tea.String("AddSmsSign"), + Version: tea.String("2017-05-25"), + Protocol: tea.String("HTTPS"), + Pathname: tea.String("/"), + Method: tea.String("POST"), + AuthType: tea.String("AK"), + Style: tea.String("RPC"), + ReqBodyType: tea.String("formData"), + BodyType: tea.String("json"), + } + _result = &AddSmsSignResponse{} + _body, _err := client.CallApi(params, req, runtime) + if _err != nil { + return _result, _err + } + _err = tea.Convert(_body, &_result) + return _result, _err +} + +func (client *Client) AddSmsSign(request *AddSmsSignRequest) (_result *AddSmsSignResponse, _err error) { + runtime := &util.RuntimeOptions{} + _result = &AddSmsSignResponse{} + _body, _err := client.AddSmsSignWithOptions(request, runtime) + if _err != nil { + return _result, _err + } + _result = _body + return _result, _err +} + +func (client *Client) AddSmsTemplateWithOptions(request *AddSmsTemplateRequest, runtime *util.RuntimeOptions) (_result *AddSmsTemplateResponse, _err error) { + _err = util.ValidateModel(request) + if _err != nil { + return _result, _err + } + query := map[string]interface{}{} + if !tea.BoolValue(util.IsUnset(request.OwnerId)) { + query["OwnerId"] = request.OwnerId + } + + if !tea.BoolValue(util.IsUnset(request.Remark)) { + query["Remark"] = request.Remark + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerAccount)) { + query["ResourceOwnerAccount"] = request.ResourceOwnerAccount + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerId)) { + query["ResourceOwnerId"] = request.ResourceOwnerId + } + + if !tea.BoolValue(util.IsUnset(request.TemplateContent)) { + query["TemplateContent"] = request.TemplateContent + } + + if !tea.BoolValue(util.IsUnset(request.TemplateName)) { + query["TemplateName"] = request.TemplateName + } + + if !tea.BoolValue(util.IsUnset(request.TemplateType)) { + query["TemplateType"] = request.TemplateType + } + + req := &openapi.OpenApiRequest{ + Query: openapiutil.Query(query), + } + params := &openapi.Params{ + Action: tea.String("AddSmsTemplate"), + Version: tea.String("2017-05-25"), + Protocol: tea.String("HTTPS"), + Pathname: tea.String("/"), + Method: tea.String("POST"), + AuthType: tea.String("AK"), + Style: tea.String("RPC"), + ReqBodyType: tea.String("formData"), + BodyType: tea.String("json"), + } + _result = &AddSmsTemplateResponse{} + _body, _err := client.CallApi(params, req, runtime) + if _err != nil { + return _result, _err + } + _err = tea.Convert(_body, &_result) + return _result, _err +} + +func (client *Client) AddSmsTemplate(request *AddSmsTemplateRequest) (_result *AddSmsTemplateResponse, _err error) { + runtime := &util.RuntimeOptions{} + _result = &AddSmsTemplateResponse{} + _body, _err := client.AddSmsTemplateWithOptions(request, runtime) + if _err != nil { + return _result, _err + } + _result = _body + return _result, _err +} + +func (client *Client) DeleteShortUrlWithOptions(request *DeleteShortUrlRequest, runtime *util.RuntimeOptions) (_result *DeleteShortUrlResponse, _err error) { + _err = util.ValidateModel(request) + if _err != nil { + return _result, _err + } + query := map[string]interface{}{} + if !tea.BoolValue(util.IsUnset(request.OwnerId)) { + query["OwnerId"] = request.OwnerId + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerAccount)) { + query["ResourceOwnerAccount"] = request.ResourceOwnerAccount + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerId)) { + query["ResourceOwnerId"] = request.ResourceOwnerId + } + + body := map[string]interface{}{} + if !tea.BoolValue(util.IsUnset(request.SourceUrl)) { + body["SourceUrl"] = request.SourceUrl + } + + req := &openapi.OpenApiRequest{ + Query: openapiutil.Query(query), + Body: openapiutil.ParseToMap(body), + } + params := &openapi.Params{ + Action: tea.String("DeleteShortUrl"), + Version: tea.String("2017-05-25"), + Protocol: tea.String("HTTPS"), + Pathname: tea.String("/"), + Method: tea.String("POST"), + AuthType: tea.String("AK"), + Style: tea.String("RPC"), + ReqBodyType: tea.String("formData"), + BodyType: tea.String("json"), + } + _result = &DeleteShortUrlResponse{} + _body, _err := client.CallApi(params, req, runtime) + if _err != nil { + return _result, _err + } + _err = tea.Convert(_body, &_result) + return _result, _err +} + +func (client *Client) DeleteShortUrl(request *DeleteShortUrlRequest) (_result *DeleteShortUrlResponse, _err error) { + runtime := &util.RuntimeOptions{} + _result = &DeleteShortUrlResponse{} + _body, _err := client.DeleteShortUrlWithOptions(request, runtime) + if _err != nil { + return _result, _err + } + _result = _body + return _result, _err +} + +func (client *Client) DeleteSmsSignWithOptions(request *DeleteSmsSignRequest, runtime *util.RuntimeOptions) (_result *DeleteSmsSignResponse, _err error) { + _err = util.ValidateModel(request) + if _err != nil { + return _result, _err + } + query := map[string]interface{}{} + if !tea.BoolValue(util.IsUnset(request.OwnerId)) { + query["OwnerId"] = request.OwnerId + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerAccount)) { + query["ResourceOwnerAccount"] = request.ResourceOwnerAccount + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerId)) { + query["ResourceOwnerId"] = request.ResourceOwnerId + } + + if !tea.BoolValue(util.IsUnset(request.SignName)) { + query["SignName"] = request.SignName + } + + req := &openapi.OpenApiRequest{ + Query: openapiutil.Query(query), + } + params := &openapi.Params{ + Action: tea.String("DeleteSmsSign"), + Version: tea.String("2017-05-25"), + Protocol: tea.String("HTTPS"), + Pathname: tea.String("/"), + Method: tea.String("POST"), + AuthType: tea.String("AK"), + Style: tea.String("RPC"), + ReqBodyType: tea.String("formData"), + BodyType: tea.String("json"), + } + _result = &DeleteSmsSignResponse{} + _body, _err := client.CallApi(params, req, runtime) + if _err != nil { + return _result, _err + } + _err = tea.Convert(_body, &_result) + return _result, _err +} + +func (client *Client) DeleteSmsSign(request *DeleteSmsSignRequest) (_result *DeleteSmsSignResponse, _err error) { + runtime := &util.RuntimeOptions{} + _result = &DeleteSmsSignResponse{} + _body, _err := client.DeleteSmsSignWithOptions(request, runtime) + if _err != nil { + return _result, _err + } + _result = _body + return _result, _err +} + +func (client *Client) DeleteSmsTemplateWithOptions(request *DeleteSmsTemplateRequest, runtime *util.RuntimeOptions) (_result *DeleteSmsTemplateResponse, _err error) { + _err = util.ValidateModel(request) + if _err != nil { + return _result, _err + } + query := map[string]interface{}{} + if !tea.BoolValue(util.IsUnset(request.OwnerId)) { + query["OwnerId"] = request.OwnerId + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerAccount)) { + query["ResourceOwnerAccount"] = request.ResourceOwnerAccount + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerId)) { + query["ResourceOwnerId"] = request.ResourceOwnerId + } + + if !tea.BoolValue(util.IsUnset(request.TemplateCode)) { + query["TemplateCode"] = request.TemplateCode + } + + req := &openapi.OpenApiRequest{ + Query: openapiutil.Query(query), + } + params := &openapi.Params{ + Action: tea.String("DeleteSmsTemplate"), + Version: tea.String("2017-05-25"), + Protocol: tea.String("HTTPS"), + Pathname: tea.String("/"), + Method: tea.String("POST"), + AuthType: tea.String("AK"), + Style: tea.String("RPC"), + ReqBodyType: tea.String("formData"), + BodyType: tea.String("json"), + } + _result = &DeleteSmsTemplateResponse{} + _body, _err := client.CallApi(params, req, runtime) + if _err != nil { + return _result, _err + } + _err = tea.Convert(_body, &_result) + return _result, _err +} + +func (client *Client) DeleteSmsTemplate(request *DeleteSmsTemplateRequest) (_result *DeleteSmsTemplateResponse, _err error) { + runtime := &util.RuntimeOptions{} + _result = &DeleteSmsTemplateResponse{} + _body, _err := client.DeleteSmsTemplateWithOptions(request, runtime) + if _err != nil { + return _result, _err + } + _result = _body + return _result, _err +} + +func (client *Client) ListTagResourcesWithOptions(request *ListTagResourcesRequest, runtime *util.RuntimeOptions) (_result *ListTagResourcesResponse, _err error) { + _err = util.ValidateModel(request) + if _err != nil { + return _result, _err + } + query := map[string]interface{}{} + if !tea.BoolValue(util.IsUnset(request.NextToken)) { + query["NextToken"] = request.NextToken + } + + if !tea.BoolValue(util.IsUnset(request.OwnerId)) { + query["OwnerId"] = request.OwnerId + } + + if !tea.BoolValue(util.IsUnset(request.PageSize)) { + query["PageSize"] = request.PageSize + } + + if !tea.BoolValue(util.IsUnset(request.ProdCode)) { + query["ProdCode"] = request.ProdCode + } + + if !tea.BoolValue(util.IsUnset(request.RegionId)) { + query["RegionId"] = request.RegionId + } + + if !tea.BoolValue(util.IsUnset(request.ResourceId)) { + query["ResourceId"] = request.ResourceId + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerAccount)) { + query["ResourceOwnerAccount"] = request.ResourceOwnerAccount + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerId)) { + query["ResourceOwnerId"] = request.ResourceOwnerId + } + + if !tea.BoolValue(util.IsUnset(request.ResourceType)) { + query["ResourceType"] = request.ResourceType + } + + if !tea.BoolValue(util.IsUnset(request.Tag)) { + query["Tag"] = request.Tag + } + + req := &openapi.OpenApiRequest{ + Query: openapiutil.Query(query), + } + params := &openapi.Params{ + Action: tea.String("ListTagResources"), + Version: tea.String("2017-05-25"), + Protocol: tea.String("HTTPS"), + Pathname: tea.String("/"), + Method: tea.String("POST"), + AuthType: tea.String("AK"), + Style: tea.String("RPC"), + ReqBodyType: tea.String("formData"), + BodyType: tea.String("json"), + } + _result = &ListTagResourcesResponse{} + _body, _err := client.CallApi(params, req, runtime) + if _err != nil { + return _result, _err + } + _err = tea.Convert(_body, &_result) + return _result, _err +} + +func (client *Client) ListTagResources(request *ListTagResourcesRequest) (_result *ListTagResourcesResponse, _err error) { + runtime := &util.RuntimeOptions{} + _result = &ListTagResourcesResponse{} + _body, _err := client.ListTagResourcesWithOptions(request, runtime) + if _err != nil { + return _result, _err + } + _result = _body + return _result, _err +} + +func (client *Client) ModifySmsSignWithOptions(request *ModifySmsSignRequest, runtime *util.RuntimeOptions) (_result *ModifySmsSignResponse, _err error) { + _err = util.ValidateModel(request) + if _err != nil { + return _result, _err + } + query := map[string]interface{}{} + if !tea.BoolValue(util.IsUnset(request.OwnerId)) { + query["OwnerId"] = request.OwnerId + } + + if !tea.BoolValue(util.IsUnset(request.Remark)) { + query["Remark"] = request.Remark + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerAccount)) { + query["ResourceOwnerAccount"] = request.ResourceOwnerAccount + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerId)) { + query["ResourceOwnerId"] = request.ResourceOwnerId + } + + if !tea.BoolValue(util.IsUnset(request.SignName)) { + query["SignName"] = request.SignName + } + + if !tea.BoolValue(util.IsUnset(request.SignSource)) { + query["SignSource"] = request.SignSource + } + + body := map[string]interface{}{} + if !tea.BoolValue(util.IsUnset(request.SignFileList)) { + body["SignFileList"] = request.SignFileList + } + + req := &openapi.OpenApiRequest{ + Query: openapiutil.Query(query), + Body: openapiutil.ParseToMap(body), + } + params := &openapi.Params{ + Action: tea.String("ModifySmsSign"), + Version: tea.String("2017-05-25"), + Protocol: tea.String("HTTPS"), + Pathname: tea.String("/"), + Method: tea.String("POST"), + AuthType: tea.String("AK"), + Style: tea.String("RPC"), + ReqBodyType: tea.String("formData"), + BodyType: tea.String("json"), + } + _result = &ModifySmsSignResponse{} + _body, _err := client.CallApi(params, req, runtime) + if _err != nil { + return _result, _err + } + _err = tea.Convert(_body, &_result) + return _result, _err +} + +func (client *Client) ModifySmsSign(request *ModifySmsSignRequest) (_result *ModifySmsSignResponse, _err error) { + runtime := &util.RuntimeOptions{} + _result = &ModifySmsSignResponse{} + _body, _err := client.ModifySmsSignWithOptions(request, runtime) + if _err != nil { + return _result, _err + } + _result = _body + return _result, _err +} + +func (client *Client) ModifySmsTemplateWithOptions(request *ModifySmsTemplateRequest, runtime *util.RuntimeOptions) (_result *ModifySmsTemplateResponse, _err error) { + _err = util.ValidateModel(request) + if _err != nil { + return _result, _err + } + query := map[string]interface{}{} + if !tea.BoolValue(util.IsUnset(request.OwnerId)) { + query["OwnerId"] = request.OwnerId + } + + if !tea.BoolValue(util.IsUnset(request.Remark)) { + query["Remark"] = request.Remark + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerAccount)) { + query["ResourceOwnerAccount"] = request.ResourceOwnerAccount + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerId)) { + query["ResourceOwnerId"] = request.ResourceOwnerId + } + + if !tea.BoolValue(util.IsUnset(request.TemplateCode)) { + query["TemplateCode"] = request.TemplateCode + } + + if !tea.BoolValue(util.IsUnset(request.TemplateContent)) { + query["TemplateContent"] = request.TemplateContent + } + + if !tea.BoolValue(util.IsUnset(request.TemplateName)) { + query["TemplateName"] = request.TemplateName + } + + if !tea.BoolValue(util.IsUnset(request.TemplateType)) { + query["TemplateType"] = request.TemplateType + } + + req := &openapi.OpenApiRequest{ + Query: openapiutil.Query(query), + } + params := &openapi.Params{ + Action: tea.String("ModifySmsTemplate"), + Version: tea.String("2017-05-25"), + Protocol: tea.String("HTTPS"), + Pathname: tea.String("/"), + Method: tea.String("POST"), + AuthType: tea.String("AK"), + Style: tea.String("RPC"), + ReqBodyType: tea.String("formData"), + BodyType: tea.String("json"), + } + _result = &ModifySmsTemplateResponse{} + _body, _err := client.CallApi(params, req, runtime) + if _err != nil { + return _result, _err + } + _err = tea.Convert(_body, &_result) + return _result, _err +} + +func (client *Client) ModifySmsTemplate(request *ModifySmsTemplateRequest) (_result *ModifySmsTemplateResponse, _err error) { + runtime := &util.RuntimeOptions{} + _result = &ModifySmsTemplateResponse{} + _body, _err := client.ModifySmsTemplateWithOptions(request, runtime) + if _err != nil { + return _result, _err + } + _result = _body + return _result, _err +} + +func (client *Client) QuerySendDetailsWithOptions(request *QuerySendDetailsRequest, runtime *util.RuntimeOptions) (_result *QuerySendDetailsResponse, _err error) { + _err = util.ValidateModel(request) + if _err != nil { + return _result, _err + } + query := map[string]interface{}{} + if !tea.BoolValue(util.IsUnset(request.BizId)) { + query["BizId"] = request.BizId + } + + if !tea.BoolValue(util.IsUnset(request.CurrentPage)) { + query["CurrentPage"] = request.CurrentPage + } + + if !tea.BoolValue(util.IsUnset(request.OwnerId)) { + query["OwnerId"] = request.OwnerId + } + + if !tea.BoolValue(util.IsUnset(request.PageSize)) { + query["PageSize"] = request.PageSize + } + + if !tea.BoolValue(util.IsUnset(request.PhoneNumber)) { + query["PhoneNumber"] = request.PhoneNumber + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerAccount)) { + query["ResourceOwnerAccount"] = request.ResourceOwnerAccount + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerId)) { + query["ResourceOwnerId"] = request.ResourceOwnerId + } + + if !tea.BoolValue(util.IsUnset(request.SendDate)) { + query["SendDate"] = request.SendDate + } + + req := &openapi.OpenApiRequest{ + Query: openapiutil.Query(query), + } + params := &openapi.Params{ + Action: tea.String("QuerySendDetails"), + Version: tea.String("2017-05-25"), + Protocol: tea.String("HTTPS"), + Pathname: tea.String("/"), + Method: tea.String("POST"), + AuthType: tea.String("AK"), + Style: tea.String("RPC"), + ReqBodyType: tea.String("formData"), + BodyType: tea.String("json"), + } + _result = &QuerySendDetailsResponse{} + _body, _err := client.CallApi(params, req, runtime) + if _err != nil { + return _result, _err + } + _err = tea.Convert(_body, &_result) + return _result, _err +} + +func (client *Client) QuerySendDetails(request *QuerySendDetailsRequest) (_result *QuerySendDetailsResponse, _err error) { + runtime := &util.RuntimeOptions{} + _result = &QuerySendDetailsResponse{} + _body, _err := client.QuerySendDetailsWithOptions(request, runtime) + if _err != nil { + return _result, _err + } + _result = _body + return _result, _err +} + +func (client *Client) QuerySendStatisticsWithOptions(request *QuerySendStatisticsRequest, runtime *util.RuntimeOptions) (_result *QuerySendStatisticsResponse, _err error) { + _err = util.ValidateModel(request) + if _err != nil { + return _result, _err + } + query := map[string]interface{}{} + if !tea.BoolValue(util.IsUnset(request.EndDate)) { + query["EndDate"] = request.EndDate + } + + if !tea.BoolValue(util.IsUnset(request.IsGlobe)) { + query["IsGlobe"] = request.IsGlobe + } + + if !tea.BoolValue(util.IsUnset(request.OwnerId)) { + query["OwnerId"] = request.OwnerId + } + + if !tea.BoolValue(util.IsUnset(request.PageIndex)) { + query["PageIndex"] = request.PageIndex + } + + if !tea.BoolValue(util.IsUnset(request.PageSize)) { + query["PageSize"] = request.PageSize + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerAccount)) { + query["ResourceOwnerAccount"] = request.ResourceOwnerAccount + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerId)) { + query["ResourceOwnerId"] = request.ResourceOwnerId + } + + if !tea.BoolValue(util.IsUnset(request.StartDate)) { + query["StartDate"] = request.StartDate + } + + req := &openapi.OpenApiRequest{ + Query: openapiutil.Query(query), + } + params := &openapi.Params{ + Action: tea.String("QuerySendStatistics"), + Version: tea.String("2017-05-25"), + Protocol: tea.String("HTTPS"), + Pathname: tea.String("/"), + Method: tea.String("POST"), + AuthType: tea.String("AK"), + Style: tea.String("RPC"), + ReqBodyType: tea.String("formData"), + BodyType: tea.String("json"), + } + _result = &QuerySendStatisticsResponse{} + _body, _err := client.CallApi(params, req, runtime) + if _err != nil { + return _result, _err + } + _err = tea.Convert(_body, &_result) + return _result, _err +} + +func (client *Client) QuerySendStatistics(request *QuerySendStatisticsRequest) (_result *QuerySendStatisticsResponse, _err error) { + runtime := &util.RuntimeOptions{} + _result = &QuerySendStatisticsResponse{} + _body, _err := client.QuerySendStatisticsWithOptions(request, runtime) + if _err != nil { + return _result, _err + } + _result = _body + return _result, _err +} + +func (client *Client) QueryShortUrlWithOptions(request *QueryShortUrlRequest, runtime *util.RuntimeOptions) (_result *QueryShortUrlResponse, _err error) { + _err = util.ValidateModel(request) + if _err != nil { + return _result, _err + } + query := map[string]interface{}{} + if !tea.BoolValue(util.IsUnset(request.OwnerId)) { + query["OwnerId"] = request.OwnerId + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerAccount)) { + query["ResourceOwnerAccount"] = request.ResourceOwnerAccount + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerId)) { + query["ResourceOwnerId"] = request.ResourceOwnerId + } + + body := map[string]interface{}{} + if !tea.BoolValue(util.IsUnset(request.ShortUrl)) { + body["ShortUrl"] = request.ShortUrl + } + + req := &openapi.OpenApiRequest{ + Query: openapiutil.Query(query), + Body: openapiutil.ParseToMap(body), + } + params := &openapi.Params{ + Action: tea.String("QueryShortUrl"), + Version: tea.String("2017-05-25"), + Protocol: tea.String("HTTPS"), + Pathname: tea.String("/"), + Method: tea.String("POST"), + AuthType: tea.String("AK"), + Style: tea.String("RPC"), + ReqBodyType: tea.String("formData"), + BodyType: tea.String("json"), + } + _result = &QueryShortUrlResponse{} + _body, _err := client.CallApi(params, req, runtime) + if _err != nil { + return _result, _err + } + _err = tea.Convert(_body, &_result) + return _result, _err +} + +func (client *Client) QueryShortUrl(request *QueryShortUrlRequest) (_result *QueryShortUrlResponse, _err error) { + runtime := &util.RuntimeOptions{} + _result = &QueryShortUrlResponse{} + _body, _err := client.QueryShortUrlWithOptions(request, runtime) + if _err != nil { + return _result, _err + } + _result = _body + return _result, _err +} + +func (client *Client) QuerySmsSignWithOptions(request *QuerySmsSignRequest, runtime *util.RuntimeOptions) (_result *QuerySmsSignResponse, _err error) { + _err = util.ValidateModel(request) + if _err != nil { + return _result, _err + } + query := map[string]interface{}{} + if !tea.BoolValue(util.IsUnset(request.OwnerId)) { + query["OwnerId"] = request.OwnerId + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerAccount)) { + query["ResourceOwnerAccount"] = request.ResourceOwnerAccount + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerId)) { + query["ResourceOwnerId"] = request.ResourceOwnerId + } + + if !tea.BoolValue(util.IsUnset(request.SignName)) { + query["SignName"] = request.SignName + } + + req := &openapi.OpenApiRequest{ + Query: openapiutil.Query(query), + } + params := &openapi.Params{ + Action: tea.String("QuerySmsSign"), + Version: tea.String("2017-05-25"), + Protocol: tea.String("HTTPS"), + Pathname: tea.String("/"), + Method: tea.String("POST"), + AuthType: tea.String("AK"), + Style: tea.String("RPC"), + ReqBodyType: tea.String("formData"), + BodyType: tea.String("json"), + } + _result = &QuerySmsSignResponse{} + _body, _err := client.CallApi(params, req, runtime) + if _err != nil { + return _result, _err + } + _err = tea.Convert(_body, &_result) + return _result, _err +} + +func (client *Client) QuerySmsSign(request *QuerySmsSignRequest) (_result *QuerySmsSignResponse, _err error) { + runtime := &util.RuntimeOptions{} + _result = &QuerySmsSignResponse{} + _body, _err := client.QuerySmsSignWithOptions(request, runtime) + if _err != nil { + return _result, _err + } + _result = _body + return _result, _err +} + +func (client *Client) QuerySmsSignListWithOptions(request *QuerySmsSignListRequest, runtime *util.RuntimeOptions) (_result *QuerySmsSignListResponse, _err error) { + _err = util.ValidateModel(request) + if _err != nil { + return _result, _err + } + query := map[string]interface{}{} + if !tea.BoolValue(util.IsUnset(request.OwnerId)) { + query["OwnerId"] = request.OwnerId + } + + if !tea.BoolValue(util.IsUnset(request.PageIndex)) { + query["PageIndex"] = request.PageIndex + } + + if !tea.BoolValue(util.IsUnset(request.PageSize)) { + query["PageSize"] = request.PageSize + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerAccount)) { + query["ResourceOwnerAccount"] = request.ResourceOwnerAccount + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerId)) { + query["ResourceOwnerId"] = request.ResourceOwnerId + } + + req := &openapi.OpenApiRequest{ + Query: openapiutil.Query(query), + } + params := &openapi.Params{ + Action: tea.String("QuerySmsSignList"), + Version: tea.String("2017-05-25"), + Protocol: tea.String("HTTPS"), + Pathname: tea.String("/"), + Method: tea.String("POST"), + AuthType: tea.String("AK"), + Style: tea.String("RPC"), + ReqBodyType: tea.String("formData"), + BodyType: tea.String("json"), + } + _result = &QuerySmsSignListResponse{} + _body, _err := client.CallApi(params, req, runtime) + if _err != nil { + return _result, _err + } + _err = tea.Convert(_body, &_result) + return _result, _err +} + +func (client *Client) QuerySmsSignList(request *QuerySmsSignListRequest) (_result *QuerySmsSignListResponse, _err error) { + runtime := &util.RuntimeOptions{} + _result = &QuerySmsSignListResponse{} + _body, _err := client.QuerySmsSignListWithOptions(request, runtime) + if _err != nil { + return _result, _err + } + _result = _body + return _result, _err +} + +func (client *Client) QuerySmsTemplateWithOptions(request *QuerySmsTemplateRequest, runtime *util.RuntimeOptions) (_result *QuerySmsTemplateResponse, _err error) { + _err = util.ValidateModel(request) + if _err != nil { + return _result, _err + } + query := map[string]interface{}{} + if !tea.BoolValue(util.IsUnset(request.OwnerId)) { + query["OwnerId"] = request.OwnerId + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerAccount)) { + query["ResourceOwnerAccount"] = request.ResourceOwnerAccount + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerId)) { + query["ResourceOwnerId"] = request.ResourceOwnerId + } + + if !tea.BoolValue(util.IsUnset(request.TemplateCode)) { + query["TemplateCode"] = request.TemplateCode + } + + req := &openapi.OpenApiRequest{ + Query: openapiutil.Query(query), + } + params := &openapi.Params{ + Action: tea.String("QuerySmsTemplate"), + Version: tea.String("2017-05-25"), + Protocol: tea.String("HTTPS"), + Pathname: tea.String("/"), + Method: tea.String("POST"), + AuthType: tea.String("AK"), + Style: tea.String("RPC"), + ReqBodyType: tea.String("formData"), + BodyType: tea.String("json"), + } + _result = &QuerySmsTemplateResponse{} + _body, _err := client.CallApi(params, req, runtime) + if _err != nil { + return _result, _err + } + _err = tea.Convert(_body, &_result) + return _result, _err +} + +func (client *Client) QuerySmsTemplate(request *QuerySmsTemplateRequest) (_result *QuerySmsTemplateResponse, _err error) { + runtime := &util.RuntimeOptions{} + _result = &QuerySmsTemplateResponse{} + _body, _err := client.QuerySmsTemplateWithOptions(request, runtime) + if _err != nil { + return _result, _err + } + _result = _body + return _result, _err +} + +func (client *Client) QuerySmsTemplateListWithOptions(request *QuerySmsTemplateListRequest, runtime *util.RuntimeOptions) (_result *QuerySmsTemplateListResponse, _err error) { + _err = util.ValidateModel(request) + if _err != nil { + return _result, _err + } + query := map[string]interface{}{} + if !tea.BoolValue(util.IsUnset(request.OwnerId)) { + query["OwnerId"] = request.OwnerId + } + + if !tea.BoolValue(util.IsUnset(request.PageIndex)) { + query["PageIndex"] = request.PageIndex + } + + if !tea.BoolValue(util.IsUnset(request.PageSize)) { + query["PageSize"] = request.PageSize + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerAccount)) { + query["ResourceOwnerAccount"] = request.ResourceOwnerAccount + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerId)) { + query["ResourceOwnerId"] = request.ResourceOwnerId + } + + req := &openapi.OpenApiRequest{ + Query: openapiutil.Query(query), + } + params := &openapi.Params{ + Action: tea.String("QuerySmsTemplateList"), + Version: tea.String("2017-05-25"), + Protocol: tea.String("HTTPS"), + Pathname: tea.String("/"), + Method: tea.String("POST"), + AuthType: tea.String("AK"), + Style: tea.String("RPC"), + ReqBodyType: tea.String("formData"), + BodyType: tea.String("json"), + } + _result = &QuerySmsTemplateListResponse{} + _body, _err := client.CallApi(params, req, runtime) + if _err != nil { + return _result, _err + } + _err = tea.Convert(_body, &_result) + return _result, _err +} + +func (client *Client) QuerySmsTemplateList(request *QuerySmsTemplateListRequest) (_result *QuerySmsTemplateListResponse, _err error) { + runtime := &util.RuntimeOptions{} + _result = &QuerySmsTemplateListResponse{} + _body, _err := client.QuerySmsTemplateListWithOptions(request, runtime) + if _err != nil { + return _result, _err + } + _result = _body + return _result, _err +} + +func (client *Client) SendBatchSmsWithOptions(request *SendBatchSmsRequest, runtime *util.RuntimeOptions) (_result *SendBatchSmsResponse, _err error) { + _err = util.ValidateModel(request) + if _err != nil { + return _result, _err + } + query := map[string]interface{}{} + if !tea.BoolValue(util.IsUnset(request.OwnerId)) { + query["OwnerId"] = request.OwnerId + } + + if !tea.BoolValue(util.IsUnset(request.PhoneNumberJson)) { + query["PhoneNumberJson"] = request.PhoneNumberJson + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerAccount)) { + query["ResourceOwnerAccount"] = request.ResourceOwnerAccount + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerId)) { + query["ResourceOwnerId"] = request.ResourceOwnerId + } + + if !tea.BoolValue(util.IsUnset(request.SignNameJson)) { + query["SignNameJson"] = request.SignNameJson + } + + if !tea.BoolValue(util.IsUnset(request.SmsUpExtendCodeJson)) { + query["SmsUpExtendCodeJson"] = request.SmsUpExtendCodeJson + } + + if !tea.BoolValue(util.IsUnset(request.TemplateCode)) { + query["TemplateCode"] = request.TemplateCode + } + + if !tea.BoolValue(util.IsUnset(request.TemplateParamJson)) { + query["TemplateParamJson"] = request.TemplateParamJson + } + + req := &openapi.OpenApiRequest{ + Query: openapiutil.Query(query), + } + params := &openapi.Params{ + Action: tea.String("SendBatchSms"), + Version: tea.String("2017-05-25"), + Protocol: tea.String("HTTPS"), + Pathname: tea.String("/"), + Method: tea.String("POST"), + AuthType: tea.String("AK"), + Style: tea.String("RPC"), + ReqBodyType: tea.String("formData"), + BodyType: tea.String("json"), + } + _result = &SendBatchSmsResponse{} + _body, _err := client.CallApi(params, req, runtime) + if _err != nil { + return _result, _err + } + _err = tea.Convert(_body, &_result) + return _result, _err +} + +func (client *Client) SendBatchSms(request *SendBatchSmsRequest) (_result *SendBatchSmsResponse, _err error) { + runtime := &util.RuntimeOptions{} + _result = &SendBatchSmsResponse{} + _body, _err := client.SendBatchSmsWithOptions(request, runtime) + if _err != nil { + return _result, _err + } + _result = _body + return _result, _err +} + +func (client *Client) SendSmsWithOptions(request *SendSmsRequest, runtime *util.RuntimeOptions) (_result *SendSmsResponse, _err error) { + _err = util.ValidateModel(request) + if _err != nil { + return _result, _err + } + query := map[string]interface{}{} + if !tea.BoolValue(util.IsUnset(request.OutId)) { + query["OutId"] = request.OutId + } + + if !tea.BoolValue(util.IsUnset(request.OwnerId)) { + query["OwnerId"] = request.OwnerId + } + + if !tea.BoolValue(util.IsUnset(request.PhoneNumbers)) { + query["PhoneNumbers"] = request.PhoneNumbers + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerAccount)) { + query["ResourceOwnerAccount"] = request.ResourceOwnerAccount + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerId)) { + query["ResourceOwnerId"] = request.ResourceOwnerId + } + + if !tea.BoolValue(util.IsUnset(request.SignName)) { + query["SignName"] = request.SignName + } + + if !tea.BoolValue(util.IsUnset(request.SmsUpExtendCode)) { + query["SmsUpExtendCode"] = request.SmsUpExtendCode + } + + if !tea.BoolValue(util.IsUnset(request.TemplateCode)) { + query["TemplateCode"] = request.TemplateCode + } + + if !tea.BoolValue(util.IsUnset(request.TemplateParam)) { + query["TemplateParam"] = request.TemplateParam + } + + req := &openapi.OpenApiRequest{ + Query: openapiutil.Query(query), + } + params := &openapi.Params{ + Action: tea.String("SendSms"), + Version: tea.String("2017-05-25"), + Protocol: tea.String("HTTPS"), + Pathname: tea.String("/"), + Method: tea.String("POST"), + AuthType: tea.String("AK"), + Style: tea.String("RPC"), + ReqBodyType: tea.String("formData"), + BodyType: tea.String("json"), + } + _result = &SendSmsResponse{} + _body, _err := client.CallApi(params, req, runtime) + if _err != nil { + return _result, _err + } + _err = tea.Convert(_body, &_result) + return _result, _err +} + +func (client *Client) SendSms(request *SendSmsRequest) (_result *SendSmsResponse, _err error) { + runtime := &util.RuntimeOptions{} + _result = &SendSmsResponse{} + _body, _err := client.SendSmsWithOptions(request, runtime) + if _err != nil { + return _result, _err + } + _result = _body + return _result, _err +} + +func (client *Client) TagResourcesWithOptions(request *TagResourcesRequest, runtime *util.RuntimeOptions) (_result *TagResourcesResponse, _err error) { + _err = util.ValidateModel(request) + if _err != nil { + return _result, _err + } + query := map[string]interface{}{} + if !tea.BoolValue(util.IsUnset(request.OwnerId)) { + query["OwnerId"] = request.OwnerId + } + + if !tea.BoolValue(util.IsUnset(request.ProdCode)) { + query["ProdCode"] = request.ProdCode + } + + if !tea.BoolValue(util.IsUnset(request.RegionId)) { + query["RegionId"] = request.RegionId + } + + if !tea.BoolValue(util.IsUnset(request.ResourceId)) { + query["ResourceId"] = request.ResourceId + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerAccount)) { + query["ResourceOwnerAccount"] = request.ResourceOwnerAccount + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerId)) { + query["ResourceOwnerId"] = request.ResourceOwnerId + } + + if !tea.BoolValue(util.IsUnset(request.ResourceType)) { + query["ResourceType"] = request.ResourceType + } + + if !tea.BoolValue(util.IsUnset(request.Tag)) { + query["Tag"] = request.Tag + } + + req := &openapi.OpenApiRequest{ + Query: openapiutil.Query(query), + } + params := &openapi.Params{ + Action: tea.String("TagResources"), + Version: tea.String("2017-05-25"), + Protocol: tea.String("HTTPS"), + Pathname: tea.String("/"), + Method: tea.String("POST"), + AuthType: tea.String("AK"), + Style: tea.String("RPC"), + ReqBodyType: tea.String("formData"), + BodyType: tea.String("json"), + } + _result = &TagResourcesResponse{} + _body, _err := client.CallApi(params, req, runtime) + if _err != nil { + return _result, _err + } + _err = tea.Convert(_body, &_result) + return _result, _err +} + +func (client *Client) TagResources(request *TagResourcesRequest) (_result *TagResourcesResponse, _err error) { + runtime := &util.RuntimeOptions{} + _result = &TagResourcesResponse{} + _body, _err := client.TagResourcesWithOptions(request, runtime) + if _err != nil { + return _result, _err + } + _result = _body + return _result, _err +} + +func (client *Client) UntagResourcesWithOptions(request *UntagResourcesRequest, runtime *util.RuntimeOptions) (_result *UntagResourcesResponse, _err error) { + _err = util.ValidateModel(request) + if _err != nil { + return _result, _err + } + query := map[string]interface{}{} + if !tea.BoolValue(util.IsUnset(request.All)) { + query["All"] = request.All + } + + if !tea.BoolValue(util.IsUnset(request.OwnerId)) { + query["OwnerId"] = request.OwnerId + } + + if !tea.BoolValue(util.IsUnset(request.ProdCode)) { + query["ProdCode"] = request.ProdCode + } + + if !tea.BoolValue(util.IsUnset(request.RegionId)) { + query["RegionId"] = request.RegionId + } + + if !tea.BoolValue(util.IsUnset(request.ResourceId)) { + query["ResourceId"] = request.ResourceId + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerAccount)) { + query["ResourceOwnerAccount"] = request.ResourceOwnerAccount + } + + if !tea.BoolValue(util.IsUnset(request.ResourceOwnerId)) { + query["ResourceOwnerId"] = request.ResourceOwnerId + } + + if !tea.BoolValue(util.IsUnset(request.ResourceType)) { + query["ResourceType"] = request.ResourceType + } + + if !tea.BoolValue(util.IsUnset(request.TagKey)) { + query["TagKey"] = request.TagKey + } + + req := &openapi.OpenApiRequest{ + Query: openapiutil.Query(query), + } + params := &openapi.Params{ + Action: tea.String("UntagResources"), + Version: tea.String("2017-05-25"), + Protocol: tea.String("HTTPS"), + Pathname: tea.String("/"), + Method: tea.String("POST"), + AuthType: tea.String("AK"), + Style: tea.String("RPC"), + ReqBodyType: tea.String("formData"), + BodyType: tea.String("json"), + } + _result = &UntagResourcesResponse{} + _body, _err := client.CallApi(params, req, runtime) + if _err != nil { + return _result, _err + } + _err = tea.Convert(_body, &_result) + return _result, _err +} + +func (client *Client) UntagResources(request *UntagResourcesRequest) (_result *UntagResourcesResponse, _err error) { + runtime := &util.RuntimeOptions{} + _result = &UntagResourcesResponse{} + _body, _err := client.UntagResourcesWithOptions(request, runtime) + if _err != nil { + return _result, _err + } + _result = _body + return _result, _err +} diff --git a/vendor/github.com/alibabacloud-go/endpoint-util/service/service.go b/vendor/github.com/alibabacloud-go/endpoint-util/service/service.go new file mode 100644 index 000000000..85e5fda6e --- /dev/null +++ b/vendor/github.com/alibabacloud-go/endpoint-util/service/service.go @@ -0,0 +1,41 @@ +// This file is auto-generated, don't edit it. Thanks. +/** + * Get endpoint + * @return string + */ +package service + +import ( + "fmt" + "strings" + + "github.com/alibabacloud-go/tea/tea" +) + +func GetEndpointRules(product, regionId, endpointType, network, suffix *string) (_result *string, _err error) { + if tea.StringValue(endpointType) == "regional" { + if tea.StringValue(regionId) == "" { + _err = fmt.Errorf("RegionId is empty, please set a valid RegionId") + return tea.String(""), _err + } + _result = tea.String(strings.Replace("..aliyuncs.com", + "", tea.StringValue(regionId), 1)) + } else { + _result = tea.String(".aliyuncs.com") + } + _result = tea.String(strings.Replace(tea.StringValue(_result), + "", strings.ToLower(tea.StringValue(product)), 1)) + if tea.StringValue(network) == "" || tea.StringValue(network) == "public" { + _result = tea.String(strings.Replace(tea.StringValue(_result), "", "", 1)) + } else { + _result = tea.String(strings.Replace(tea.StringValue(_result), + "", "-"+tea.StringValue(network), 1)) + } + if tea.StringValue(suffix) == "" { + _result = tea.String(strings.Replace(tea.StringValue(_result), "", "", 1)) + } else { + _result = tea.String(strings.Replace(tea.StringValue(_result), + "", "-"+tea.StringValue(suffix), 1)) + } + return _result, nil +} diff --git a/vendor/github.com/alibabacloud-go/openapi-util/LICENSE b/vendor/github.com/alibabacloud-go/openapi-util/LICENSE new file mode 100644 index 000000000..0c44dcefe --- /dev/null +++ b/vendor/github.com/alibabacloud-go/openapi-util/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) 2009-present, Alibaba Cloud All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/alibabacloud-go/openapi-util/service/service.go b/vendor/github.com/alibabacloud-go/openapi-util/service/service.go new file mode 100644 index 000000000..245eeccb0 --- /dev/null +++ b/vendor/github.com/alibabacloud-go/openapi-util/service/service.go @@ -0,0 +1,635 @@ +// This file is auto-generated, don't edit it. Thanks. +/** + * This is for OpenApi Util + */ +package service + +import ( + "bytes" + "crypto" + "crypto/hmac" + "crypto/rand" + "crypto/rsa" + "crypto/sha1" + "crypto/sha256" + "crypto/x509" + "encoding/base64" + "encoding/hex" + "encoding/json" + "encoding/pem" + "errors" + "fmt" + "hash" + "io" + "net/http" + "net/textproto" + "net/url" + "reflect" + "sort" + "strconv" + "strings" + "time" + + util "github.com/alibabacloud-go/tea-utils/service" + "github.com/alibabacloud-go/tea/tea" + "github.com/tjfoc/gmsm/sm3" +) + +const ( + PEM_BEGIN = "-----BEGIN RSA PRIVATE KEY-----\n" + PEM_END = "\n-----END RSA PRIVATE KEY-----" +) + +type Sorter struct { + Keys []string + Vals []string +} + +func newSorter(m map[string]string) *Sorter { + hs := &Sorter{ + Keys: make([]string, 0, len(m)), + Vals: make([]string, 0, len(m)), + } + + for k, v := range m { + hs.Keys = append(hs.Keys, k) + hs.Vals = append(hs.Vals, v) + } + return hs +} + +// Sort is an additional function for function SignHeader. +func (hs *Sorter) Sort() { + sort.Sort(hs) +} + +// Len is an additional function for function SignHeader. +func (hs *Sorter) Len() int { + return len(hs.Vals) +} + +// Less is an additional function for function SignHeader. +func (hs *Sorter) Less(i, j int) bool { + return bytes.Compare([]byte(hs.Keys[i]), []byte(hs.Keys[j])) < 0 +} + +// Swap is an additional function for function SignHeader. +func (hs *Sorter) Swap(i, j int) { + hs.Vals[i], hs.Vals[j] = hs.Vals[j], hs.Vals[i] + hs.Keys[i], hs.Keys[j] = hs.Keys[j], hs.Keys[i] +} + +/** + * Convert all params of body other than type of readable into content + * @param body source Model + * @param content target Model + * @return void + */ +func Convert(body interface{}, content interface{}) { + res := make(map[string]interface{}) + val := reflect.ValueOf(body).Elem() + dataType := val.Type() + for i := 0; i < dataType.NumField(); i++ { + field := dataType.Field(i) + name, _ := field.Tag.Lookup("json") + name = strings.Split(name, ",omitempty")[0] + _, ok := val.Field(i).Interface().(io.Reader) + if !ok { + res[name] = val.Field(i).Interface() + } + } + byt, _ := json.Marshal(res) + json.Unmarshal(byt, content) +} + +/** + * Get the string to be signed according to request + * @param request which contains signed messages + * @return the signed string + */ +func GetStringToSign(request *tea.Request) (_result *string) { + return tea.String(getStringToSign(request)) +} + +func getStringToSign(request *tea.Request) string { + resource := tea.StringValue(request.Pathname) + queryParams := request.Query + // sort QueryParams by key + var queryKeys []string + for key := range queryParams { + queryKeys = append(queryKeys, key) + } + sort.Strings(queryKeys) + tmp := "" + for i := 0; i < len(queryKeys); i++ { + queryKey := queryKeys[i] + v := tea.StringValue(queryParams[queryKey]) + if v != "" { + tmp = tmp + "&" + queryKey + "=" + v + } else { + tmp = tmp + "&" + queryKey + } + } + if tmp != "" { + tmp = strings.TrimLeft(tmp, "&") + resource = resource + "?" + tmp + } + return getSignedStr(request, resource) +} + +func getSignedStr(req *tea.Request, canonicalizedResource string) string { + temp := make(map[string]string) + + for k, v := range req.Headers { + if strings.HasPrefix(strings.ToLower(k), "x-acs-") { + temp[strings.ToLower(k)] = tea.StringValue(v) + } + } + hs := newSorter(temp) + + // Sort the temp by the ascending order + hs.Sort() + + // Get the canonicalizedOSSHeaders + canonicalizedOSSHeaders := "" + for i := range hs.Keys { + canonicalizedOSSHeaders += hs.Keys[i] + ":" + hs.Vals[i] + "\n" + } + + // Give other parameters values + // when sign URL, date is expires + date := tea.StringValue(req.Headers["date"]) + accept := tea.StringValue(req.Headers["accept"]) + contentType := tea.StringValue(req.Headers["content-type"]) + contentMd5 := tea.StringValue(req.Headers["content-md5"]) + + signStr := tea.StringValue(req.Method) + "\n" + accept + "\n" + contentMd5 + "\n" + contentType + "\n" + date + "\n" + canonicalizedOSSHeaders + canonicalizedResource + return signStr +} + +/** + * Get signature according to stringToSign, secret + * @param stringToSign the signed string + * @param secret accesskey secret + * @return the signature + */ +func GetROASignature(stringToSign *string, secret *string) (_result *string) { + h := hmac.New(func() hash.Hash { return sha1.New() }, []byte(tea.StringValue(secret))) + io.WriteString(h, tea.StringValue(stringToSign)) + signedStr := base64.StdEncoding.EncodeToString(h.Sum(nil)) + return tea.String(signedStr) +} + +func GetEndpoint(endpoint *string, server *bool, endpointType *string) *string { + if tea.StringValue(endpointType) == "internal" { + strs := strings.Split(tea.StringValue(endpoint), ".") + strs[0] += "-internal" + endpoint = tea.String(strings.Join(strs, ".")) + } + if tea.BoolValue(server) && tea.StringValue(endpointType) == "accelerate" { + return tea.String("oss-accelerate.aliyuncs.com") + } + + return endpoint +} + +func HexEncode(raw []byte) *string { + return tea.String(hex.EncodeToString(raw)) +} + +func Hash(raw []byte, signatureAlgorithm *string) []byte { + signType := tea.StringValue(signatureAlgorithm) + if signType == "ACS3-HMAC-SHA256" || signType == "ACS3-RSA-SHA256" { + h := sha256.New() + h.Write(raw) + return h.Sum(nil) + } else if signType == "ACS3-HMAC-SM3" { + h := sm3.New() + h.Write(raw) + return h.Sum(nil) + } + return nil +} + +func GetEncodePath(path *string) *string { + uri := tea.StringValue(path) + strs := strings.Split(uri, "/") + for i, v := range strs { + strs[i] = url.QueryEscape(v) + } + uri = strings.Join(strs, "/") + uri = strings.Replace(uri, "+", "%20", -1) + uri = strings.Replace(uri, "*", "%2A", -1) + uri = strings.Replace(uri, "%7E", "~", -1) + return tea.String(uri) +} + +func GetEncodeParam(param *string) *string { + uri := tea.StringValue(param) + uri = url.QueryEscape(uri) + uri = strings.Replace(uri, "+", "%20", -1) + uri = strings.Replace(uri, "*", "%2A", -1) + uri = strings.Replace(uri, "%7E", "~", -1) + return tea.String(uri) +} + +func GetAuthorization(request *tea.Request, signatureAlgorithm, payload, acesskey, secret *string) *string { + canonicalURI := tea.StringValue(request.Pathname) + if canonicalURI == "" { + canonicalURI = "/" + } + + canonicalURI = strings.Replace(canonicalURI, "+", "%20", -1) + canonicalURI = strings.Replace(canonicalURI, "*", "%2A", -1) + canonicalURI = strings.Replace(canonicalURI, "%7E", "~", -1) + + method := tea.StringValue(request.Method) + canonicalQueryString := getCanonicalQueryString(request.Query) + canonicalheaders, signedHeaders := getCanonicalHeaders(request.Headers) + + canonicalRequest := method + "\n" + canonicalURI + "\n" + canonicalQueryString + "\n" + canonicalheaders + "\n" + + strings.Join(signedHeaders, ";") + "\n" + tea.StringValue(payload) + signType := tea.StringValue(signatureAlgorithm) + StringToSign := signType + "\n" + tea.StringValue(HexEncode(Hash([]byte(canonicalRequest), signatureAlgorithm))) + signature := tea.StringValue(HexEncode(SignatureMethod(tea.StringValue(secret), StringToSign, signType))) + auth := signType + " Credential=" + tea.StringValue(acesskey) + ",SignedHeaders=" + + strings.Join(signedHeaders, ";") + ",Signature=" + signature + return tea.String(auth) +} + +func SignatureMethod(secret, source, signatureAlgorithm string) []byte { + if signatureAlgorithm == "ACS3-HMAC-SHA256" { + h := hmac.New(sha256.New, []byte(secret)) + h.Write([]byte(source)) + return h.Sum(nil) + } else if signatureAlgorithm == "ACS3-HMAC-SM3" { + h := hmac.New(sm3.New, []byte(secret)) + h.Write([]byte(source)) + return h.Sum(nil) + } else if signatureAlgorithm == "ACS3-RSA-SHA256" { + return rsaSign(source, secret) + } + return nil +} + +func rsaSign(content, secret string) []byte { + h := crypto.SHA256.New() + h.Write([]byte(content)) + hashed := h.Sum(nil) + priv, err := parsePrivateKey(secret) + if err != nil { + return nil + } + sign, err := rsa.SignPKCS1v15(rand.Reader, priv, crypto.SHA256, hashed) + if err != nil { + return nil + } + return sign +} + +func parsePrivateKey(privateKey string) (*rsa.PrivateKey, error) { + privateKey = formatPrivateKey(privateKey) + block, _ := pem.Decode([]byte(privateKey)) + if block == nil { + return nil, errors.New("PrivateKey is invalid") + } + priKey, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + return nil, err + } + switch priKey.(type) { + case *rsa.PrivateKey: + return priKey.(*rsa.PrivateKey), nil + default: + return nil, nil + } +} + +func formatPrivateKey(privateKey string) string { + if !strings.HasPrefix(privateKey, PEM_BEGIN) { + privateKey = PEM_BEGIN + privateKey + } + + if !strings.HasSuffix(privateKey, PEM_END) { + privateKey += PEM_END + } + return privateKey +} + +func getCanonicalHeaders(headers map[string]*string) (string, []string) { + tmp := make(map[string]string) + tmpHeader := http.Header{} + for k, v := range headers { + if strings.HasPrefix(strings.ToLower(k), "x-acs-") || strings.ToLower(k) == "host" || + strings.ToLower(k) == "content-type" { + tmp[strings.ToLower(k)] = strings.TrimSpace(tea.StringValue(v)) + tmpHeader.Add(strings.ToLower(k), strings.TrimSpace(tea.StringValue(v))) + } + } + hs := newSorter(tmp) + + // Sort the temp by the ascending order + hs.Sort() + canonicalheaders := "" + for _, key := range hs.Keys { + vals := tmpHeader[textproto.CanonicalMIMEHeaderKey(key)] + sort.Strings(vals) + canonicalheaders += key + ":" + strings.Join(vals, ",") + "\n" + } + + return canonicalheaders, hs.Keys +} + +func getCanonicalQueryString(query map[string]*string) string { + canonicalQueryString := "" + if tea.BoolValue(util.IsUnset(query)) { + return canonicalQueryString + } + tmp := make(map[string]string) + for k, v := range query { + tmp[k] = tea.StringValue(v) + } + + hs := newSorter(tmp) + + // Sort the temp by the ascending order + hs.Sort() + for i := range hs.Keys { + if hs.Vals[i] != "" { + canonicalQueryString += "&" + hs.Keys[i] + "=" + url.QueryEscape(hs.Vals[i]) + } else { + canonicalQueryString += "&" + hs.Keys[i] + "=" + } + } + canonicalQueryString = strings.Replace(canonicalQueryString, "+", "%20", -1) + canonicalQueryString = strings.Replace(canonicalQueryString, "*", "%2A", -1) + canonicalQueryString = strings.Replace(canonicalQueryString, "%7E", "~", -1) + + if canonicalQueryString != "" { + canonicalQueryString = strings.TrimLeft(canonicalQueryString, "&") + } + return canonicalQueryString +} + +/** + * Parse filter into a form string + * @param filter object + * @return the string + */ +func ToForm(filter map[string]interface{}) (_result *string) { + tmp := make(map[string]interface{}) + byt, _ := json.Marshal(filter) + d := json.NewDecoder(bytes.NewReader(byt)) + d.UseNumber() + _ = d.Decode(&tmp) + + result := make(map[string]*string) + for key, value := range tmp { + filterValue := reflect.ValueOf(value) + flatRepeatedList(filterValue, result, key) + } + + m := util.AnyifyMapValue(result) + return util.ToFormString(m) +} + +func flatRepeatedList(dataValue reflect.Value, result map[string]*string, prefix string) { + if !dataValue.IsValid() { + return + } + + dataType := dataValue.Type() + if dataType.Kind().String() == "slice" { + handleRepeatedParams(dataValue, result, prefix) + } else if dataType.Kind().String() == "map" { + handleMap(dataValue, result, prefix) + } else { + result[prefix] = tea.String(fmt.Sprintf("%v", dataValue.Interface())) + } +} + +func handleRepeatedParams(repeatedFieldValue reflect.Value, result map[string]*string, prefix string) { + if repeatedFieldValue.IsValid() && !repeatedFieldValue.IsNil() { + for m := 0; m < repeatedFieldValue.Len(); m++ { + elementValue := repeatedFieldValue.Index(m) + key := prefix + "." + strconv.Itoa(m+1) + fieldValue := reflect.ValueOf(elementValue.Interface()) + if fieldValue.Kind().String() == "map" { + handleMap(fieldValue, result, key) + } else { + result[key] = tea.String(fmt.Sprintf("%v", fieldValue.Interface())) + } + } + } +} + +func handleMap(valueField reflect.Value, result map[string]*string, prefix string) { + if valueField.IsValid() && valueField.String() != "" { + valueFieldType := valueField.Type() + if valueFieldType.Kind().String() == "map" { + var byt []byte + byt, _ = json.Marshal(valueField.Interface()) + cache := make(map[string]interface{}) + d := json.NewDecoder(bytes.NewReader(byt)) + d.UseNumber() + _ = d.Decode(&cache) + for key, value := range cache { + pre := "" + if prefix != "" { + pre = prefix + "." + key + } else { + pre = key + } + fieldValue := reflect.ValueOf(value) + flatRepeatedList(fieldValue, result, pre) + } + } + } +} + +/** + * Get timestamp + * @return the timestamp string + */ +func GetTimestamp() (_result *string) { + gmt := time.FixedZone("GMT", 0) + return tea.String(time.Now().In(gmt).Format("2006-01-02T15:04:05Z")) +} + +/** + * Parse filter into a object which's type is map[string]string + * @param filter query param + * @return the object + */ +func Query(filter interface{}) (_result map[string]*string) { + tmp := make(map[string]interface{}) + byt, _ := json.Marshal(filter) + d := json.NewDecoder(bytes.NewReader(byt)) + d.UseNumber() + _ = d.Decode(&tmp) + + result := make(map[string]*string) + for key, value := range tmp { + filterValue := reflect.ValueOf(value) + flatRepeatedList(filterValue, result, key) + } + + return result +} + +/** + * Get signature according to signedParams, method and secret + * @param signedParams params which need to be signed + * @param method http method e.g. GET + * @param secret AccessKeySecret + * @return the signature + */ +func GetRPCSignature(signedParams map[string]*string, method *string, secret *string) (_result *string) { + stringToSign := buildRpcStringToSign(signedParams, tea.StringValue(method)) + signature := sign(stringToSign, tea.StringValue(secret), "&") + return tea.String(signature) +} + +/** + * Parse array into a string with specified style + * @param array the array + * @param prefix the prefix string + * @style specified style e.g. repeatList + * @return the string + */ +func ArrayToStringWithSpecifiedStyle(array interface{}, prefix *string, style *string) (_result *string) { + if tea.BoolValue(util.IsUnset(array)) { + return tea.String("") + } + + sty := tea.StringValue(style) + if sty == "repeatList" { + tmp := map[string]interface{}{ + tea.StringValue(prefix): array, + } + return flatRepeatList(tmp) + } else if sty == "simple" || sty == "spaceDelimited" || sty == "pipeDelimited" { + return flatArray(array, sty) + } else if sty == "json" { + return util.ToJSONString(array) + } + return tea.String("") +} + +func ParseToMap(in interface{}) map[string]interface{} { + if tea.BoolValue(util.IsUnset(in)) { + return nil + } + + tmp := make(map[string]interface{}) + byt, _ := json.Marshal(in) + d := json.NewDecoder(bytes.NewReader(byt)) + d.UseNumber() + err := d.Decode(&tmp) + if err != nil { + return nil + } + return tmp +} + +func flatRepeatList(filter map[string]interface{}) (_result *string) { + tmp := make(map[string]interface{}) + byt, _ := json.Marshal(filter) + d := json.NewDecoder(bytes.NewReader(byt)) + d.UseNumber() + _ = d.Decode(&tmp) + + result := make(map[string]*string) + for key, value := range tmp { + filterValue := reflect.ValueOf(value) + flatRepeatedList(filterValue, result, key) + } + + res := make(map[string]string) + for k, v := range result { + res[k] = tea.StringValue(v) + } + hs := newSorter(res) + + hs.Sort() + + // Get the canonicalizedOSSHeaders + t := "" + for i := range hs.Keys { + if i == len(hs.Keys)-1 { + t += hs.Keys[i] + "=" + hs.Vals[i] + } else { + t += hs.Keys[i] + "=" + hs.Vals[i] + "&&" + } + } + return tea.String(t) +} + +func flatArray(array interface{}, sty string) *string { + t := reflect.ValueOf(array) + strs := make([]string, 0) + for i := 0; i < t.Len(); i++ { + tmp := t.Index(i) + if tmp.Kind() == reflect.Ptr || tmp.Kind() == reflect.Interface { + tmp = tmp.Elem() + } + + if tmp.Kind() == reflect.Ptr { + tmp = tmp.Elem() + } + if tmp.Kind() == reflect.String { + strs = append(strs, tmp.String()) + } else { + inter := tmp.Interface() + byt, _ := json.Marshal(inter) + strs = append(strs, string(byt)) + } + } + str := "" + if sty == "simple" { + str = strings.Join(strs, ",") + } else if sty == "spaceDelimited" { + str = strings.Join(strs, " ") + } else if sty == "pipeDelimited" { + str = strings.Join(strs, "|") + } + return tea.String(str) +} + +func buildRpcStringToSign(signedParam map[string]*string, method string) (stringToSign string) { + signParams := make(map[string]string) + for key, value := range signedParam { + signParams[key] = tea.StringValue(value) + } + + stringToSign = getUrlFormedMap(signParams) + stringToSign = strings.Replace(stringToSign, "+", "%20", -1) + stringToSign = strings.Replace(stringToSign, "*", "%2A", -1) + stringToSign = strings.Replace(stringToSign, "%7E", "~", -1) + stringToSign = url.QueryEscape(stringToSign) + stringToSign = method + "&%2F&" + stringToSign + return +} + +func getUrlFormedMap(source map[string]string) (urlEncoded string) { + urlEncoder := url.Values{} + for key, value := range source { + urlEncoder.Add(key, value) + } + urlEncoded = urlEncoder.Encode() + return +} + +func sign(stringToSign, accessKeySecret, secretSuffix string) string { + secret := accessKeySecret + secretSuffix + signedBytes := shaHmac1(stringToSign, secret) + signedString := base64.StdEncoding.EncodeToString(signedBytes) + return signedString +} + +func shaHmac1(source, secret string) []byte { + key := []byte(secret) + hmac := hmac.New(sha1.New, key) + hmac.Write([]byte(source)) + return hmac.Sum(nil) +} diff --git a/vendor/github.com/alibabacloud-go/tea-utils/service/service.go b/vendor/github.com/alibabacloud-go/tea-utils/service/service.go new file mode 100644 index 000000000..2b6935723 --- /dev/null +++ b/vendor/github.com/alibabacloud-go/tea-utils/service/service.go @@ -0,0 +1,462 @@ +package service + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "reflect" + "runtime" + "strconv" + "strings" + "time" + + "github.com/alibabacloud-go/tea/tea" +) + +var defaultUserAgent = fmt.Sprintf("AlibabaCloud (%s; %s) Golang/%s Core/%s TeaDSL/1", runtime.GOOS, runtime.GOARCH, strings.Trim(runtime.Version(), "go"), "0.01") + +type RuntimeOptions struct { + Autoretry *bool `json:"autoretry" xml:"autoretry"` + IgnoreSSL *bool `json:"ignoreSSL" xml:"ignoreSSL"` + MaxAttempts *int `json:"maxAttempts" xml:"maxAttempts"` + BackoffPolicy *string `json:"backoffPolicy" xml:"backoffPolicy"` + BackoffPeriod *int `json:"backoffPeriod" xml:"backoffPeriod"` + ReadTimeout *int `json:"readTimeout" xml:"readTimeout"` + ConnectTimeout *int `json:"connectTimeout" xml:"connectTimeout"` + LocalAddr *string `json:"localAddr" xml:"localAddr"` + HttpProxy *string `json:"httpProxy" xml:"httpProxy"` + HttpsProxy *string `json:"httpsProxy" xml:"httpsProxy"` + NoProxy *string `json:"noProxy" xml:"noProxy"` + MaxIdleConns *int `json:"maxIdleConns" xml:"maxIdleConns"` + Socks5Proxy *string `json:"socks5Proxy" xml:"socks5Proxy"` + Socks5NetWork *string `json:"socks5NetWork" xml:"socks5NetWork"` +} + +func (s RuntimeOptions) String() string { + return tea.Prettify(s) +} + +func (s RuntimeOptions) GoString() string { + return s.String() +} + +func (s *RuntimeOptions) SetAutoretry(v bool) *RuntimeOptions { + s.Autoretry = &v + return s +} + +func (s *RuntimeOptions) SetIgnoreSSL(v bool) *RuntimeOptions { + s.IgnoreSSL = &v + return s +} + +func (s *RuntimeOptions) SetMaxAttempts(v int) *RuntimeOptions { + s.MaxAttempts = &v + return s +} + +func (s *RuntimeOptions) SetBackoffPolicy(v string) *RuntimeOptions { + s.BackoffPolicy = &v + return s +} + +func (s *RuntimeOptions) SetBackoffPeriod(v int) *RuntimeOptions { + s.BackoffPeriod = &v + return s +} + +func (s *RuntimeOptions) SetReadTimeout(v int) *RuntimeOptions { + s.ReadTimeout = &v + return s +} + +func (s *RuntimeOptions) SetConnectTimeout(v int) *RuntimeOptions { + s.ConnectTimeout = &v + return s +} + +func (s *RuntimeOptions) SetHttpProxy(v string) *RuntimeOptions { + s.HttpProxy = &v + return s +} + +func (s *RuntimeOptions) SetHttpsProxy(v string) *RuntimeOptions { + s.HttpsProxy = &v + return s +} + +func (s *RuntimeOptions) SetNoProxy(v string) *RuntimeOptions { + s.NoProxy = &v + return s +} + +func (s *RuntimeOptions) SetMaxIdleConns(v int) *RuntimeOptions { + s.MaxIdleConns = &v + return s +} + +func (s *RuntimeOptions) SetLocalAddr(v string) *RuntimeOptions { + s.LocalAddr = &v + return s +} + +func (s *RuntimeOptions) SetSocks5Proxy(v string) *RuntimeOptions { + s.Socks5Proxy = &v + return s +} + +func (s *RuntimeOptions) SetSocks5NetWork(v string) *RuntimeOptions { + s.Socks5NetWork = &v + return s +} + +func ReadAsString(body io.Reader) (*string, error) { + byt, err := ioutil.ReadAll(body) + if err != nil { + return tea.String(""), err + } + r, ok := body.(io.ReadCloser) + if ok { + r.Close() + } + return tea.String(string(byt)), nil +} + +func StringifyMapValue(a map[string]interface{}) map[string]*string { + res := make(map[string]*string) + for key, value := range a { + if value != nil { + switch value.(type) { + case string: + res[key] = tea.String(value.(string)) + default: + byt, _ := json.Marshal(value) + res[key] = tea.String(string(byt)) + } + } + } + return res +} + +func AnyifyMapValue(a map[string]*string) map[string]interface{} { + res := make(map[string]interface{}) + for key, value := range a { + res[key] = tea.StringValue(value) + } + return res +} + +func ReadAsBytes(body io.Reader) ([]byte, error) { + byt, err := ioutil.ReadAll(body) + if err != nil { + return nil, err + } + r, ok := body.(io.ReadCloser) + if ok { + r.Close() + } + return byt, nil +} + +func DefaultString(reaStr, defaultStr *string) *string { + if reaStr == nil { + return defaultStr + } + return reaStr +} + +func ToJSONString(a interface{}) *string { + switch v := a.(type) { + case *string: + return v + case string: + return tea.String(v) + case []byte: + return tea.String(string(v)) + case io.Reader: + byt, err := ioutil.ReadAll(v) + if err != nil { + return nil + } + return tea.String(string(byt)) + } + byt, err := json.Marshal(a) + if err != nil { + return nil + } + return tea.String(string(byt)) +} + +func DefaultNumber(reaNum, defaultNum *int) *int { + if reaNum == nil { + return defaultNum + } + return reaNum +} + +func ReadAsJSON(body io.Reader) (result interface{}, err error) { + byt, err := ioutil.ReadAll(body) + if err != nil { + return + } + if string(byt) == "" { + return + } + r, ok := body.(io.ReadCloser) + if ok { + r.Close() + } + d := json.NewDecoder(bytes.NewReader(byt)) + d.UseNumber() + err = d.Decode(&result) + return +} + +func GetNonce() *string { + return tea.String(getUUID()) +} + +func Empty(val *string) *bool { + return tea.Bool(val == nil || tea.StringValue(val) == "") +} + +func ValidateModel(a interface{}) error { + if a == nil { + return nil + } + err := tea.Validate(a) + return err +} + +func EqualString(val1, val2 *string) *bool { + return tea.Bool(tea.StringValue(val1) == tea.StringValue(val2)) +} + +func EqualNumber(val1, val2 *int) *bool { + return tea.Bool(tea.IntValue(val1) == tea.IntValue(val2)) +} + +func IsUnset(val interface{}) *bool { + if val == nil { + return tea.Bool(true) + } + + v := reflect.ValueOf(val) + if v.Kind() == reflect.Ptr || v.Kind() == reflect.Slice || v.Kind() == reflect.Map { + return tea.Bool(v.IsNil()) + } + + valType := reflect.TypeOf(val) + valZero := reflect.Zero(valType) + return tea.Bool(valZero == v) +} + +func ToBytes(a *string) []byte { + return []byte(tea.StringValue(a)) +} + +func AssertAsMap(a interface{}) map[string]interface{} { + r := reflect.ValueOf(a) + if r.Kind().String() != "map" { + panic(fmt.Sprintf("%v is not a map[string]interface{}", a)) + } + + res := make(map[string]interface{}) + tmp := r.MapKeys() + for _, key := range tmp { + res[key.String()] = r.MapIndex(key).Interface() + } + + return res +} + +func AssertAsNumber(a interface{}) *int { + res := 0 + switch a.(type) { + case int: + tmp := a.(int) + res = tmp + case *int: + tmp := a.(*int) + res = tea.IntValue(tmp) + default: + panic(fmt.Sprintf("%v is not a int", a)) + } + + return tea.Int(res) +} + +func AssertAsBoolean(a interface{}) *bool { + res := false + switch a.(type) { + case bool: + tmp := a.(bool) + res = tmp + case *bool: + tmp := a.(*bool) + res = tea.BoolValue(tmp) + default: + panic(fmt.Sprintf("%v is not a bool", a)) + } + + return tea.Bool(res) +} + +func AssertAsString(a interface{}) *string { + res := "" + switch a.(type) { + case string: + tmp := a.(string) + res = tmp + case *string: + tmp := a.(*string) + res = tea.StringValue(tmp) + default: + panic(fmt.Sprintf("%v is not a string", a)) + } + + return tea.String(res) +} + +func AssertAsBytes(a interface{}) []byte { + res, ok := a.([]byte) + if !ok { + panic(fmt.Sprintf("%v is not []byte", a)) + } + return res +} + +func AssertAsReadable(a interface{}) io.Reader { + res, ok := a.(io.Reader) + if !ok { + panic(fmt.Sprintf("%v is not reader", a)) + } + return res +} + +func AssertAsArray(a interface{}) []interface{} { + r := reflect.ValueOf(a) + if r.Kind().String() != "array" && r.Kind().String() != "slice" { + panic(fmt.Sprintf("%v is not a [x]interface{}", a)) + } + aLen := r.Len() + res := make([]interface{}, 0) + for i := 0; i < aLen; i++ { + res = append(res, r.Index(i).Interface()) + } + return res +} + +func ParseJSON(a *string) interface{} { + mapTmp := make(map[string]interface{}) + d := json.NewDecoder(bytes.NewReader([]byte(tea.StringValue(a)))) + d.UseNumber() + err := d.Decode(&mapTmp) + if err == nil { + return mapTmp + } + + sliceTmp := make([]interface{}, 0) + d = json.NewDecoder(bytes.NewReader([]byte(tea.StringValue(a)))) + d.UseNumber() + err = d.Decode(&sliceTmp) + if err == nil { + return sliceTmp + } + + if num, err := strconv.Atoi(tea.StringValue(a)); err == nil { + return num + } + + if ok, err := strconv.ParseBool(tea.StringValue(a)); err == nil { + return ok + } + + if floa64tVal, err := strconv.ParseFloat(tea.StringValue(a), 64); err == nil { + return floa64tVal + } + return nil +} + +func ToString(a []byte) *string { + return tea.String(string(a)) +} + +func ToMap(in interface{}) map[string]interface{} { + if in == nil { + return nil + } + res := tea.ToMap(in) + return res +} + +func ToFormString(a map[string]interface{}) *string { + if a == nil { + return tea.String("") + } + res := "" + urlEncoder := url.Values{} + for key, value := range a { + v := fmt.Sprintf("%v", value) + urlEncoder.Add(key, v) + } + res = urlEncoder.Encode() + return tea.String(res) +} + +func GetDateUTCString() *string { + return tea.String(time.Now().UTC().Format(http.TimeFormat)) +} + +func GetUserAgent(userAgent *string) *string { + if userAgent != nil && tea.StringValue(userAgent) != "" { + return tea.String(defaultUserAgent + " " + tea.StringValue(userAgent)) + } + return tea.String(defaultUserAgent) +} + +func Is2xx(code *int) *bool { + tmp := tea.IntValue(code) + return tea.Bool(tmp >= 200 && tmp < 300) +} + +func Is3xx(code *int) *bool { + tmp := tea.IntValue(code) + return tea.Bool(tmp >= 300 && tmp < 400) +} + +func Is4xx(code *int) *bool { + tmp := tea.IntValue(code) + return tea.Bool(tmp >= 400 && tmp < 500) +} + +func Is5xx(code *int) *bool { + tmp := tea.IntValue(code) + return tea.Bool(tmp >= 500 && tmp < 600) +} + +func Sleep(millisecond *int) error { + ms := tea.IntValue(millisecond) + time.Sleep(time.Duration(ms) * time.Millisecond) + return nil +} + +func ToArray(in interface{}) []map[string]interface{} { + if tea.BoolValue(IsUnset(in)) { + return nil + } + + tmp := make([]map[string]interface{}, 0) + byt, _ := json.Marshal(in) + d := json.NewDecoder(bytes.NewReader(byt)) + d.UseNumber() + err := d.Decode(&tmp) + if err != nil { + return nil + } + return tmp +} diff --git a/vendor/github.com/alibabacloud-go/tea-utils/service/util.go b/vendor/github.com/alibabacloud-go/tea-utils/service/util.go new file mode 100644 index 000000000..a73cb5600 --- /dev/null +++ b/vendor/github.com/alibabacloud-go/tea-utils/service/util.go @@ -0,0 +1,52 @@ +package service + +import ( + "crypto/md5" + "crypto/rand" + "encoding/hex" + "hash" + rand2 "math/rand" +) + +type UUID [16]byte + +const numBytes = "1234567890" + +func getUUID() (uuidHex string) { + uuid := newUUID() + uuidHex = hex.EncodeToString(uuid[:]) + return +} + +func randStringBytes(n int) string { + b := make([]byte, n) + for i := range b { + b[i] = numBytes[rand2.Intn(len(numBytes))] + } + return string(b) +} + +func newUUID() UUID { + ns := UUID{} + safeRandom(ns[:]) + u := newFromHash(md5.New(), ns, randStringBytes(16)) + u[6] = (u[6] & 0x0f) | (byte(2) << 4) + u[8] = (u[8]&(0xff>>2) | (0x02 << 6)) + + return u +} + +func newFromHash(h hash.Hash, ns UUID, name string) UUID { + u := UUID{} + h.Write(ns[:]) + h.Write([]byte(name)) + copy(u[:], h.Sum(nil)) + + return u +} + +func safeRandom(dest []byte) { + if _, err := rand.Read(dest); err != nil { + panic(err) + } +} diff --git a/vendor/github.com/alibabacloud-go/tea-xml/service/service.go b/vendor/github.com/alibabacloud-go/tea-xml/service/service.go new file mode 100644 index 000000000..33139c74b --- /dev/null +++ b/vendor/github.com/alibabacloud-go/tea-xml/service/service.go @@ -0,0 +1,105 @@ +package service + +import ( + "bytes" + "encoding/xml" + "fmt" + "reflect" + "strings" + + "github.com/alibabacloud-go/tea/tea" + v2 "github.com/clbanning/mxj/v2" +) + +func ToXML(obj map[string]interface{}) *string { + return tea.String(mapToXML(obj)) +} + +func ParseXml(val *string, result interface{}) map[string]interface{} { + resp := make(map[string]interface{}) + + start := getStartElement([]byte(tea.StringValue(val))) + if result == nil { + vm, err := v2.NewMapXml([]byte(tea.StringValue(val))) + if err != nil { + return nil + } + return vm + } + out, err := xmlUnmarshal([]byte(tea.StringValue(val)), result) + if err != nil { + return resp + } + resp[start] = out + return resp +} + +func mapToXML(val map[string]interface{}) string { + res := "" + for key, value := range val { + switch value.(type) { + case []interface{}: + for _, v := range value.([]interface{}) { + switch v.(type) { + case map[string]interface{}: + res += `<` + key + `>` + res += mapToXML(v.(map[string]interface{})) + res += `` + default: + if fmt.Sprintf("%v", v) != `` { + res += `<` + key + `>` + res += fmt.Sprintf("%v", v) + res += `` + } + } + } + case map[string]interface{}: + res += `<` + key + `>` + res += mapToXML(value.(map[string]interface{})) + res += `` + default: + if fmt.Sprintf("%v", value) != `` { + res += `<` + key + `>` + res += fmt.Sprintf("%v", value) + res += `` + } + } + } + return res +} + +func getStartElement(body []byte) string { + d := xml.NewDecoder(bytes.NewReader(body)) + for { + tok, err := d.Token() + if err != nil { + return "" + } + if t, ok := tok.(xml.StartElement); ok { + return t.Name.Local + } + } +} + +func xmlUnmarshal(body []byte, result interface{}) (interface{}, error) { + start := getStartElement(body) + dataValue := reflect.ValueOf(result).Elem() + dataType := dataValue.Type() + for i := 0; i < dataType.NumField(); i++ { + field := dataType.Field(i) + name, containsNameTag := field.Tag.Lookup("xml") + name = strings.Replace(name, ",omitempty", "", -1) + if containsNameTag { + if name == start { + realType := dataValue.Field(i).Type() + realValue := reflect.New(realType).Interface() + err := xml.Unmarshal(body, realValue) + if err != nil { + return nil, err + } + return realValue, nil + } + } + } + return nil, nil +} diff --git a/vendor/github.com/alibabacloud-go/tea/LICENSE b/vendor/github.com/alibabacloud-go/tea/LICENSE new file mode 100644 index 000000000..0c44dcefe --- /dev/null +++ b/vendor/github.com/alibabacloud-go/tea/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) 2009-present, Alibaba Cloud All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/alibabacloud-go/tea/tea/json_parser.go b/vendor/github.com/alibabacloud-go/tea/tea/json_parser.go new file mode 100644 index 000000000..b3f202243 --- /dev/null +++ b/vendor/github.com/alibabacloud-go/tea/tea/json_parser.go @@ -0,0 +1,333 @@ +package tea + +import ( + "encoding/json" + "io" + "math" + "reflect" + "strconv" + "strings" + "unsafe" + + jsoniter "github.com/json-iterator/go" + "github.com/modern-go/reflect2" +) + +const maxUint = ^uint(0) +const maxInt = int(maxUint >> 1) +const minInt = -maxInt - 1 + +var jsonParser jsoniter.API + +func init() { + jsonParser = jsoniter.Config{ + EscapeHTML: true, + SortMapKeys: true, + ValidateJsonRawMessage: true, + CaseSensitive: true, + }.Froze() + + jsonParser.RegisterExtension(newBetterFuzzyExtension()) +} + +func newBetterFuzzyExtension() jsoniter.DecoderExtension { + return jsoniter.DecoderExtension{ + reflect2.DefaultTypeOfKind(reflect.String): &nullableFuzzyStringDecoder{}, + reflect2.DefaultTypeOfKind(reflect.Bool): &fuzzyBoolDecoder{}, + reflect2.DefaultTypeOfKind(reflect.Float32): &nullableFuzzyFloat32Decoder{}, + reflect2.DefaultTypeOfKind(reflect.Float64): &nullableFuzzyFloat64Decoder{}, + reflect2.DefaultTypeOfKind(reflect.Int): &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(maxInt) || val < float64(minInt) { + iter.ReportError("fuzzy decode int", "exceed range") + return + } + *((*int)(ptr)) = int(val) + } else { + *((*int)(ptr)) = iter.ReadInt() + } + }}, + reflect2.DefaultTypeOfKind(reflect.Uint): &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(maxUint) || val < 0 { + iter.ReportError("fuzzy decode uint", "exceed range") + return + } + *((*uint)(ptr)) = uint(val) + } else { + *((*uint)(ptr)) = iter.ReadUint() + } + }}, + reflect2.DefaultTypeOfKind(reflect.Int8): &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(math.MaxInt8) || val < float64(math.MinInt8) { + iter.ReportError("fuzzy decode int8", "exceed range") + return + } + *((*int8)(ptr)) = int8(val) + } else { + *((*int8)(ptr)) = iter.ReadInt8() + } + }}, + reflect2.DefaultTypeOfKind(reflect.Uint8): &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(math.MaxUint8) || val < 0 { + iter.ReportError("fuzzy decode uint8", "exceed range") + return + } + *((*uint8)(ptr)) = uint8(val) + } else { + *((*uint8)(ptr)) = iter.ReadUint8() + } + }}, + reflect2.DefaultTypeOfKind(reflect.Int16): &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(math.MaxInt16) || val < float64(math.MinInt16) { + iter.ReportError("fuzzy decode int16", "exceed range") + return + } + *((*int16)(ptr)) = int16(val) + } else { + *((*int16)(ptr)) = iter.ReadInt16() + } + }}, + reflect2.DefaultTypeOfKind(reflect.Uint16): &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(math.MaxUint16) || val < 0 { + iter.ReportError("fuzzy decode uint16", "exceed range") + return + } + *((*uint16)(ptr)) = uint16(val) + } else { + *((*uint16)(ptr)) = iter.ReadUint16() + } + }}, + reflect2.DefaultTypeOfKind(reflect.Int32): &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(math.MaxInt32) || val < float64(math.MinInt32) { + iter.ReportError("fuzzy decode int32", "exceed range") + return + } + *((*int32)(ptr)) = int32(val) + } else { + *((*int32)(ptr)) = iter.ReadInt32() + } + }}, + reflect2.DefaultTypeOfKind(reflect.Uint32): &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(math.MaxUint32) || val < 0 { + iter.ReportError("fuzzy decode uint32", "exceed range") + return + } + *((*uint32)(ptr)) = uint32(val) + } else { + *((*uint32)(ptr)) = iter.ReadUint32() + } + }}, + reflect2.DefaultTypeOfKind(reflect.Int64): &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(math.MaxInt64) || val < float64(math.MinInt64) { + iter.ReportError("fuzzy decode int64", "exceed range") + return + } + *((*int64)(ptr)) = int64(val) + } else { + *((*int64)(ptr)) = iter.ReadInt64() + } + }}, + reflect2.DefaultTypeOfKind(reflect.Uint64): &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(math.MaxUint64) || val < 0 { + iter.ReportError("fuzzy decode uint64", "exceed range") + return + } + *((*uint64)(ptr)) = uint64(val) + } else { + *((*uint64)(ptr)) = iter.ReadUint64() + } + }}, + } +} + +type nullableFuzzyStringDecoder struct { +} + +func (decoder *nullableFuzzyStringDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { + valueType := iter.WhatIsNext() + switch valueType { + case jsoniter.NumberValue: + var number json.Number + iter.ReadVal(&number) + *((*string)(ptr)) = string(number) + case jsoniter.StringValue: + *((*string)(ptr)) = iter.ReadString() + case jsoniter.BoolValue: + *((*string)(ptr)) = strconv.FormatBool(iter.ReadBool()) + case jsoniter.NilValue: + iter.ReadNil() + *((*string)(ptr)) = "" + default: + iter.ReportError("fuzzyStringDecoder", "not number or string or bool") + } +} + +type fuzzyBoolDecoder struct { +} + +func (decoder *fuzzyBoolDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { + valueType := iter.WhatIsNext() + switch valueType { + case jsoniter.BoolValue: + *((*bool)(ptr)) = iter.ReadBool() + case jsoniter.NumberValue: + var number json.Number + iter.ReadVal(&number) + num, err := number.Int64() + if err != nil { + iter.ReportError("fuzzyBoolDecoder", "get value from json.number failed") + } + if num == 0 { + *((*bool)(ptr)) = false + } else { + *((*bool)(ptr)) = true + } + case jsoniter.StringValue: + strValue := strings.ToLower(iter.ReadString()) + if strValue == "true" { + *((*bool)(ptr)) = true + } else if strValue == "false" || strValue == "" { + *((*bool)(ptr)) = false + } else { + iter.ReportError("fuzzyBoolDecoder", "unsupported bool value: "+strValue) + } + case jsoniter.NilValue: + iter.ReadNil() + *((*bool)(ptr)) = false + default: + iter.ReportError("fuzzyBoolDecoder", "not number or string or nil") + } +} + +type nullableFuzzyIntegerDecoder struct { + fun func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) +} + +func (decoder *nullableFuzzyIntegerDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { + valueType := iter.WhatIsNext() + var str string + switch valueType { + case jsoniter.NumberValue: + var number json.Number + iter.ReadVal(&number) + str = string(number) + case jsoniter.StringValue: + str = iter.ReadString() + // support empty string + if str == "" { + str = "0" + } + case jsoniter.BoolValue: + if iter.ReadBool() { + str = "1" + } else { + str = "0" + } + case jsoniter.NilValue: + iter.ReadNil() + str = "0" + default: + iter.ReportError("fuzzyIntegerDecoder", "not number or string") + } + newIter := iter.Pool().BorrowIterator([]byte(str)) + defer iter.Pool().ReturnIterator(newIter) + isFloat := strings.IndexByte(str, '.') != -1 + decoder.fun(isFloat, ptr, newIter) + if newIter.Error != nil && newIter.Error != io.EOF { + iter.Error = newIter.Error + } +} + +type nullableFuzzyFloat32Decoder struct { +} + +func (decoder *nullableFuzzyFloat32Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { + valueType := iter.WhatIsNext() + var str string + switch valueType { + case jsoniter.NumberValue: + *((*float32)(ptr)) = iter.ReadFloat32() + case jsoniter.StringValue: + str = iter.ReadString() + // support empty string + if str == "" { + *((*float32)(ptr)) = 0 + return + } + newIter := iter.Pool().BorrowIterator([]byte(str)) + defer iter.Pool().ReturnIterator(newIter) + *((*float32)(ptr)) = newIter.ReadFloat32() + if newIter.Error != nil && newIter.Error != io.EOF { + iter.Error = newIter.Error + } + case jsoniter.BoolValue: + // support bool to float32 + if iter.ReadBool() { + *((*float32)(ptr)) = 1 + } else { + *((*float32)(ptr)) = 0 + } + case jsoniter.NilValue: + iter.ReadNil() + *((*float32)(ptr)) = 0 + default: + iter.ReportError("nullableFuzzyFloat32Decoder", "not number or string") + } +} + +type nullableFuzzyFloat64Decoder struct { +} + +func (decoder *nullableFuzzyFloat64Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { + valueType := iter.WhatIsNext() + var str string + switch valueType { + case jsoniter.NumberValue: + *((*float64)(ptr)) = iter.ReadFloat64() + case jsoniter.StringValue: + str = iter.ReadString() + // support empty string + if str == "" { + *((*float64)(ptr)) = 0 + return + } + newIter := iter.Pool().BorrowIterator([]byte(str)) + defer iter.Pool().ReturnIterator(newIter) + *((*float64)(ptr)) = newIter.ReadFloat64() + if newIter.Error != nil && newIter.Error != io.EOF { + iter.Error = newIter.Error + } + case jsoniter.BoolValue: + // support bool to float64 + if iter.ReadBool() { + *((*float64)(ptr)) = 1 + } else { + *((*float64)(ptr)) = 0 + } + case jsoniter.NilValue: + // support empty string + iter.ReadNil() + *((*float64)(ptr)) = 0 + default: + iter.ReportError("nullableFuzzyFloat64Decoder", "not number or string") + } +} diff --git a/vendor/github.com/alibabacloud-go/tea/tea/tea.go b/vendor/github.com/alibabacloud-go/tea/tea/tea.go new file mode 100644 index 000000000..ac5a7e684 --- /dev/null +++ b/vendor/github.com/alibabacloud-go/tea/tea/tea.go @@ -0,0 +1,1121 @@ +package tea + +import ( + "bytes" + "context" + "crypto/tls" + "crypto/x509" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "io" + "math" + "math/rand" + "net" + "net/http" + "net/url" + "os" + "reflect" + "regexp" + "strconv" + "strings" + "sync" + "time" + + "github.com/alibabacloud-go/debug/debug" + "github.com/alibabacloud-go/tea/utils" + + "golang.org/x/net/proxy" +) + +var debugLog = debug.Init("tea") + +var hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { + return fn +} + +var basicTypes = []string{ + "int", "int16", "int64", "int32", "float32", "float64", "string", "bool", "uint64", "uint32", "uint16", +} + +// Verify whether the parameters meet the requirements +var validateParams = []string{"require", "pattern", "maxLength", "minLength", "maximum", "minimum", "maxItems", "minItems"} + +// CastError is used for cast type fails +type CastError struct { + Message *string +} + +// Request is used wrap http request +type Request struct { + Protocol *string + Port *int + Method *string + Pathname *string + Domain *string + Headers map[string]*string + Query map[string]*string + Body io.Reader +} + +// Response is use d wrap http response +type Response struct { + Body io.ReadCloser + StatusCode *int + StatusMessage *string + Headers map[string]*string +} + +// SDKError struct is used save error code and message +type SDKError struct { + Code *string + StatusCode *int + Message *string + Data *string + Stack *string + errMsg *string +} + +// RuntimeObject is used for converting http configuration +type RuntimeObject struct { + IgnoreSSL *bool `json:"ignoreSSL" xml:"ignoreSSL"` + ReadTimeout *int `json:"readTimeout" xml:"readTimeout"` + ConnectTimeout *int `json:"connectTimeout" xml:"connectTimeout"` + LocalAddr *string `json:"localAddr" xml:"localAddr"` + HttpProxy *string `json:"httpProxy" xml:"httpProxy"` + HttpsProxy *string `json:"httpsProxy" xml:"httpsProxy"` + NoProxy *string `json:"noProxy" xml:"noProxy"` + MaxIdleConns *int `json:"maxIdleConns" xml:"maxIdleConns"` + Key *string `json:"key" xml:"key"` + Cert *string `json:"cert" xml:"cert"` + CA *string `json:"ca" xml:"ca"` + Socks5Proxy *string `json:"socks5Proxy" xml:"socks5Proxy"` + Socks5NetWork *string `json:"socks5NetWork" xml:"socks5NetWork"` + Listener utils.ProgressListener `json:"listener" xml:"listener"` + Tracker *utils.ReaderTracker `json:"tracker" xml:"tracker"` + Logger *utils.Logger `json:"logger" xml:"logger"` +} + +type teaClient struct { + sync.Mutex + httpClient *http.Client + ifInit bool +} + +var clientPool = &sync.Map{} + +func (r *RuntimeObject) getClientTag(domain string) string { + return strconv.FormatBool(BoolValue(r.IgnoreSSL)) + strconv.Itoa(IntValue(r.ReadTimeout)) + + strconv.Itoa(IntValue(r.ConnectTimeout)) + StringValue(r.LocalAddr) + StringValue(r.HttpProxy) + + StringValue(r.HttpsProxy) + StringValue(r.NoProxy) + StringValue(r.Socks5Proxy) + StringValue(r.Socks5NetWork) + domain +} + +// NewRuntimeObject is used for shortly create runtime object +func NewRuntimeObject(runtime map[string]interface{}) *RuntimeObject { + if runtime == nil { + return &RuntimeObject{} + } + + runtimeObject := &RuntimeObject{ + IgnoreSSL: TransInterfaceToBool(runtime["ignoreSSL"]), + ReadTimeout: TransInterfaceToInt(runtime["readTimeout"]), + ConnectTimeout: TransInterfaceToInt(runtime["connectTimeout"]), + LocalAddr: TransInterfaceToString(runtime["localAddr"]), + HttpProxy: TransInterfaceToString(runtime["httpProxy"]), + HttpsProxy: TransInterfaceToString(runtime["httpsProxy"]), + NoProxy: TransInterfaceToString(runtime["noProxy"]), + MaxIdleConns: TransInterfaceToInt(runtime["maxIdleConns"]), + Socks5Proxy: TransInterfaceToString(runtime["socks5Proxy"]), + Socks5NetWork: TransInterfaceToString(runtime["socks5NetWork"]), + Key: TransInterfaceToString(runtime["key"]), + Cert: TransInterfaceToString(runtime["cert"]), + CA: TransInterfaceToString(runtime["ca"]), + } + if runtime["listener"] != nil { + runtimeObject.Listener = runtime["listener"].(utils.ProgressListener) + } + if runtime["tracker"] != nil { + runtimeObject.Tracker = runtime["tracker"].(*utils.ReaderTracker) + } + if runtime["logger"] != nil { + runtimeObject.Logger = runtime["logger"].(*utils.Logger) + } + return runtimeObject +} + +// NewCastError is used for cast type fails +func NewCastError(message *string) (err error) { + return &CastError{ + Message: message, + } +} + +// NewRequest is used shortly create Request +func NewRequest() (req *Request) { + return &Request{ + Headers: map[string]*string{}, + Query: map[string]*string{}, + } +} + +// NewResponse is create response with http response +func NewResponse(httpResponse *http.Response) (res *Response) { + res = &Response{} + res.Body = httpResponse.Body + res.Headers = make(map[string]*string) + res.StatusCode = Int(httpResponse.StatusCode) + res.StatusMessage = String(httpResponse.Status) + return +} + +// NewSDKError is used for shortly create SDKError object +func NewSDKError(obj map[string]interface{}) *SDKError { + err := &SDKError{} + if val, ok := obj["code"].(int); ok { + err.Code = String(strconv.Itoa(val)) + } else if val, ok := obj["code"].(string); ok { + err.Code = String(val) + } + + if statusCode, ok := obj["statusCode"].(int); ok { + err.StatusCode = Int(statusCode) + } else if status, ok := obj["statusCode"].(string); ok { + statusCode, err2 := strconv.Atoi(status) + if err2 == nil { + err.StatusCode = Int(statusCode) + } + } + + if obj["message"] != nil { + err.Message = String(obj["message"].(string)) + } + if data := obj["data"]; data != nil { + byt, _ := json.Marshal(data) + err.Data = String(string(byt)) + } + return err +} + +// Set ErrMsg by msg +func (err *SDKError) SetErrMsg(msg string) { + err.errMsg = String(msg) +} + +func (err *SDKError) Error() string { + if err.errMsg == nil { + str := fmt.Sprintf("SDKError:\n StatusCode: %d\n Code: %s\n Message: %s\n Data: %s\n", + IntValue(err.StatusCode), StringValue(err.Code), StringValue(err.Message), StringValue(err.Data)) + err.SetErrMsg(str) + } + return StringValue(err.errMsg) +} + +// Return message of CastError +func (err *CastError) Error() string { + return StringValue(err.Message) +} + +// Convert is use convert map[string]interface object to struct +func Convert(in interface{}, out interface{}) error { + byt, _ := json.Marshal(in) + decoder := jsonParser.NewDecoder(bytes.NewReader(byt)) + decoder.UseNumber() + err := decoder.Decode(&out) + return err +} + +// Convert is use convert map[string]interface object to struct +func Recover(in interface{}) error { + if in == nil { + return nil + } + return errors.New(fmt.Sprint(in)) +} + +// ReadBody is used read response body +func (response *Response) ReadBody() (body []byte, err error) { + defer response.Body.Close() + var buffer [512]byte + result := bytes.NewBuffer(nil) + + for { + n, err := response.Body.Read(buffer[0:]) + result.Write(buffer[0:n]) + if err != nil && err == io.EOF { + break + } else if err != nil { + return nil, err + } + } + return result.Bytes(), nil +} + +func getTeaClient(tag string) *teaClient { + client, ok := clientPool.Load(tag) + if client == nil && !ok { + client = &teaClient{ + httpClient: &http.Client{}, + ifInit: false, + } + clientPool.Store(tag, client) + } + return client.(*teaClient) +} + +// DoRequest is used send request to server +func DoRequest(request *Request, requestRuntime map[string]interface{}) (response *Response, err error) { + runtimeObject := NewRuntimeObject(requestRuntime) + fieldMap := make(map[string]string) + utils.InitLogMsg(fieldMap) + defer func() { + if runtimeObject.Logger != nil { + runtimeObject.Logger.PrintLog(fieldMap, err) + } + }() + if request.Method == nil { + request.Method = String("GET") + } + + if request.Protocol == nil { + request.Protocol = String("http") + } else { + request.Protocol = String(strings.ToLower(StringValue(request.Protocol))) + } + + requestURL := "" + request.Domain = request.Headers["host"] + requestURL = fmt.Sprintf("%s://%s%s", StringValue(request.Protocol), StringValue(request.Domain), StringValue(request.Pathname)) + queryParams := request.Query + // sort QueryParams by key + q := url.Values{} + for key, value := range queryParams { + q.Add(key, StringValue(value)) + } + querystring := q.Encode() + if len(querystring) > 0 { + if strings.Contains(requestURL, "?") { + requestURL = fmt.Sprintf("%s&%s", requestURL, querystring) + } else { + requestURL = fmt.Sprintf("%s?%s", requestURL, querystring) + } + } + debugLog("> %s %s", StringValue(request.Method), requestURL) + + httpRequest, err := http.NewRequest(StringValue(request.Method), requestURL, request.Body) + if err != nil { + return + } + httpRequest.Host = StringValue(request.Domain) + + client := getTeaClient(runtimeObject.getClientTag(StringValue(request.Domain))) + client.Lock() + if !client.ifInit { + trans, err := getHttpTransport(request, runtimeObject) + if err != nil { + return nil, err + } + client.httpClient.Timeout = time.Duration(IntValue(runtimeObject.ReadTimeout)) * time.Millisecond + client.httpClient.Transport = trans + client.ifInit = true + } + client.Unlock() + for key, value := range request.Headers { + if value == nil || key == "content-length" { + continue + } else if key == "host" { + httpRequest.Header["Host"] = []string{*value} + delete(httpRequest.Header, "host") + } else if key == "user-agent" { + httpRequest.Header["User-Agent"] = []string{*value} + delete(httpRequest.Header, "user-agent") + } else { + httpRequest.Header[key] = []string{*value} + } + debugLog("> %s: %s", key, StringValue(value)) + } + contentlength, _ := strconv.Atoi(StringValue(request.Headers["content-length"])) + event := utils.NewProgressEvent(utils.TransferStartedEvent, 0, int64(contentlength), 0) + utils.PublishProgress(runtimeObject.Listener, event) + + putMsgToMap(fieldMap, httpRequest) + startTime := time.Now() + fieldMap["{start_time}"] = startTime.Format("2006-01-02 15:04:05") + res, err := hookDo(client.httpClient.Do)(httpRequest) + fieldMap["{cost}"] = time.Since(startTime).String() + completedBytes := int64(0) + if runtimeObject.Tracker != nil { + completedBytes = runtimeObject.Tracker.CompletedBytes + } + if err != nil { + event = utils.NewProgressEvent(utils.TransferFailedEvent, completedBytes, int64(contentlength), 0) + utils.PublishProgress(runtimeObject.Listener, event) + return + } + + event = utils.NewProgressEvent(utils.TransferCompletedEvent, completedBytes, int64(contentlength), 0) + utils.PublishProgress(runtimeObject.Listener, event) + + response = NewResponse(res) + fieldMap["{code}"] = strconv.Itoa(res.StatusCode) + fieldMap["{res_headers}"] = transToString(res.Header) + debugLog("< HTTP/1.1 %s", res.Status) + for key, value := range res.Header { + debugLog("< %s: %s", key, strings.Join(value, "")) + if len(value) != 0 { + response.Headers[strings.ToLower(key)] = String(value[0]) + } + } + return +} + +func getHttpTransport(req *Request, runtime *RuntimeObject) (*http.Transport, error) { + trans := new(http.Transport) + httpProxy, err := getHttpProxy(StringValue(req.Protocol), StringValue(req.Domain), runtime) + if err != nil { + return nil, err + } + if strings.ToLower(*req.Protocol) == "https" && + runtime.Key != nil && runtime.Cert != nil { + cert, err := tls.X509KeyPair([]byte(StringValue(runtime.Cert)), []byte(StringValue(runtime.Key))) + if err != nil { + return nil, err + } + + trans.TLSClientConfig = &tls.Config{ + Certificates: []tls.Certificate{cert}, + InsecureSkipVerify: BoolValue(runtime.IgnoreSSL), + } + if runtime.CA != nil { + clientCertPool := x509.NewCertPool() + ok := clientCertPool.AppendCertsFromPEM([]byte(StringValue(runtime.CA))) + if !ok { + return nil, errors.New("Failed to parse root certificate") + } + trans.TLSClientConfig.RootCAs = clientCertPool + } + } else { + trans.TLSClientConfig = &tls.Config{ + InsecureSkipVerify: BoolValue(runtime.IgnoreSSL), + } + } + if httpProxy != nil { + trans.Proxy = http.ProxyURL(httpProxy) + if httpProxy.User != nil { + password, _ := httpProxy.User.Password() + auth := httpProxy.User.Username() + ":" + password + basic := "Basic " + base64.StdEncoding.EncodeToString([]byte(auth)) + req.Headers["Proxy-Authorization"] = String(basic) + } + } + if runtime.Socks5Proxy != nil && StringValue(runtime.Socks5Proxy) != "" { + socks5Proxy, err := getSocks5Proxy(runtime) + if err != nil { + return nil, err + } + if socks5Proxy != nil { + var auth *proxy.Auth + if socks5Proxy.User != nil { + password, _ := socks5Proxy.User.Password() + auth = &proxy.Auth{ + User: socks5Proxy.User.Username(), + Password: password, + } + } + dialer, err := proxy.SOCKS5(strings.ToLower(StringValue(runtime.Socks5NetWork)), socks5Proxy.String(), auth, + &net.Dialer{ + Timeout: time.Duration(IntValue(runtime.ConnectTimeout)) * time.Millisecond, + DualStack: true, + LocalAddr: getLocalAddr(StringValue(runtime.LocalAddr)), + }) + if err != nil { + return nil, err + } + trans.Dial = dialer.Dial + } + } else { + trans.DialContext = setDialContext(runtime) + } + return trans, nil +} + +func transToString(object interface{}) string { + byt, _ := json.Marshal(object) + return string(byt) +} + +func putMsgToMap(fieldMap map[string]string, request *http.Request) { + fieldMap["{host}"] = request.Host + fieldMap["{method}"] = request.Method + fieldMap["{uri}"] = request.URL.RequestURI() + fieldMap["{pid}"] = strconv.Itoa(os.Getpid()) + fieldMap["{version}"] = strings.Split(request.Proto, "/")[1] + hostname, _ := os.Hostname() + fieldMap["{hostname}"] = hostname + fieldMap["{req_headers}"] = transToString(request.Header) + fieldMap["{target}"] = request.URL.Path + request.URL.RawQuery +} + +func getNoProxy(protocol string, runtime *RuntimeObject) []string { + var urls []string + if runtime.NoProxy != nil && StringValue(runtime.NoProxy) != "" { + urls = strings.Split(StringValue(runtime.NoProxy), ",") + } else if rawurl := os.Getenv("NO_PROXY"); rawurl != "" { + urls = strings.Split(rawurl, ",") + } else if rawurl := os.Getenv("no_proxy"); rawurl != "" { + urls = strings.Split(rawurl, ",") + } + + return urls +} + +func ToReader(obj interface{}) io.Reader { + switch obj.(type) { + case *string: + tmp := obj.(*string) + return strings.NewReader(StringValue(tmp)) + case []byte: + return strings.NewReader(string(obj.([]byte))) + case io.Reader: + return obj.(io.Reader) + default: + panic("Invalid Body. Please set a valid Body.") + } +} + +func ToString(val interface{}) string { + return fmt.Sprintf("%v", val) +} + +func getHttpProxy(protocol, host string, runtime *RuntimeObject) (proxy *url.URL, err error) { + urls := getNoProxy(protocol, runtime) + for _, url := range urls { + if url == host { + return nil, nil + } + } + if protocol == "https" { + if runtime.HttpsProxy != nil && StringValue(runtime.HttpsProxy) != "" { + proxy, err = url.Parse(StringValue(runtime.HttpsProxy)) + } else if rawurl := os.Getenv("HTTPS_PROXY"); rawurl != "" { + proxy, err = url.Parse(rawurl) + } else if rawurl := os.Getenv("https_proxy"); rawurl != "" { + proxy, err = url.Parse(rawurl) + } + } else { + if runtime.HttpProxy != nil && StringValue(runtime.HttpProxy) != "" { + proxy, err = url.Parse(StringValue(runtime.HttpProxy)) + } else if rawurl := os.Getenv("HTTP_PROXY"); rawurl != "" { + proxy, err = url.Parse(rawurl) + } else if rawurl := os.Getenv("http_proxy"); rawurl != "" { + proxy, err = url.Parse(rawurl) + } + } + + return proxy, err +} + +func getSocks5Proxy(runtime *RuntimeObject) (proxy *url.URL, err error) { + if runtime.Socks5Proxy != nil && StringValue(runtime.Socks5Proxy) != "" { + proxy, err = url.Parse(StringValue(runtime.Socks5Proxy)) + } + return proxy, err +} + +func getLocalAddr(localAddr string) (addr *net.TCPAddr) { + if localAddr != "" { + addr = &net.TCPAddr{ + IP: []byte(localAddr), + } + } + return addr +} + +func setDialContext(runtime *RuntimeObject) func(cxt context.Context, net, addr string) (c net.Conn, err error) { + return func(ctx context.Context, network, address string) (net.Conn, error) { + if runtime.LocalAddr != nil && StringValue(runtime.LocalAddr) != "" { + netAddr := &net.TCPAddr{ + IP: []byte(StringValue(runtime.LocalAddr)), + } + return (&net.Dialer{ + Timeout: time.Duration(IntValue(runtime.ConnectTimeout)) * time.Second, + DualStack: true, + LocalAddr: netAddr, + }).DialContext(ctx, network, address) + } + return (&net.Dialer{ + Timeout: time.Duration(IntValue(runtime.ConnectTimeout)) * time.Second, + DualStack: true, + }).DialContext(ctx, network, address) + } +} + +func ToObject(obj interface{}) map[string]interface{} { + result := make(map[string]interface{}) + byt, _ := json.Marshal(obj) + err := json.Unmarshal(byt, &result) + if err != nil { + return nil + } + return result +} + +func AllowRetry(retry interface{}, retryTimes *int) *bool { + if IntValue(retryTimes) == 0 { + return Bool(true) + } + retryMap, ok := retry.(map[string]interface{}) + if !ok { + return Bool(false) + } + retryable, ok := retryMap["retryable"].(bool) + if !ok || !retryable { + return Bool(false) + } + + maxAttempts, ok := retryMap["maxAttempts"].(int) + if !ok || maxAttempts < IntValue(retryTimes) { + return Bool(false) + } + return Bool(true) +} + +func Merge(args ...interface{}) map[string]*string { + finalArg := make(map[string]*string) + for _, obj := range args { + switch obj.(type) { + case map[string]*string: + arg := obj.(map[string]*string) + for key, value := range arg { + if value != nil { + finalArg[key] = value + } + } + default: + byt, _ := json.Marshal(obj) + arg := make(map[string]string) + err := json.Unmarshal(byt, &arg) + if err != nil { + return finalArg + } + for key, value := range arg { + if value != "" { + finalArg[key] = String(value) + } + } + } + } + + return finalArg +} + +func isNil(a interface{}) bool { + defer func() { + recover() + }() + vi := reflect.ValueOf(a) + return vi.IsNil() +} + +func ToMap(args ...interface{}) map[string]interface{} { + isNotNil := false + finalArg := make(map[string]interface{}) + for _, obj := range args { + if obj == nil { + continue + } + + if isNil(obj) { + continue + } + isNotNil = true + + switch obj.(type) { + case map[string]*string: + arg := obj.(map[string]*string) + for key, value := range arg { + if value != nil { + finalArg[key] = StringValue(value) + } + } + case map[string]interface{}: + arg := obj.(map[string]interface{}) + for key, value := range arg { + if value != nil { + finalArg[key] = value + } + } + case *string: + str := obj.(*string) + arg := make(map[string]interface{}) + err := json.Unmarshal([]byte(StringValue(str)), &arg) + if err == nil { + for key, value := range arg { + if value != nil { + finalArg[key] = value + } + } + } + tmp := make(map[string]string) + err = json.Unmarshal([]byte(StringValue(str)), &tmp) + if err == nil { + for key, value := range arg { + if value != "" { + finalArg[key] = value + } + } + } + case []byte: + byt := obj.([]byte) + arg := make(map[string]interface{}) + err := json.Unmarshal(byt, &arg) + if err == nil { + for key, value := range arg { + if value != nil { + finalArg[key] = value + } + } + break + } + default: + val := reflect.ValueOf(obj) + res := structToMap(val) + for key, value := range res { + if value != nil { + finalArg[key] = value + } + } + } + } + + if !isNotNil { + return nil + } + return finalArg +} + +func structToMap(dataValue reflect.Value) map[string]interface{} { + out := make(map[string]interface{}) + if !dataValue.IsValid() { + return out + } + if dataValue.Kind().String() == "ptr" { + if dataValue.IsNil() { + return out + } + dataValue = dataValue.Elem() + } + if !dataValue.IsValid() { + return out + } + dataType := dataValue.Type() + if dataType.Kind().String() != "struct" { + return out + } + for i := 0; i < dataType.NumField(); i++ { + field := dataType.Field(i) + name, containsNameTag := field.Tag.Lookup("json") + if !containsNameTag { + name = field.Name + } else { + strs := strings.Split(name, ",") + name = strs[0] + } + fieldValue := dataValue.FieldByName(field.Name) + if !fieldValue.IsValid() || fieldValue.IsNil() { + continue + } + if field.Type.String() == "io.Reader" || field.Type.String() == "io.Writer" { + continue + } else if field.Type.Kind().String() == "struct" { + out[name] = structToMap(fieldValue) + } else if field.Type.Kind().String() == "ptr" && + field.Type.Elem().Kind().String() == "struct" { + if fieldValue.Elem().IsValid() { + out[name] = structToMap(fieldValue) + } + } else if field.Type.Kind().String() == "ptr" { + if fieldValue.IsValid() && !fieldValue.IsNil() { + out[name] = fieldValue.Elem().Interface() + } + } else if field.Type.Kind().String() == "slice" { + tmp := make([]interface{}, 0) + num := fieldValue.Len() + for i := 0; i < num; i++ { + value := fieldValue.Index(i) + if !value.IsValid() { + continue + } + if value.Type().Kind().String() == "ptr" && + value.Type().Elem().Kind().String() == "struct" { + if value.IsValid() && !value.IsNil() { + tmp = append(tmp, structToMap(value)) + } + } else if value.Type().Kind().String() == "struct" { + tmp = append(tmp, structToMap(value)) + } else if value.Type().Kind().String() == "ptr" { + if value.IsValid() && !value.IsNil() { + tmp = append(tmp, value.Elem().Interface()) + } + } else { + tmp = append(tmp, value.Interface()) + } + } + if len(tmp) > 0 { + out[name] = tmp + } + } else { + out[name] = fieldValue.Interface() + } + + } + return out +} + +func Retryable(err error) *bool { + if err == nil { + return Bool(false) + } + if realErr, ok := err.(*SDKError); ok { + if realErr.StatusCode == nil { + return Bool(false) + } + code := IntValue(realErr.StatusCode) + return Bool(code >= http.StatusInternalServerError) + } + return Bool(true) +} + +func GetBackoffTime(backoff interface{}, retrytimes *int) *int { + backoffMap, ok := backoff.(map[string]interface{}) + if !ok { + return Int(0) + } + policy, ok := backoffMap["policy"].(string) + if !ok || policy == "no" { + return Int(0) + } + + period, ok := backoffMap["period"].(int) + if !ok || period == 0 { + return Int(0) + } + + maxTime := math.Pow(2.0, float64(IntValue(retrytimes))) + return Int(rand.Intn(int(maxTime-1)) * period) +} + +func Sleep(backoffTime *int) { + sleeptime := time.Duration(IntValue(backoffTime)) * time.Second + time.Sleep(sleeptime) +} + +func Validate(params interface{}) error { + if params == nil { + return nil + } + requestValue := reflect.ValueOf(params) + if requestValue.IsNil() { + return nil + } + err := validate(requestValue.Elem()) + return err +} + +// Verify whether the parameters meet the requirements +func validate(dataValue reflect.Value) error { + if strings.HasPrefix(dataValue.Type().String(), "*") { // Determines whether the input is a structure object or a pointer object + if dataValue.IsNil() { + return nil + } + dataValue = dataValue.Elem() + } + dataType := dataValue.Type() + for i := 0; i < dataType.NumField(); i++ { + field := dataType.Field(i) + valueField := dataValue.Field(i) + for _, value := range validateParams { + err := validateParam(field, valueField, value) + if err != nil { + return err + } + } + } + return nil +} + +func validateParam(field reflect.StructField, valueField reflect.Value, tagName string) error { + tag, containsTag := field.Tag.Lookup(tagName) // Take out the checked regular expression + if containsTag && tagName == "require" { + err := checkRequire(field, valueField) + if err != nil { + return err + } + } + if strings.HasPrefix(field.Type.String(), "[]") { // Verify the parameters of the array type + err := validateSlice(field, valueField, containsTag, tag, tagName) + if err != nil { + return err + } + } else if valueField.Kind() == reflect.Ptr { // Determines whether it is a pointer object + err := validatePtr(field, valueField, containsTag, tag, tagName) + if err != nil { + return err + } + } + return nil +} + +func validateSlice(field reflect.StructField, valueField reflect.Value, containsregexpTag bool, tag, tagName string) error { + if valueField.IsValid() && !valueField.IsNil() { // Determines whether the parameter has a value + if containsregexpTag { + if tagName == "maxItems" { + err := checkMaxItems(field, valueField, tag) + if err != nil { + return err + } + } + + if tagName == "minItems" { + err := checkMinItems(field, valueField, tag) + if err != nil { + return err + } + } + } + + for m := 0; m < valueField.Len(); m++ { + elementValue := valueField.Index(m) + if elementValue.Type().Kind() == reflect.Ptr { // Determines whether the child elements of an array are of a basic type + err := validatePtr(field, elementValue, containsregexpTag, tag, tagName) + if err != nil { + return err + } + } + } + } + return nil +} + +func validatePtr(field reflect.StructField, elementValue reflect.Value, containsregexpTag bool, tag, tagName string) error { + if elementValue.IsNil() { + return nil + } + if isFilterType(elementValue.Elem().Type().String(), basicTypes) { + if containsregexpTag { + if tagName == "pattern" { + err := checkPattern(field, elementValue.Elem(), tag) + if err != nil { + return err + } + } + + if tagName == "maxLength" { + err := checkMaxLength(field, elementValue.Elem(), tag) + if err != nil { + return err + } + } + + if tagName == "minLength" { + err := checkMinLength(field, elementValue.Elem(), tag) + if err != nil { + return err + } + } + + if tagName == "maximum" { + err := checkMaximum(field, elementValue.Elem(), tag) + if err != nil { + return err + } + } + + if tagName == "minimum" { + err := checkMinimum(field, elementValue.Elem(), tag) + if err != nil { + return err + } + } + } + } else { + err := validate(elementValue) + if err != nil { + return err + } + } + return nil +} + +func checkRequire(field reflect.StructField, valueField reflect.Value) error { + name, _ := field.Tag.Lookup("json") + strs := strings.Split(name, ",") + name = strs[0] + if !valueField.IsNil() && valueField.IsValid() { + return nil + } + return errors.New(name + " should be setted") +} + +func checkPattern(field reflect.StructField, valueField reflect.Value, tag string) error { + if valueField.IsValid() && valueField.String() != "" { + value := valueField.String() + r, _ := regexp.Compile("^" + tag + "$") + if match := r.MatchString(value); !match { // Determines whether the parameter value satisfies the regular expression or not, and throws an error + return errors.New(value + " is not matched " + tag) + } + } + return nil +} + +func checkMaxItems(field reflect.StructField, valueField reflect.Value, tag string) error { + if valueField.IsValid() && valueField.String() != "" { + maxItems, err := strconv.Atoi(tag) + if err != nil { + return err + } + length := valueField.Len() + if maxItems < length { + errMsg := fmt.Sprintf("The length of %s is %d which is more than %d", field.Name, length, maxItems) + return errors.New(errMsg) + } + } + return nil +} + +func checkMinItems(field reflect.StructField, valueField reflect.Value, tag string) error { + if valueField.IsValid() { + minItems, err := strconv.Atoi(tag) + if err != nil { + return err + } + length := valueField.Len() + if minItems > length { + errMsg := fmt.Sprintf("The length of %s is %d which is less than %d", field.Name, length, minItems) + return errors.New(errMsg) + } + } + return nil +} + +func checkMaxLength(field reflect.StructField, valueField reflect.Value, tag string) error { + if valueField.IsValid() && valueField.String() != "" { + maxLength, err := strconv.Atoi(tag) + if err != nil { + return err + } + length := valueField.Len() + if valueField.Kind().String() == "string" { + length = strings.Count(valueField.String(), "") - 1 + } + if maxLength < length { + errMsg := fmt.Sprintf("The length of %s is %d which is more than %d", field.Name, length, maxLength) + return errors.New(errMsg) + } + } + return nil +} + +func checkMinLength(field reflect.StructField, valueField reflect.Value, tag string) error { + if valueField.IsValid() { + minLength, err := strconv.Atoi(tag) + if err != nil { + return err + } + length := valueField.Len() + if valueField.Kind().String() == "string" { + length = strings.Count(valueField.String(), "") - 1 + } + if minLength > length { + errMsg := fmt.Sprintf("The length of %s is %d which is less than %d", field.Name, length, minLength) + return errors.New(errMsg) + } + } + return nil +} + +func checkMaximum(field reflect.StructField, valueField reflect.Value, tag string) error { + if valueField.IsValid() && valueField.String() != "" { + maximum, err := strconv.ParseFloat(tag, 64) + if err != nil { + return err + } + byt, _ := json.Marshal(valueField.Interface()) + num, err := strconv.ParseFloat(string(byt), 64) + if err != nil { + return err + } + if maximum < num { + errMsg := fmt.Sprintf("The size of %s is %f which is greater than %f", field.Name, num, maximum) + return errors.New(errMsg) + } + } + return nil +} + +func checkMinimum(field reflect.StructField, valueField reflect.Value, tag string) error { + if valueField.IsValid() && valueField.String() != "" { + minimum, err := strconv.ParseFloat(tag, 64) + if err != nil { + return err + } + + byt, _ := json.Marshal(valueField.Interface()) + num, err := strconv.ParseFloat(string(byt), 64) + if err != nil { + return err + } + if minimum > num { + errMsg := fmt.Sprintf("The size of %s is %f which is less than %f", field.Name, num, minimum) + return errors.New(errMsg) + } + } + return nil +} + +// Determines whether realType is in filterTypes +func isFilterType(realType string, filterTypes []string) bool { + for _, value := range filterTypes { + if value == realType { + return true + } + } + return false +} + +func TransInterfaceToBool(val interface{}) *bool { + if val == nil { + return nil + } + + return Bool(val.(bool)) +} + +func TransInterfaceToInt(val interface{}) *int { + if val == nil { + return nil + } + + return Int(val.(int)) +} + +func TransInterfaceToString(val interface{}) *string { + if val == nil { + return nil + } + + return String(val.(string)) +} + +func Prettify(i interface{}) string { + resp, _ := json.MarshalIndent(i, "", " ") + return string(resp) +} + +func ToInt(a *int32) *int { + return Int(int(Int32Value(a))) +} + +func ToInt32(a *int) *int32 { + return Int32(int32(IntValue(a))) +} diff --git a/vendor/github.com/alibabacloud-go/tea/tea/trans.go b/vendor/github.com/alibabacloud-go/tea/tea/trans.go new file mode 100644 index 000000000..ded1642fa --- /dev/null +++ b/vendor/github.com/alibabacloud-go/tea/tea/trans.go @@ -0,0 +1,491 @@ +package tea + +func String(a string) *string { + return &a +} + +func StringValue(a *string) string { + if a == nil { + return "" + } + return *a +} + +func Int(a int) *int { + return &a +} + +func IntValue(a *int) int { + if a == nil { + return 0 + } + return *a +} + +func Int8(a int8) *int8 { + return &a +} + +func Int8Value(a *int8) int8 { + if a == nil { + return 0 + } + return *a +} + +func Int16(a int16) *int16 { + return &a +} + +func Int16Value(a *int16) int16 { + if a == nil { + return 0 + } + return *a +} + +func Int32(a int32) *int32 { + return &a +} + +func Int32Value(a *int32) int32 { + if a == nil { + return 0 + } + return *a +} + +func Int64(a int64) *int64 { + return &a +} + +func Int64Value(a *int64) int64 { + if a == nil { + return 0 + } + return *a +} + +func Bool(a bool) *bool { + return &a +} + +func BoolValue(a *bool) bool { + if a == nil { + return false + } + return *a +} + +func Uint(a uint) *uint { + return &a +} + +func UintValue(a *uint) uint { + if a == nil { + return 0 + } + return *a +} + +func Uint8(a uint8) *uint8 { + return &a +} + +func Uint8Value(a *uint8) uint8 { + if a == nil { + return 0 + } + return *a +} + +func Uint16(a uint16) *uint16 { + return &a +} + +func Uint16Value(a *uint16) uint16 { + if a == nil { + return 0 + } + return *a +} + +func Uint32(a uint32) *uint32 { + return &a +} + +func Uint32Value(a *uint32) uint32 { + if a == nil { + return 0 + } + return *a +} + +func Uint64(a uint64) *uint64 { + return &a +} + +func Uint64Value(a *uint64) uint64 { + if a == nil { + return 0 + } + return *a +} + +func Float32(a float32) *float32 { + return &a +} + +func Float32Value(a *float32) float32 { + if a == nil { + return 0 + } + return *a +} + +func Float64(a float64) *float64 { + return &a +} + +func Float64Value(a *float64) float64 { + if a == nil { + return 0 + } + return *a +} + +func IntSlice(a []int) []*int { + if a == nil { + return nil + } + res := make([]*int, len(a)) + for i := 0; i < len(a); i++ { + res[i] = &a[i] + } + return res +} + +func IntValueSlice(a []*int) []int { + if a == nil { + return nil + } + res := make([]int, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != nil { + res[i] = *a[i] + } + } + return res +} + +func Int8Slice(a []int8) []*int8 { + if a == nil { + return nil + } + res := make([]*int8, len(a)) + for i := 0; i < len(a); i++ { + res[i] = &a[i] + } + return res +} + +func Int8ValueSlice(a []*int8) []int8 { + if a == nil { + return nil + } + res := make([]int8, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != nil { + res[i] = *a[i] + } + } + return res +} + +func Int16Slice(a []int16) []*int16 { + if a == nil { + return nil + } + res := make([]*int16, len(a)) + for i := 0; i < len(a); i++ { + res[i] = &a[i] + } + return res +} + +func Int16ValueSlice(a []*int16) []int16 { + if a == nil { + return nil + } + res := make([]int16, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != nil { + res[i] = *a[i] + } + } + return res +} + +func Int32Slice(a []int32) []*int32 { + if a == nil { + return nil + } + res := make([]*int32, len(a)) + for i := 0; i < len(a); i++ { + res[i] = &a[i] + } + return res +} + +func Int32ValueSlice(a []*int32) []int32 { + if a == nil { + return nil + } + res := make([]int32, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != nil { + res[i] = *a[i] + } + } + return res +} + +func Int64Slice(a []int64) []*int64 { + if a == nil { + return nil + } + res := make([]*int64, len(a)) + for i := 0; i < len(a); i++ { + res[i] = &a[i] + } + return res +} + +func Int64ValueSlice(a []*int64) []int64 { + if a == nil { + return nil + } + res := make([]int64, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != nil { + res[i] = *a[i] + } + } + return res +} + +func UintSlice(a []uint) []*uint { + if a == nil { + return nil + } + res := make([]*uint, len(a)) + for i := 0; i < len(a); i++ { + res[i] = &a[i] + } + return res +} + +func UintValueSlice(a []*uint) []uint { + if a == nil { + return nil + } + res := make([]uint, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != nil { + res[i] = *a[i] + } + } + return res +} + +func Uint8Slice(a []uint8) []*uint8 { + if a == nil { + return nil + } + res := make([]*uint8, len(a)) + for i := 0; i < len(a); i++ { + res[i] = &a[i] + } + return res +} + +func Uint8ValueSlice(a []*uint8) []uint8 { + if a == nil { + return nil + } + res := make([]uint8, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != nil { + res[i] = *a[i] + } + } + return res +} + +func Uint16Slice(a []uint16) []*uint16 { + if a == nil { + return nil + } + res := make([]*uint16, len(a)) + for i := 0; i < len(a); i++ { + res[i] = &a[i] + } + return res +} + +func Uint16ValueSlice(a []*uint16) []uint16 { + if a == nil { + return nil + } + res := make([]uint16, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != nil { + res[i] = *a[i] + } + } + return res +} + +func Uint32Slice(a []uint32) []*uint32 { + if a == nil { + return nil + } + res := make([]*uint32, len(a)) + for i := 0; i < len(a); i++ { + res[i] = &a[i] + } + return res +} + +func Uint32ValueSlice(a []*uint32) []uint32 { + if a == nil { + return nil + } + res := make([]uint32, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != nil { + res[i] = *a[i] + } + } + return res +} + +func Uint64Slice(a []uint64) []*uint64 { + if a == nil { + return nil + } + res := make([]*uint64, len(a)) + for i := 0; i < len(a); i++ { + res[i] = &a[i] + } + return res +} + +func Uint64ValueSlice(a []*uint64) []uint64 { + if a == nil { + return nil + } + res := make([]uint64, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != nil { + res[i] = *a[i] + } + } + return res +} + +func Float32Slice(a []float32) []*float32 { + if a == nil { + return nil + } + res := make([]*float32, len(a)) + for i := 0; i < len(a); i++ { + res[i] = &a[i] + } + return res +} + +func Float32ValueSlice(a []*float32) []float32 { + if a == nil { + return nil + } + res := make([]float32, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != nil { + res[i] = *a[i] + } + } + return res +} + +func Float64Slice(a []float64) []*float64 { + if a == nil { + return nil + } + res := make([]*float64, len(a)) + for i := 0; i < len(a); i++ { + res[i] = &a[i] + } + return res +} + +func Float64ValueSlice(a []*float64) []float64 { + if a == nil { + return nil + } + res := make([]float64, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != nil { + res[i] = *a[i] + } + } + return res +} + +func StringSlice(a []string) []*string { + if a == nil { + return nil + } + res := make([]*string, len(a)) + for i := 0; i < len(a); i++ { + res[i] = &a[i] + } + return res +} + +func StringSliceValue(a []*string) []string { + if a == nil { + return nil + } + res := make([]string, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != nil { + res[i] = *a[i] + } + } + return res +} + +func BoolSlice(a []bool) []*bool { + if a == nil { + return nil + } + res := make([]*bool, len(a)) + for i := 0; i < len(a); i++ { + res[i] = &a[i] + } + return res +} + +func BoolSliceValue(a []*bool) []bool { + if a == nil { + return nil + } + res := make([]bool, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != nil { + res[i] = *a[i] + } + } + return res +} diff --git a/vendor/github.com/alibabacloud-go/tea/utils/assert.go b/vendor/github.com/alibabacloud-go/tea/utils/assert.go new file mode 100644 index 000000000..7ae677501 --- /dev/null +++ b/vendor/github.com/alibabacloud-go/tea/utils/assert.go @@ -0,0 +1,64 @@ +package utils + +import ( + "reflect" + "strings" + "testing" +) + +func isNil(object interface{}) bool { + if object == nil { + return true + } + + value := reflect.ValueOf(object) + kind := value.Kind() + isNilableKind := containsKind( + []reflect.Kind{ + reflect.Chan, reflect.Func, + reflect.Interface, reflect.Map, + reflect.Ptr, reflect.Slice}, + kind) + + if isNilableKind && value.IsNil() { + return true + } + + return false +} + +func containsKind(kinds []reflect.Kind, kind reflect.Kind) bool { + for i := 0; i < len(kinds); i++ { + if kind == kinds[i] { + return true + } + } + + return false +} + +func AssertEqual(t *testing.T, a, b interface{}) { + if !reflect.DeepEqual(a, b) { + t.Errorf("%v != %v", a, b) + } +} + +func AssertNil(t *testing.T, object interface{}) { + if !isNil(object) { + t.Errorf("%v is not nil", object) + } +} + +func AssertNotNil(t *testing.T, object interface{}) { + if isNil(object) { + t.Errorf("%v is nil", object) + } +} + +func AssertContains(t *testing.T, contains string, msgAndArgs ...string) { + for _, value := range msgAndArgs { + if ok := strings.Contains(contains, value); !ok { + t.Errorf("%s does not contain %s", contains, value) + } + } +} diff --git a/vendor/github.com/alibabacloud-go/tea/utils/logger.go b/vendor/github.com/alibabacloud-go/tea/utils/logger.go new file mode 100644 index 000000000..051366887 --- /dev/null +++ b/vendor/github.com/alibabacloud-go/tea/utils/logger.go @@ -0,0 +1,109 @@ +package utils + +import ( + "io" + "log" + "strings" + "time" +) + +type Logger struct { + *log.Logger + formatTemplate string + isOpen bool + lastLogMsg string +} + +var defaultLoggerTemplate = `{time} {channel}: "{method} {uri} HTTP/{version}" {code} {cost} {hostname}` +var loggerParam = []string{"{time}", "{start_time}", "{ts}", "{channel}", "{pid}", "{host}", "{method}", "{uri}", "{version}", "{target}", "{hostname}", "{code}", "{error}", "{req_headers}", "{res_body}", "{res_headers}", "{cost}"} +var logChannel string + +func InitLogMsg(fieldMap map[string]string) { + for _, value := range loggerParam { + fieldMap[value] = "" + } +} + +func (logger *Logger) SetFormatTemplate(template string) { + logger.formatTemplate = template + +} + +func (logger *Logger) GetFormatTemplate() string { + return logger.formatTemplate + +} + +func NewLogger(level string, channel string, out io.Writer, template string) *Logger { + if level == "" { + level = "info" + } + + logChannel = "AlibabaCloud" + if channel != "" { + logChannel = channel + } + log := log.New(out, "["+strings.ToUpper(level)+"]", log.Lshortfile) + if template == "" { + template = defaultLoggerTemplate + } + + return &Logger{ + Logger: log, + formatTemplate: template, + isOpen: true, + } +} + +func (logger *Logger) OpenLogger() { + logger.isOpen = true +} + +func (logger *Logger) CloseLogger() { + logger.isOpen = false +} + +func (logger *Logger) SetIsopen(isopen bool) { + logger.isOpen = isopen +} + +func (logger *Logger) GetIsopen() bool { + return logger.isOpen +} + +func (logger *Logger) SetLastLogMsg(lastLogMsg string) { + logger.lastLogMsg = lastLogMsg +} + +func (logger *Logger) GetLastLogMsg() string { + return logger.lastLogMsg +} + +func SetLogChannel(channel string) { + logChannel = channel +} + +func (logger *Logger) PrintLog(fieldMap map[string]string, err error) { + if err != nil { + fieldMap["{error}"] = err.Error() + } + fieldMap["{time}"] = time.Now().Format("2006-01-02 15:04:05") + fieldMap["{ts}"] = getTimeInFormatISO8601() + fieldMap["{channel}"] = logChannel + if logger != nil { + logMsg := logger.formatTemplate + for key, value := range fieldMap { + logMsg = strings.Replace(logMsg, key, value, -1) + } + logger.lastLogMsg = logMsg + if logger.isOpen == true { + logger.Output(2, logMsg) + } + } +} + +func getTimeInFormatISO8601() (timeStr string) { + gmt := time.FixedZone("GMT", 0) + + return time.Now().In(gmt).Format("2006-01-02T15:04:05Z") +} diff --git a/vendor/github.com/alibabacloud-go/tea/utils/progress.go b/vendor/github.com/alibabacloud-go/tea/utils/progress.go new file mode 100644 index 000000000..2f5364aea --- /dev/null +++ b/vendor/github.com/alibabacloud-go/tea/utils/progress.go @@ -0,0 +1,60 @@ +package utils + +// ProgressEventType defines transfer progress event type +type ProgressEventType int + +const ( + // TransferStartedEvent transfer started, set TotalBytes + TransferStartedEvent ProgressEventType = 1 + iota + // TransferDataEvent transfer data, set ConsumedBytes anmd TotalBytes + TransferDataEvent + // TransferCompletedEvent transfer completed + TransferCompletedEvent + // TransferFailedEvent transfer encounters an error + TransferFailedEvent +) + +// ProgressEvent defines progress event +type ProgressEvent struct { + ConsumedBytes int64 + TotalBytes int64 + RwBytes int64 + EventType ProgressEventType +} + +// ProgressListener listens progress change +type ProgressListener interface { + ProgressChanged(event *ProgressEvent) +} + +// -------------------- Private -------------------- + +func NewProgressEvent(eventType ProgressEventType, consumed, total int64, rwBytes int64) *ProgressEvent { + return &ProgressEvent{ + ConsumedBytes: consumed, + TotalBytes: total, + RwBytes: rwBytes, + EventType: eventType} +} + +// publishProgress +func PublishProgress(listener ProgressListener, event *ProgressEvent) { + if listener != nil && event != nil { + listener.ProgressChanged(event) + } +} + +func GetProgressListener(obj interface{}) ProgressListener { + if obj == nil { + return nil + } + listener, ok := obj.(ProgressListener) + if !ok { + return nil + } + return listener +} + +type ReaderTracker struct { + CompletedBytes int64 +} diff --git a/vendor/github.com/aliyun/credentials-go/LICENSE b/vendor/github.com/aliyun/credentials-go/LICENSE new file mode 100644 index 000000000..0c44dcefe --- /dev/null +++ b/vendor/github.com/aliyun/credentials-go/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) 2009-present, Alibaba Cloud All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/aliyun/credentials-go/credentials/access_key_credential.go b/vendor/github.com/aliyun/credentials-go/credentials/access_key_credential.go new file mode 100644 index 000000000..7bcaa9740 --- /dev/null +++ b/vendor/github.com/aliyun/credentials-go/credentials/access_key_credential.go @@ -0,0 +1,41 @@ +package credentials + +import "github.com/alibabacloud-go/tea/tea" + +// AccessKeyCredential is a kind of credential +type AccessKeyCredential struct { + AccessKeyId string + AccessKeySecret string +} + +func newAccessKeyCredential(accessKeyId, accessKeySecret string) *AccessKeyCredential { + return &AccessKeyCredential{ + AccessKeyId: accessKeyId, + AccessKeySecret: accessKeySecret, + } +} + +// GetAccessKeyId reutrns AccessKeyCreential's AccessKeyId +func (a *AccessKeyCredential) GetAccessKeyId() (*string, error) { + return tea.String(a.AccessKeyId), nil +} + +// GetAccessSecret reutrns AccessKeyCreential's AccessKeySecret +func (a *AccessKeyCredential) GetAccessKeySecret() (*string, error) { + return tea.String(a.AccessKeySecret), nil +} + +// GetSecurityToken is useless for AccessKeyCreential +func (a *AccessKeyCredential) GetSecurityToken() (*string, error) { + return tea.String(""), nil +} + +// GetBearerToken is useless for AccessKeyCreential +func (a *AccessKeyCredential) GetBearerToken() *string { + return tea.String("") +} + +// GetType reutrns AccessKeyCreential's type +func (a *AccessKeyCredential) GetType() *string { + return tea.String("access_key") +} diff --git a/vendor/github.com/aliyun/credentials-go/credentials/bearer_token_credential.go b/vendor/github.com/aliyun/credentials-go/credentials/bearer_token_credential.go new file mode 100644 index 000000000..cca291621 --- /dev/null +++ b/vendor/github.com/aliyun/credentials-go/credentials/bearer_token_credential.go @@ -0,0 +1,40 @@ +package credentials + +import "github.com/alibabacloud-go/tea/tea" + +// BearerTokenCredential is a kind of credential +type BearerTokenCredential struct { + BearerToken string +} + +// newBearerTokenCredential return a BearerTokenCredential object +func newBearerTokenCredential(token string) *BearerTokenCredential { + return &BearerTokenCredential{ + BearerToken: token, + } +} + +// GetAccessKeyId is useless for BearerTokenCredential +func (b *BearerTokenCredential) GetAccessKeyId() (*string, error) { + return tea.String(""), nil +} + +// GetAccessSecret is useless for BearerTokenCredential +func (b *BearerTokenCredential) GetAccessKeySecret() (*string, error) { + return tea.String(("")), nil +} + +// GetSecurityToken is useless for BearerTokenCredential +func (b *BearerTokenCredential) GetSecurityToken() (*string, error) { + return tea.String(""), nil +} + +// GetBearerToken reutrns BearerTokenCredential's BearerToken +func (b *BearerTokenCredential) GetBearerToken() *string { + return tea.String(b.BearerToken) +} + +// GetType reutrns BearerTokenCredential's type +func (b *BearerTokenCredential) GetType() *string { + return tea.String("bearer") +} diff --git a/vendor/github.com/aliyun/credentials-go/credentials/credential.go b/vendor/github.com/aliyun/credentials-go/credentials/credential.go new file mode 100644 index 000000000..92212e132 --- /dev/null +++ b/vendor/github.com/aliyun/credentials-go/credentials/credential.go @@ -0,0 +1,349 @@ +package credentials + +import ( + "bufio" + "errors" + "fmt" + "net/http" + "net/url" + "os" + "strings" + "time" + + "github.com/alibabacloud-go/debug/debug" + "github.com/alibabacloud-go/tea/tea" + "github.com/aliyun/credentials-go/credentials/request" + "github.com/aliyun/credentials-go/credentials/response" + "github.com/aliyun/credentials-go/credentials/utils" +) + +var debuglog = debug.Init("credential") + +var hookParse = func(err error) error { + return err +} + +// Credential is an interface for getting actual credential +type Credential interface { + GetAccessKeyId() (*string, error) + GetAccessKeySecret() (*string, error) + GetSecurityToken() (*string, error) + GetBearerToken() *string + GetType() *string +} + +// Config is important when call NewCredential +type Config struct { + Type *string `json:"type"` + AccessKeyId *string `json:"access_key_id"` + AccessKeySecret *string `json:"access_key_secret"` + RoleArn *string `json:"role_arn"` + RoleSessionName *string `json:"role_session_name"` + PublicKeyId *string `json:"public_key_id"` + RoleName *string `json:"role_name"` + SessionExpiration *int `json:"session_expiration"` + PrivateKeyFile *string `json:"private_key_file"` + BearerToken *string `json:"bearer_token"` + SecurityToken *string `json:"security_token"` + RoleSessionExpiration *int `json:"role_session_expiratioon"` + Policy *string `json:"policy"` + Host *string `json:"host"` + Timeout *int `json:"timeout"` + ConnectTimeout *int `json:"connect_timeout"` + Proxy *string `json:"proxy"` +} + +func (s Config) String() string { + return tea.Prettify(s) +} + +func (s Config) GoString() string { + return s.String() +} + +func (s *Config) SetAccessKeyId(v string) *Config { + s.AccessKeyId = &v + return s +} + +func (s *Config) SetAccessKeySecret(v string) *Config { + s.AccessKeySecret = &v + return s +} + +func (s *Config) SetSecurityToken(v string) *Config { + s.SecurityToken = &v + return s +} + +func (s *Config) SetRoleArn(v string) *Config { + s.RoleArn = &v + return s +} + +func (s *Config) SetRoleSessionName(v string) *Config { + s.RoleSessionName = &v + return s +} + +func (s *Config) SetPublicKeyId(v string) *Config { + s.PublicKeyId = &v + return s +} + +func (s *Config) SetRoleName(v string) *Config { + s.RoleName = &v + return s +} + +func (s *Config) SetSessionExpiration(v int) *Config { + s.SessionExpiration = &v + return s +} + +func (s *Config) SetPrivateKeyFile(v string) *Config { + s.PrivateKeyFile = &v + return s +} + +func (s *Config) SetBearerToken(v string) *Config { + s.BearerToken = &v + return s +} + +func (s *Config) SetRoleSessionExpiration(v int) *Config { + s.RoleSessionExpiration = &v + return s +} + +func (s *Config) SetPolicy(v string) *Config { + s.Policy = &v + return s +} + +func (s *Config) SetHost(v string) *Config { + s.Host = &v + return s +} + +func (s *Config) SetTimeout(v int) *Config { + s.Timeout = &v + return s +} + +func (s *Config) SetConnectTimeout(v int) *Config { + s.ConnectTimeout = &v + return s +} + +func (s *Config) SetProxy(v string) *Config { + s.Proxy = &v + return s +} + +func (s *Config) SetType(v string) *Config { + s.Type = &v + return s +} + +// NewCredential return a credential according to the type in config. +// if config is nil, the function will use default provider chain to get credential. +// please see README.md for detail. +func NewCredential(config *Config) (credential Credential, err error) { + if config == nil { + config, err = defaultChain.resolve() + if err != nil { + return + } + return NewCredential(config) + } + switch tea.StringValue(config.Type) { + case "access_key": + err = checkAccessKey(config) + if err != nil { + return + } + credential = newAccessKeyCredential(tea.StringValue(config.AccessKeyId), tea.StringValue(config.AccessKeySecret)) + case "sts": + err = checkSTS(config) + if err != nil { + return + } + credential = newStsTokenCredential(tea.StringValue(config.AccessKeyId), tea.StringValue(config.AccessKeySecret), tea.StringValue(config.SecurityToken)) + case "ecs_ram_role": + checkEcsRAMRole(config) + runtime := &utils.Runtime{ + Host: tea.StringValue(config.Host), + Proxy: tea.StringValue(config.Proxy), + ReadTimeout: tea.IntValue(config.Timeout), + ConnectTimeout: tea.IntValue(config.ConnectTimeout), + } + credential = newEcsRAMRoleCredential(tea.StringValue(config.RoleName), runtime) + case "ram_role_arn": + err = checkRAMRoleArn(config) + if err != nil { + return + } + runtime := &utils.Runtime{ + Host: tea.StringValue(config.Host), + Proxy: tea.StringValue(config.Proxy), + ReadTimeout: tea.IntValue(config.Timeout), + ConnectTimeout: tea.IntValue(config.ConnectTimeout), + } + credential = newRAMRoleArnCredential(tea.StringValue(config.AccessKeyId), tea.StringValue(config.AccessKeySecret), tea.StringValue(config.RoleArn), tea.StringValue(config.RoleSessionName), tea.StringValue(config.Policy), tea.IntValue(config.RoleSessionExpiration), runtime) + case "rsa_key_pair": + err = checkRSAKeyPair(config) + if err != nil { + return + } + file, err1 := os.Open(tea.StringValue(config.PrivateKeyFile)) + if err1 != nil { + err = fmt.Errorf("InvalidPath: Can not open PrivateKeyFile, err is %s", err1.Error()) + return + } + defer file.Close() + var privateKey string + scan := bufio.NewScanner(file) + for scan.Scan() { + if strings.HasPrefix(scan.Text(), "----") { + continue + } + privateKey += scan.Text() + "\n" + } + runtime := &utils.Runtime{ + Host: tea.StringValue(config.Host), + Proxy: tea.StringValue(config.Proxy), + ReadTimeout: tea.IntValue(config.Timeout), + ConnectTimeout: tea.IntValue(config.ConnectTimeout), + } + credential = newRsaKeyPairCredential(privateKey, tea.StringValue(config.PublicKeyId), tea.IntValue(config.SessionExpiration), runtime) + case "bearer": + if tea.StringValue(config.BearerToken) == "" { + err = errors.New("BearerToken cannot be empty") + return + } + credential = newBearerTokenCredential(tea.StringValue(config.BearerToken)) + default: + err = errors.New("Invalid type option, support: access_key, sts, ecs_ram_role, ram_role_arn, rsa_key_pair") + return + } + return credential, nil +} + +func checkRSAKeyPair(config *Config) (err error) { + if tea.StringValue(config.PrivateKeyFile) == "" { + err = errors.New("PrivateKeyFile cannot be empty") + return + } + if tea.StringValue(config.PublicKeyId) == "" { + err = errors.New("PublicKeyId cannot be empty") + return + } + return +} + +func checkRAMRoleArn(config *Config) (err error) { + if tea.StringValue(config.AccessKeySecret) == "" { + err = errors.New("AccessKeySecret cannot be empty") + return + } + if tea.StringValue(config.RoleArn) == "" { + err = errors.New("RoleArn cannot be empty") + return + } + if tea.StringValue(config.RoleSessionName) == "" { + err = errors.New("RoleSessionName cannot be empty") + return + } + if tea.StringValue(config.AccessKeyId) == "" { + err = errors.New("AccessKeyId cannot be empty") + return + } + return +} + +func checkEcsRAMRole(config *Config) (err error) { + return +} + +func checkSTS(config *Config) (err error) { + if tea.StringValue(config.AccessKeyId) == "" { + err = errors.New("AccessKeyId cannot be empty") + return + } + if tea.StringValue(config.AccessKeySecret) == "" { + err = errors.New("AccessKeySecret cannot be empty") + return + } + if tea.StringValue(config.SecurityToken) == "" { + err = errors.New("SecurityToken cannot be empty") + return + } + return +} + +func checkAccessKey(config *Config) (err error) { + if tea.StringValue(config.AccessKeyId) == "" { + err = errors.New("AccessKeyId cannot be empty") + return + } + if tea.StringValue(config.AccessKeySecret) == "" { + err = errors.New("AccessKeySecret cannot be empty") + return + } + return +} + +func doAction(request *request.CommonRequest, runtime *utils.Runtime) (content []byte, err error) { + httpRequest, err := http.NewRequest(request.Method, request.URL, strings.NewReader("")) + if err != nil { + return + } + httpRequest.Proto = "HTTP/1.1" + httpRequest.Host = request.Domain + debuglog("> %s %s %s", httpRequest.Method, httpRequest.URL.RequestURI(), httpRequest.Proto) + debuglog("> Host: %s", httpRequest.Host) + for key, value := range request.Headers { + if value != "" { + debuglog("> %s: %s", key, value) + httpRequest.Header[key] = []string{value} + } + } + debuglog(">") + httpClient := &http.Client{} + httpClient.Timeout = time.Duration(runtime.ReadTimeout) * time.Second + proxy := &url.URL{} + if runtime.Proxy != "" { + proxy, err = url.Parse(runtime.Proxy) + if err != nil { + return + } + } + trans := &http.Transport{} + if proxy != nil && runtime.Proxy != "" { + trans.Proxy = http.ProxyURL(proxy) + } + trans.DialContext = utils.Timeout(time.Duration(runtime.ConnectTimeout) * time.Second) + httpClient.Transport = trans + httpResponse, err := hookDo(httpClient.Do)(httpRequest) + if err != nil { + return + } + debuglog("< %s %s", httpResponse.Proto, httpResponse.Status) + for key, value := range httpResponse.Header { + debuglog("< %s: %v", key, strings.Join(value, "")) + } + debuglog("<") + + resp := &response.CommonResponse{} + err = hookParse(resp.ParseFromHTTPResponse(httpResponse)) + if err != nil { + return + } + debuglog("%s", resp.GetHTTPContentString()) + if resp.GetHTTPStatus() != http.StatusOK { + err = fmt.Errorf("httpStatus: %d, message = %s", resp.GetHTTPStatus(), resp.GetHTTPContentString()) + return + } + return resp.GetHTTPContentBytes(), nil +} diff --git a/vendor/github.com/aliyun/credentials-go/credentials/credential_updater.go b/vendor/github.com/aliyun/credentials-go/credentials/credential_updater.go new file mode 100644 index 000000000..8d4433b7e --- /dev/null +++ b/vendor/github.com/aliyun/credentials-go/credentials/credential_updater.go @@ -0,0 +1,25 @@ +package credentials + +import ( + "net/http" + "time" +) + +const defaultInAdvanceScale = 0.95 + +var hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { + return fn +} + +type credentialUpdater struct { + credentialExpiration int + lastUpdateTimestamp int64 + inAdvanceScale float64 +} + +func (updater *credentialUpdater) needUpdateCredential() (result bool) { + if updater.inAdvanceScale == 0 { + updater.inAdvanceScale = defaultInAdvanceScale + } + return time.Now().Unix()-updater.lastUpdateTimestamp >= int64(float64(updater.credentialExpiration)*updater.inAdvanceScale) +} diff --git a/vendor/github.com/aliyun/credentials-go/credentials/ecs_ram_role.go b/vendor/github.com/aliyun/credentials-go/credentials/ecs_ram_role.go new file mode 100644 index 000000000..53abfe9da --- /dev/null +++ b/vendor/github.com/aliyun/credentials-go/credentials/ecs_ram_role.go @@ -0,0 +1,136 @@ +package credentials + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/alibabacloud-go/tea/tea" + "github.com/aliyun/credentials-go/credentials/request" + "github.com/aliyun/credentials-go/credentials/utils" +) + +var securityCredURL = "http://100.100.100.200/latest/meta-data/ram/security-credentials/" + +// EcsRAMRoleCredential is a kind of credential +type EcsRAMRoleCredential struct { + *credentialUpdater + RoleName string + sessionCredential *sessionCredential + runtime *utils.Runtime +} + +type ecsRAMRoleResponse struct { + Code string `json:"Code" xml:"Code"` + AccessKeyId string `json:"AccessKeyId" xml:"AccessKeyId"` + AccessKeySecret string `json:"AccessKeySecret" xml:"AccessKeySecret"` + SecurityToken string `json:"SecurityToken" xml:"SecurityToken"` + Expiration string `json:"Expiration" xml:"Expiration"` +} + +func newEcsRAMRoleCredential(roleName string, runtime *utils.Runtime) *EcsRAMRoleCredential { + return &EcsRAMRoleCredential{ + RoleName: roleName, + credentialUpdater: new(credentialUpdater), + runtime: runtime, + } +} + +// GetAccessKeyId reutrns EcsRAMRoleCredential's AccessKeyId +// if AccessKeyId is not exist or out of date, the function will update it. +func (e *EcsRAMRoleCredential) GetAccessKeyId() (*string, error) { + if e.sessionCredential == nil || e.needUpdateCredential() { + err := e.updateCredential() + if err != nil { + return tea.String(""), err + } + } + return tea.String(e.sessionCredential.AccessKeyId), nil +} + +// GetAccessSecret reutrns EcsRAMRoleCredential's AccessKeySecret +// if AccessKeySecret is not exist or out of date, the function will update it. +func (e *EcsRAMRoleCredential) GetAccessKeySecret() (*string, error) { + if e.sessionCredential == nil || e.needUpdateCredential() { + err := e.updateCredential() + if err != nil { + return tea.String(""), err + } + } + return tea.String(e.sessionCredential.AccessKeySecret), nil +} + +// GetSecurityToken reutrns EcsRAMRoleCredential's SecurityToken +// if SecurityToken is not exist or out of date, the function will update it. +func (e *EcsRAMRoleCredential) GetSecurityToken() (*string, error) { + if e.sessionCredential == nil || e.needUpdateCredential() { + err := e.updateCredential() + if err != nil { + return tea.String(""), err + } + } + return tea.String(e.sessionCredential.SecurityToken), nil +} + +// GetBearerToken is useless for EcsRAMRoleCredential +func (e *EcsRAMRoleCredential) GetBearerToken() *string { + return tea.String("") +} + +// GetType reutrns EcsRAMRoleCredential's type +func (e *EcsRAMRoleCredential) GetType() *string { + return tea.String("ecs_ram_role") +} + +func getRoleName() (string, error) { + runtime := utils.NewRuntime(1, 1, "", "") + request := request.NewCommonRequest() + request.URL = securityCredURL + request.Method = "GET" + content, err := doAction(request, runtime) + if err != nil { + return "", err + } + return string(content), nil +} + +func (e *EcsRAMRoleCredential) updateCredential() (err error) { + if e.runtime == nil { + e.runtime = new(utils.Runtime) + } + request := request.NewCommonRequest() + if e.RoleName == "" { + e.RoleName, err = getRoleName() + if err != nil { + return fmt.Errorf("refresh Ecs sts token err: %s", err.Error()) + } + } + request.URL = securityCredURL + e.RoleName + request.Method = "GET" + content, err := doAction(request, e.runtime) + if err != nil { + return fmt.Errorf("refresh Ecs sts token err: %s", err.Error()) + } + var resp *ecsRAMRoleResponse + err = json.Unmarshal(content, &resp) + if err != nil { + return fmt.Errorf("refresh Ecs sts token err: Json Unmarshal fail: %s", err.Error()) + } + if resp.Code != "Success" { + return fmt.Errorf("refresh Ecs sts token err: Code is not Success") + } + if resp.AccessKeyId == "" || resp.AccessKeySecret == "" || resp.SecurityToken == "" || resp.Expiration == "" { + return fmt.Errorf("refresh Ecs sts token err: AccessKeyId: %s, AccessKeySecret: %s, SecurityToken: %s, Expiration: %s", resp.AccessKeyId, resp.AccessKeySecret, resp.SecurityToken, resp.Expiration) + } + + expirationTime, err := time.Parse("2006-01-02T15:04:05Z", resp.Expiration) + e.lastUpdateTimestamp = time.Now().Unix() + e.credentialExpiration = int(expirationTime.Unix() - time.Now().Unix()) + e.sessionCredential = &sessionCredential{ + AccessKeyId: resp.AccessKeyId, + AccessKeySecret: resp.AccessKeySecret, + SecurityToken: resp.SecurityToken, + } + + return +} diff --git a/vendor/github.com/aliyun/credentials-go/credentials/env_provider.go b/vendor/github.com/aliyun/credentials-go/credentials/env_provider.go new file mode 100644 index 000000000..f3807bd2a --- /dev/null +++ b/vendor/github.com/aliyun/credentials-go/credentials/env_provider.go @@ -0,0 +1,43 @@ +package credentials + +import ( + "errors" + "os" + + "github.com/alibabacloud-go/tea/tea" +) + +type envProvider struct{} + +var providerEnv = new(envProvider) + +const ( + // EnvVarAccessKeyId is a name of ALIBABA_CLOUD_ACCESS_KEY_Id + EnvVarAccessKeyId = "ALIBABA_CLOUD_ACCESS_KEY_Id" + // EnvVarAccessKeySecret is a name of ALIBABA_CLOUD_ACCESS_KEY_SECRET + EnvVarAccessKeySecret = "ALIBABA_CLOUD_ACCESS_KEY_SECRET" +) + +func newEnvProvider() Provider { + return &envProvider{} +} + +func (p *envProvider) resolve() (*Config, error) { + accessKeyId, ok1 := os.LookupEnv(EnvVarAccessKeyId) + accessKeySecret, ok2 := os.LookupEnv(EnvVarAccessKeySecret) + if !ok1 || !ok2 { + return nil, nil + } + if accessKeyId == "" { + return nil, errors.New(EnvVarAccessKeyId + " cannot be empty") + } + if accessKeySecret == "" { + return nil, errors.New(EnvVarAccessKeySecret + " cannot be empty") + } + config := &Config{ + Type: tea.String("access_key"), + AccessKeyId: tea.String(accessKeyId), + AccessKeySecret: tea.String(accessKeySecret), + } + return config, nil +} diff --git a/vendor/github.com/aliyun/credentials-go/credentials/instance_provider.go b/vendor/github.com/aliyun/credentials-go/credentials/instance_provider.go new file mode 100644 index 000000000..7e2ea07bb --- /dev/null +++ b/vendor/github.com/aliyun/credentials-go/credentials/instance_provider.go @@ -0,0 +1,28 @@ +package credentials + +import ( + "os" + + "github.com/alibabacloud-go/tea/tea" +) + +type instanceCredentialsProvider struct{} + +var providerInstance = new(instanceCredentialsProvider) + +func newInstanceCredentialsProvider() Provider { + return &instanceCredentialsProvider{} +} + +func (p *instanceCredentialsProvider) resolve() (*Config, error) { + roleName, ok := os.LookupEnv(ENVEcsMetadata) + if !ok { + return nil, nil + } + + config := &Config{ + Type: tea.String("ecs_ram_role"), + RoleName: tea.String(roleName), + } + return config, nil +} diff --git a/vendor/github.com/aliyun/credentials-go/credentials/profile_provider.go b/vendor/github.com/aliyun/credentials-go/credentials/profile_provider.go new file mode 100644 index 000000000..d6292b035 --- /dev/null +++ b/vendor/github.com/aliyun/credentials-go/credentials/profile_provider.go @@ -0,0 +1,350 @@ +package credentials + +import ( + "errors" + "fmt" + "os" + "runtime" + "strings" + + "github.com/alibabacloud-go/tea/tea" + ini "gopkg.in/ini.v1" +) + +type profileProvider struct { + Profile string +} + +var providerProfile = newProfileProvider() + +var hookOS = func(goos string) string { + return goos +} + +var hookState = func(info os.FileInfo, err error) (os.FileInfo, error) { + return info, err +} + +// NewProfileProvider receive zero or more parameters, +// when length of name is 0, the value of field Profile will be "default", +// and when there are multiple inputs, the function will take the +// first one and discard the other values. +func newProfileProvider(name ...string) Provider { + p := new(profileProvider) + if len(name) == 0 { + p.Profile = "default" + } else { + p.Profile = name[0] + } + return p +} + +// resolve implements the Provider interface +// when credential type is rsa_key_pair, the content of private_key file +// must be able to be parsed directly into the required string +// that NewRsaKeyPairCredential function needed +func (p *profileProvider) resolve() (*Config, error) { + path, ok := os.LookupEnv(ENVCredentialFile) + if !ok { + path, err := checkDefaultPath() + if err != nil { + return nil, err + } + if path == "" { + return nil, nil + } + } else if path == "" { + return nil, errors.New(ENVCredentialFile + " cannot be empty") + } + + value, section, err := getType(path, p.Profile) + if err != nil { + return nil, err + } + switch value.String() { + case "access_key": + config, err := getAccessKey(section) + if err != nil { + return nil, err + } + return config, nil + case "sts": + config, err := getSTS(section) + if err != nil { + return nil, err + } + return config, nil + case "bearer": + config, err := getBearerToken(section) + if err != nil { + return nil, err + } + return config, nil + case "ecs_ram_role": + config, err := getEcsRAMRole(section) + if err != nil { + return nil, err + } + return config, nil + case "ram_role_arn": + config, err := getRAMRoleArn(section) + if err != nil { + return nil, err + } + return config, nil + case "rsa_key_pair": + config, err := getRSAKeyPair(section) + if err != nil { + return nil, err + } + return config, nil + default: + return nil, errors.New("Invalid type option, support: access_key, sts, ecs_ram_role, ram_role_arn, rsa_key_pair") + } +} + +func getRSAKeyPair(section *ini.Section) (*Config, error) { + publicKeyId, err := section.GetKey("public_key_id") + if err != nil { + return nil, errors.New("Missing required public_key_id option in profile for rsa_key_pair") + } + if publicKeyId.String() == "" { + return nil, errors.New("public_key_id cannot be empty") + } + privateKeyFile, err := section.GetKey("private_key_file") + if err != nil { + return nil, errors.New("Missing required private_key_file option in profile for rsa_key_pair") + } + if privateKeyFile.String() == "" { + return nil, errors.New("private_key_file cannot be empty") + } + sessionExpiration, _ := section.GetKey("session_expiration") + expiration := 0 + if sessionExpiration != nil { + expiration, err = sessionExpiration.Int() + if err != nil { + return nil, errors.New("session_expiration must be an int") + } + } + config := &Config{ + Type: tea.String("rsa_key_pair"), + PublicKeyId: tea.String(publicKeyId.String()), + PrivateKeyFile: tea.String(privateKeyFile.String()), + SessionExpiration: tea.Int(expiration), + } + err = setRuntimeToConfig(config, section) + if err != nil { + return nil, err + } + return config, nil +} + +func getRAMRoleArn(section *ini.Section) (*Config, error) { + accessKeyId, err := section.GetKey("access_key_id") + if err != nil { + return nil, errors.New("Missing required access_key_id option in profile for ram_role_arn") + } + if accessKeyId.String() == "" { + return nil, errors.New("access_key_id cannot be empty") + } + accessKeySecret, err := section.GetKey("access_key_secret") + if err != nil { + return nil, errors.New("Missing required access_key_secret option in profile for ram_role_arn") + } + if accessKeySecret.String() == "" { + return nil, errors.New("access_key_secret cannot be empty") + } + roleArn, err := section.GetKey("role_arn") + if err != nil { + return nil, errors.New("Missing required role_arn option in profile for ram_role_arn") + } + if roleArn.String() == "" { + return nil, errors.New("role_arn cannot be empty") + } + roleSessionName, err := section.GetKey("role_session_name") + if err != nil { + return nil, errors.New("Missing required role_session_name option in profile for ram_role_arn") + } + if roleSessionName.String() == "" { + return nil, errors.New("role_session_name cannot be empty") + } + roleSessionExpiration, _ := section.GetKey("role_session_expiration") + expiration := 0 + if roleSessionExpiration != nil { + expiration, err = roleSessionExpiration.Int() + if err != nil { + return nil, errors.New("role_session_expiration must be an int") + } + } + config := &Config{ + Type: tea.String("ram_role_arn"), + AccessKeyId: tea.String(accessKeyId.String()), + AccessKeySecret: tea.String(accessKeySecret.String()), + RoleArn: tea.String(roleArn.String()), + RoleSessionName: tea.String(roleSessionName.String()), + RoleSessionExpiration: tea.Int(expiration), + } + err = setRuntimeToConfig(config, section) + if err != nil { + return nil, err + } + return config, nil +} + +func getEcsRAMRole(section *ini.Section) (*Config, error) { + roleName, _ := section.GetKey("role_name") + config := &Config{ + Type: tea.String("ecs_ram_role"), + } + if roleName != nil { + config.RoleName = tea.String(roleName.String()) + } + err := setRuntimeToConfig(config, section) + if err != nil { + return nil, err + } + return config, nil +} + +func getBearerToken(section *ini.Section) (*Config, error) { + bearerToken, err := section.GetKey("bearer_token") + if err != nil { + return nil, errors.New("Missing required bearer_token option in profile for bearer") + } + if bearerToken.String() == "" { + return nil, errors.New("bearer_token cannot be empty") + } + config := &Config{ + Type: tea.String("bearer"), + BearerToken: tea.String(bearerToken.String()), + } + return config, nil +} + +func getSTS(section *ini.Section) (*Config, error) { + accesskeyid, err := section.GetKey("access_key_id") + if err != nil { + return nil, errors.New("Missing required access_key_id option in profile for sts") + } + if accesskeyid.String() == "" { + return nil, errors.New("access_key_id cannot be empty") + } + accessKeySecret, err := section.GetKey("access_key_secret") + if err != nil { + return nil, errors.New("Missing required access_key_secret option in profile for sts") + } + if accessKeySecret.String() == "" { + return nil, errors.New("access_key_secret cannot be empty") + } + securityToken, err := section.GetKey("security_token") + if err != nil { + return nil, errors.New("Missing required security_token option in profile for sts") + } + if securityToken.String() == "" { + return nil, errors.New("security_token cannot be empty") + } + config := &Config{ + Type: tea.String("sts"), + AccessKeyId: tea.String(accesskeyid.String()), + AccessKeySecret: tea.String(accessKeySecret.String()), + SecurityToken: tea.String(securityToken.String()), + } + return config, nil +} + +func getAccessKey(section *ini.Section) (*Config, error) { + accesskeyid, err := section.GetKey("access_key_id") + if err != nil { + return nil, errors.New("Missing required access_key_id option in profile for access_key") + } + if accesskeyid.String() == "" { + return nil, errors.New("access_key_id cannot be empty") + } + accessKeySecret, err := section.GetKey("access_key_secret") + if err != nil { + return nil, errors.New("Missing required access_key_secret option in profile for access_key") + } + if accessKeySecret.String() == "" { + return nil, errors.New("access_key_secret cannot be empty") + } + config := &Config{ + Type: tea.String("access_key"), + AccessKeyId: tea.String(accesskeyid.String()), + AccessKeySecret: tea.String(accessKeySecret.String()), + } + return config, nil +} + +func getType(path, profile string) (*ini.Key, *ini.Section, error) { + ini, err := ini.Load(path) + if err != nil { + return nil, nil, errors.New("ERROR: Can not open file " + err.Error()) + } + + section, err := ini.GetSection(profile) + if err != nil { + return nil, nil, errors.New("ERROR: Can not load section " + err.Error()) + } + + value, err := section.GetKey("type") + if err != nil { + return nil, nil, errors.New("Missing required type option " + err.Error()) + } + return value, section, nil +} + +func getHomePath() string { + if hookOS(runtime.GOOS) == "windows" { + path, ok := os.LookupEnv("USERPROFILE") + if !ok { + return "" + } + return path + } + path, ok := os.LookupEnv("HOME") + if !ok { + return "" + } + return path +} + +func checkDefaultPath() (path string, err error) { + path = getHomePath() + if path == "" { + return "", errors.New("The default credential file path is invalid") + } + path = strings.Replace("~/.alibabacloud/credentials", "~", path, 1) + _, err = hookState(os.Stat(path)) + if err != nil { + return "", nil + } + return path, nil +} + +func setRuntimeToConfig(config *Config, section *ini.Section) error { + rawTimeout, _ := section.GetKey("timeout") + rawConnectTimeout, _ := section.GetKey("connect_timeout") + rawProxy, _ := section.GetKey("proxy") + rawHost, _ := section.GetKey("host") + if rawProxy != nil { + config.Proxy = tea.String(rawProxy.String()) + } + if rawConnectTimeout != nil { + connectTimeout, err := rawConnectTimeout.Int() + if err != nil { + return fmt.Errorf("Please set connect_timeout with an int value") + } + config.ConnectTimeout = tea.Int(connectTimeout) + } + if rawTimeout != nil { + timeout, err := rawTimeout.Int() + if err != nil { + return fmt.Errorf("Please set timeout with an int value") + } + config.Timeout = tea.Int(timeout) + } + if rawHost != nil { + config.Host = tea.String(rawHost.String()) + } + return nil +} diff --git a/vendor/github.com/aliyun/credentials-go/credentials/provider.go b/vendor/github.com/aliyun/credentials-go/credentials/provider.go new file mode 100644 index 000000000..f9d4ae574 --- /dev/null +++ b/vendor/github.com/aliyun/credentials-go/credentials/provider.go @@ -0,0 +1,13 @@ +package credentials + +//Environmental virables that may be used by the provider +const ( + ENVCredentialFile = "ALIBABA_CLOUD_CREDENTIALS_FILE" + ENVEcsMetadata = "ALIBABA_CLOUD_ECS_METADATA" + PATHCredentialFile = "~/.alibabacloud/credentials" +) + +// Provider will be implemented When you want to customize the provider. +type Provider interface { + resolve() (*Config, error) +} diff --git a/vendor/github.com/aliyun/credentials-go/credentials/provider_chain.go b/vendor/github.com/aliyun/credentials-go/credentials/provider_chain.go new file mode 100644 index 000000000..2764a701c --- /dev/null +++ b/vendor/github.com/aliyun/credentials-go/credentials/provider_chain.go @@ -0,0 +1,32 @@ +package credentials + +import ( + "errors" +) + +type providerChain struct { + Providers []Provider +} + +var defaultproviders = []Provider{providerEnv, providerProfile, providerInstance} +var defaultChain = newProviderChain(defaultproviders) + +func newProviderChain(providers []Provider) Provider { + return &providerChain{ + Providers: providers, + } +} + +func (p *providerChain) resolve() (*Config, error) { + for _, provider := range p.Providers { + config, err := provider.resolve() + if err != nil { + return nil, err + } else if config == nil { + continue + } + return config, err + } + return nil, errors.New("No credential found") + +} diff --git a/vendor/github.com/aliyun/credentials-go/credentials/request/common_request.go b/vendor/github.com/aliyun/credentials-go/credentials/request/common_request.go new file mode 100644 index 000000000..68b9681b3 --- /dev/null +++ b/vendor/github.com/aliyun/credentials-go/credentials/request/common_request.go @@ -0,0 +1,59 @@ +package request + +import ( + "fmt" + "net/url" + "strings" + "time" + + "github.com/aliyun/credentials-go/credentials/utils" +) + +// CommonRequest is for requesting credential +type CommonRequest struct { + Scheme string + Method string + Domain string + RegionId string + URL string + ReadTimeout time.Duration + ConnectTimeout time.Duration + isInsecure *bool + + userAgent map[string]string + QueryParams map[string]string + Headers map[string]string + + queries string +} + +// NewCommonRequest returns a CommonRequest +func NewCommonRequest() *CommonRequest { + return &CommonRequest{ + QueryParams: make(map[string]string), + Headers: make(map[string]string), + } +} + +// BuildURL returns a url +func (request *CommonRequest) BuildURL() string { + url := fmt.Sprintf("%s://%s", strings.ToLower(request.Scheme), request.Domain) + request.queries = "/?" + utils.GetURLFormedMap(request.QueryParams) + return url + request.queries +} + +// BuildStringToSign returns BuildStringToSign +func (request *CommonRequest) BuildStringToSign() (stringToSign string) { + signParams := make(map[string]string) + for key, value := range request.QueryParams { + signParams[key] = value + } + + stringToSign = utils.GetURLFormedMap(signParams) + stringToSign = strings.Replace(stringToSign, "+", "%20", -1) + stringToSign = strings.Replace(stringToSign, "*", "%2A", -1) + stringToSign = strings.Replace(stringToSign, "%7E", "~", -1) + stringToSign = url.QueryEscape(stringToSign) + stringToSign = request.Method + "&%2F&" + stringToSign + return +} diff --git a/vendor/github.com/aliyun/credentials-go/credentials/response/common_response.go b/vendor/github.com/aliyun/credentials-go/credentials/response/common_response.go new file mode 100644 index 000000000..ef489c11d --- /dev/null +++ b/vendor/github.com/aliyun/credentials-go/credentials/response/common_response.go @@ -0,0 +1,53 @@ +package response + +import ( + "io" + "io/ioutil" + "net/http" +) + +var hookReadAll = func(fn func(r io.Reader) (b []byte, err error)) func(r io.Reader) (b []byte, err error) { + return fn +} + +// CommonResponse is for storing message of httpResponse +type CommonResponse struct { + httpStatus int + httpHeaders map[string][]string + httpContentString string + httpContentBytes []byte +} + +// ParseFromHTTPResponse assigns for CommonResponse, returns err when body is too large. +func (resp *CommonResponse) ParseFromHTTPResponse(httpResponse *http.Response) (err error) { + defer httpResponse.Body.Close() + body, err := hookReadAll(ioutil.ReadAll)(httpResponse.Body) + if err != nil { + return + } + resp.httpStatus = httpResponse.StatusCode + resp.httpHeaders = httpResponse.Header + resp.httpContentBytes = body + resp.httpContentString = string(body) + return +} + +// GetHTTPStatus returns httpStatus +func (resp *CommonResponse) GetHTTPStatus() int { + return resp.httpStatus +} + +// GetHTTPHeaders returns httpresponse's headers +func (resp *CommonResponse) GetHTTPHeaders() map[string][]string { + return resp.httpHeaders +} + +// GetHTTPContentString return body content as string +func (resp *CommonResponse) GetHTTPContentString() string { + return resp.httpContentString +} + +// GetHTTPContentBytes return body content as []byte +func (resp *CommonResponse) GetHTTPContentBytes() []byte { + return resp.httpContentBytes +} diff --git a/vendor/github.com/aliyun/credentials-go/credentials/rsa_key_pair_credential.go b/vendor/github.com/aliyun/credentials-go/credentials/rsa_key_pair_credential.go new file mode 100644 index 000000000..3e4310eca --- /dev/null +++ b/vendor/github.com/aliyun/credentials-go/credentials/rsa_key_pair_credential.go @@ -0,0 +1,145 @@ +package credentials + +import ( + "encoding/json" + "errors" + "fmt" + "strconv" + "time" + + "github.com/alibabacloud-go/tea/tea" + "github.com/aliyun/credentials-go/credentials/request" + "github.com/aliyun/credentials-go/credentials/utils" +) + +// RsaKeyPairCredential is a kind of credentials +type RsaKeyPairCredential struct { + *credentialUpdater + PrivateKey string + PublicKeyId string + SessionExpiration int + sessionCredential *sessionCredential + runtime *utils.Runtime +} + +type rsaKeyPairResponse struct { + SessionAccessKey *sessionAccessKey `json:"SessionAccessKey" xml:"SessionAccessKey"` +} + +type sessionAccessKey struct { + SessionAccessKeyId string `json:"SessionAccessKeyId" xml:"SessionAccessKeyId"` + SessionAccessKeySecret string `json:"SessionAccessKeySecret" xml:"SessionAccessKeySecret"` + Expiration string `json:"Expiration" xml:"Expiration"` +} + +func newRsaKeyPairCredential(privateKey, publicKeyId string, sessionExpiration int, runtime *utils.Runtime) *RsaKeyPairCredential { + return &RsaKeyPairCredential{ + PrivateKey: privateKey, + PublicKeyId: publicKeyId, + SessionExpiration: sessionExpiration, + credentialUpdater: new(credentialUpdater), + runtime: runtime, + } +} + +// GetAccessKeyId reutrns RsaKeyPairCredential's AccessKeyId +// if AccessKeyId is not exist or out of date, the function will update it. +func (r *RsaKeyPairCredential) GetAccessKeyId() (*string, error) { + if r.sessionCredential == nil || r.needUpdateCredential() { + err := r.updateCredential() + if err != nil { + return tea.String(""), err + } + } + return tea.String(r.sessionCredential.AccessKeyId), nil +} + +// GetAccessSecret reutrns RsaKeyPairCredential's AccessKeySecret +// if AccessKeySecret is not exist or out of date, the function will update it. +func (r *RsaKeyPairCredential) GetAccessKeySecret() (*string, error) { + if r.sessionCredential == nil || r.needUpdateCredential() { + err := r.updateCredential() + if err != nil { + return tea.String(""), err + } + } + return tea.String(r.sessionCredential.AccessKeySecret), nil +} + +// GetSecurityToken is useless RsaKeyPairCredential +func (r *RsaKeyPairCredential) GetSecurityToken() (*string, error) { + return tea.String(""), nil +} + +// GetBearerToken is useless for RsaKeyPairCredential +func (r *RsaKeyPairCredential) GetBearerToken() *string { + return tea.String("") +} + +// GetType reutrns RsaKeyPairCredential's type +func (r *RsaKeyPairCredential) GetType() *string { + return tea.String("rsa_key_pair") +} + +func (r *RsaKeyPairCredential) updateCredential() (err error) { + if r.runtime == nil { + r.runtime = new(utils.Runtime) + } + request := request.NewCommonRequest() + request.Domain = "sts.aliyuncs.com" + if r.runtime.Host != "" { + request.Domain = r.runtime.Host + } + request.Scheme = "HTTPS" + request.Method = "GET" + request.QueryParams["AccessKeyId"] = r.PublicKeyId + request.QueryParams["Action"] = "GenerateSessionAccessKey" + request.QueryParams["Format"] = "JSON" + if r.SessionExpiration > 0 { + if r.SessionExpiration >= 900 && r.SessionExpiration <= 3600 { + request.QueryParams["DurationSeconds"] = strconv.Itoa(r.SessionExpiration) + } else { + err = errors.New("[InvalidParam]:Key Pair session duration should be in the range of 15min - 1Hr") + return + } + } else { + request.QueryParams["DurationSeconds"] = strconv.Itoa(defaultDurationSeconds) + } + request.QueryParams["SignatureMethod"] = "SHA256withRSA" + request.QueryParams["SignatureType"] = "PRIVATEKEY" + request.QueryParams["SignatureVersion"] = "1.0" + request.QueryParams["Version"] = "2015-04-01" + request.QueryParams["Timestamp"] = utils.GetTimeInFormatISO8601() + request.QueryParams["SignatureNonce"] = utils.GetUUID() + signature := utils.Sha256WithRsa(request.BuildStringToSign(), r.PrivateKey) + request.QueryParams["Signature"] = signature + request.Headers["Host"] = request.Domain + request.Headers["Accept-Encoding"] = "identity" + request.URL = request.BuildURL() + content, err := doAction(request, r.runtime) + if err != nil { + return fmt.Errorf("refresh KeyPair err: %s", err.Error()) + } + var resp *rsaKeyPairResponse + err = json.Unmarshal(content, &resp) + if err != nil { + return fmt.Errorf("refresh KeyPair err: Json Unmarshal fail: %s", err.Error()) + } + if resp == nil || resp.SessionAccessKey == nil { + return fmt.Errorf("refresh KeyPair err: SessionAccessKey is empty") + } + sessionAccessKey := resp.SessionAccessKey + if sessionAccessKey.SessionAccessKeyId == "" || sessionAccessKey.SessionAccessKeySecret == "" || sessionAccessKey.Expiration == "" { + return fmt.Errorf("refresh KeyPair err: SessionAccessKeyId: %v, SessionAccessKeySecret: %v, Expiration: %v", sessionAccessKey.SessionAccessKeyId, sessionAccessKey.SessionAccessKeySecret, sessionAccessKey.Expiration) + } + + expirationTime, err := time.Parse("2006-01-02T15:04:05Z", sessionAccessKey.Expiration) + r.lastUpdateTimestamp = time.Now().Unix() + r.credentialExpiration = int(expirationTime.Unix() - time.Now().Unix()) + r.sessionCredential = &sessionCredential{ + AccessKeyId: sessionAccessKey.SessionAccessKeyId, + AccessKeySecret: sessionAccessKey.SessionAccessKeySecret, + } + + return +} diff --git a/vendor/github.com/aliyun/credentials-go/credentials/session_credential.go b/vendor/github.com/aliyun/credentials-go/credentials/session_credential.go new file mode 100644 index 000000000..dd48dc929 --- /dev/null +++ b/vendor/github.com/aliyun/credentials-go/credentials/session_credential.go @@ -0,0 +1,7 @@ +package credentials + +type sessionCredential struct { + AccessKeyId string + AccessKeySecret string + SecurityToken string +} diff --git a/vendor/github.com/aliyun/credentials-go/credentials/sts_credential.go b/vendor/github.com/aliyun/credentials-go/credentials/sts_credential.go new file mode 100644 index 000000000..ba07dab49 --- /dev/null +++ b/vendor/github.com/aliyun/credentials-go/credentials/sts_credential.go @@ -0,0 +1,43 @@ +package credentials + +import "github.com/alibabacloud-go/tea/tea" + +// StsTokenCredential is a kind of credentials +type StsTokenCredential struct { + AccessKeyId string + AccessKeySecret string + SecurityToken string +} + +func newStsTokenCredential(accessKeyId, accessKeySecret, securityToken string) *StsTokenCredential { + return &StsTokenCredential{ + AccessKeyId: accessKeyId, + AccessKeySecret: accessKeySecret, + SecurityToken: securityToken, + } +} + +// GetAccessKeyId reutrns StsTokenCredential's AccessKeyId +func (s *StsTokenCredential) GetAccessKeyId() (*string, error) { + return tea.String(s.AccessKeyId), nil +} + +// GetAccessSecret reutrns StsTokenCredential's AccessKeySecret +func (s *StsTokenCredential) GetAccessKeySecret() (*string, error) { + return tea.String(s.AccessKeySecret), nil +} + +// GetSecurityToken reutrns StsTokenCredential's SecurityToken +func (s *StsTokenCredential) GetSecurityToken() (*string, error) { + return tea.String(s.SecurityToken), nil +} + +// GetBearerToken is useless StsTokenCredential +func (s *StsTokenCredential) GetBearerToken() *string { + return tea.String("") +} + +// GetType reutrns StsTokenCredential's type +func (s *StsTokenCredential) GetType() *string { + return tea.String("sts") +} diff --git a/vendor/github.com/aliyun/credentials-go/credentials/sts_role_arn_credential.go b/vendor/github.com/aliyun/credentials-go/credentials/sts_role_arn_credential.go new file mode 100644 index 000000000..f31ba1e32 --- /dev/null +++ b/vendor/github.com/aliyun/credentials-go/credentials/sts_role_arn_credential.go @@ -0,0 +1,163 @@ +package credentials + +import ( + "encoding/json" + "errors" + "fmt" + "strconv" + "time" + + "github.com/alibabacloud-go/tea/tea" + "github.com/aliyun/credentials-go/credentials/request" + "github.com/aliyun/credentials-go/credentials/utils" +) + +const defaultDurationSeconds = 3600 + +// RAMRoleArnCredential is a kind of credentials +type RAMRoleArnCredential struct { + *credentialUpdater + AccessKeyId string + AccessKeySecret string + RoleArn string + RoleSessionName string + RoleSessionExpiration int + Policy string + sessionCredential *sessionCredential + runtime *utils.Runtime +} + +type ramRoleArnResponse struct { + Credentials *credentialsInResponse `json:"Credentials" xml:"Credentials"` +} + +type credentialsInResponse struct { + AccessKeyId string `json:"AccessKeyId" xml:"AccessKeyId"` + AccessKeySecret string `json:"AccessKeySecret" xml:"AccessKeySecret"` + SecurityToken string `json:"SecurityToken" xml:"SecurityToken"` + Expiration string `json:"Expiration" xml:"Expiration"` +} + +func newRAMRoleArnCredential(accessKeyId, accessKeySecret, roleArn, roleSessionName, policy string, roleSessionExpiration int, runtime *utils.Runtime) *RAMRoleArnCredential { + return &RAMRoleArnCredential{ + AccessKeyId: accessKeyId, + AccessKeySecret: accessKeySecret, + RoleArn: roleArn, + RoleSessionName: roleSessionName, + RoleSessionExpiration: roleSessionExpiration, + Policy: policy, + credentialUpdater: new(credentialUpdater), + runtime: runtime, + } +} + +// GetAccessKeyId reutrns RamRoleArnCredential's AccessKeyId +// if AccessKeyId is not exist or out of date, the function will update it. +func (r *RAMRoleArnCredential) GetAccessKeyId() (*string, error) { + if r.sessionCredential == nil || r.needUpdateCredential() { + err := r.updateCredential() + if err != nil { + return tea.String(""), err + } + } + return tea.String(r.sessionCredential.AccessKeyId), nil +} + +// GetAccessSecret reutrns RamRoleArnCredential's AccessKeySecret +// if AccessKeySecret is not exist or out of date, the function will update it. +func (r *RAMRoleArnCredential) GetAccessKeySecret() (*string, error) { + if r.sessionCredential == nil || r.needUpdateCredential() { + err := r.updateCredential() + if err != nil { + return tea.String(""), err + } + } + return tea.String(r.sessionCredential.AccessKeySecret), nil +} + +// GetSecurityToken reutrns RamRoleArnCredential's SecurityToken +// if SecurityToken is not exist or out of date, the function will update it. +func (r *RAMRoleArnCredential) GetSecurityToken() (*string, error) { + if r.sessionCredential == nil || r.needUpdateCredential() { + err := r.updateCredential() + if err != nil { + return tea.String(""), err + } + } + return tea.String(r.sessionCredential.SecurityToken), nil +} + +// GetBearerToken is useless RamRoleArnCredential +func (r *RAMRoleArnCredential) GetBearerToken() *string { + return tea.String("") +} + +// GetType reutrns RamRoleArnCredential's type +func (r *RAMRoleArnCredential) GetType() *string { + return tea.String("ram_role_arn") +} + +func (r *RAMRoleArnCredential) updateCredential() (err error) { + if r.runtime == nil { + r.runtime = new(utils.Runtime) + } + request := request.NewCommonRequest() + request.Domain = "sts.aliyuncs.com" + request.Scheme = "HTTPS" + request.Method = "GET" + request.QueryParams["AccessKeyId"] = r.AccessKeyId + request.QueryParams["Action"] = "AssumeRole" + request.QueryParams["Format"] = "JSON" + if r.RoleSessionExpiration > 0 { + if r.RoleSessionExpiration >= 900 && r.RoleSessionExpiration <= 3600 { + request.QueryParams["DurationSeconds"] = strconv.Itoa(r.RoleSessionExpiration) + } else { + err = errors.New("[InvalidParam]:Assume Role session duration should be in the range of 15min - 1Hr") + return + } + } else { + request.QueryParams["DurationSeconds"] = strconv.Itoa(defaultDurationSeconds) + } + request.QueryParams["RoleArn"] = r.RoleArn + if r.Policy != "" { + request.QueryParams["Policy"] = r.Policy + } + request.QueryParams["RoleSessionName"] = r.RoleSessionName + request.QueryParams["SignatureMethod"] = "HMAC-SHA1" + request.QueryParams["SignatureVersion"] = "1.0" + request.QueryParams["Version"] = "2015-04-01" + request.QueryParams["Timestamp"] = utils.GetTimeInFormatISO8601() + request.QueryParams["SignatureNonce"] = utils.GetUUID() + signature := utils.ShaHmac1(request.BuildStringToSign(), r.AccessKeySecret+"&") + request.QueryParams["Signature"] = signature + request.Headers["Host"] = request.Domain + request.Headers["Accept-Encoding"] = "identity" + request.URL = request.BuildURL() + content, err := doAction(request, r.runtime) + if err != nil { + return fmt.Errorf("refresh RoleArn sts token err: %s", err.Error()) + } + var resp *ramRoleArnResponse + err = json.Unmarshal(content, &resp) + if err != nil { + return fmt.Errorf("refresh RoleArn sts token err: Json.Unmarshal fail: %s", err.Error()) + } + if resp == nil || resp.Credentials == nil { + return fmt.Errorf("refresh RoleArn sts token err: Credentials is empty") + } + respCredentials := resp.Credentials + if respCredentials.AccessKeyId == "" || respCredentials.AccessKeySecret == "" || respCredentials.SecurityToken == "" || respCredentials.Expiration == "" { + return fmt.Errorf("refresh RoleArn sts token err: AccessKeyId: %s, AccessKeySecret: %s, SecurityToken: %s, Expiration: %s", respCredentials.AccessKeyId, respCredentials.AccessKeySecret, respCredentials.SecurityToken, respCredentials.Expiration) + } + + expirationTime, err := time.Parse("2006-01-02T15:04:05Z", respCredentials.Expiration) + r.lastUpdateTimestamp = time.Now().Unix() + r.credentialExpiration = int(expirationTime.Unix() - time.Now().Unix()) + r.sessionCredential = &sessionCredential{ + AccessKeyId: respCredentials.AccessKeyId, + AccessKeySecret: respCredentials.AccessKeySecret, + SecurityToken: respCredentials.SecurityToken, + } + + return +} diff --git a/vendor/github.com/aliyun/credentials-go/credentials/utils/runtime.go b/vendor/github.com/aliyun/credentials-go/credentials/utils/runtime.go new file mode 100644 index 000000000..d4a27c9cd --- /dev/null +++ b/vendor/github.com/aliyun/credentials-go/credentials/utils/runtime.go @@ -0,0 +1,35 @@ +package utils + +import ( + "context" + "net" + "time" +) + +// Runtime is for setting timeout, proxy and host +type Runtime struct { + ReadTimeout int + ConnectTimeout int + Proxy string + Host string +} + +// NewRuntime returns a Runtime +func NewRuntime(readTimeout, connectTimeout int, proxy string, host string) *Runtime { + return &Runtime{ + ReadTimeout: readTimeout, + ConnectTimeout: connectTimeout, + Proxy: proxy, + Host: host, + } +} + +// Timeout is for connect Timeout +func Timeout(connectTimeout time.Duration) func(cxt context.Context, net, addr string) (c net.Conn, err error) { + return func(ctx context.Context, network, address string) (net.Conn, error) { + return (&net.Dialer{ + Timeout: connectTimeout, + DualStack: true, + }).DialContext(ctx, network, address) + } +} diff --git a/vendor/github.com/aliyun/credentials-go/credentials/utils/utils.go b/vendor/github.com/aliyun/credentials-go/credentials/utils/utils.go new file mode 100644 index 000000000..7468407fb --- /dev/null +++ b/vendor/github.com/aliyun/credentials-go/credentials/utils/utils.go @@ -0,0 +1,146 @@ +package utils + +import ( + "crypto" + "crypto/hmac" + "crypto/md5" + "crypto/rand" + "crypto/rsa" + "crypto/sha1" + "crypto/x509" + "encoding/base64" + "encoding/hex" + "hash" + "io" + rand2 "math/rand" + "net/url" + "time" +) + +type uuid [16]byte + +const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + +var hookRead = func(fn func(p []byte) (n int, err error)) func(p []byte) (n int, err error) { + return fn +} + +var hookRSA = func(fn func(rand io.Reader, priv *rsa.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error)) func(rand io.Reader, priv *rsa.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) { + return fn +} + +// GetUUID returns a uuid +func GetUUID() (uuidHex string) { + uuid := newUUID() + uuidHex = hex.EncodeToString(uuid[:]) + return +} + +// RandStringBytes returns a rand string +func RandStringBytes(n int) string { + b := make([]byte, n) + for i := range b { + b[i] = letterBytes[rand2.Intn(len(letterBytes))] + } + return string(b) +} + +// ShaHmac1 return a string which has been hashed +func ShaHmac1(source, secret string) string { + key := []byte(secret) + hmac := hmac.New(sha1.New, key) + hmac.Write([]byte(source)) + signedBytes := hmac.Sum(nil) + signedString := base64.StdEncoding.EncodeToString(signedBytes) + return signedString +} + +// Sha256WithRsa return a string which has been hashed with Rsa +func Sha256WithRsa(source, secret string) string { + decodeString, err := base64.StdEncoding.DecodeString(secret) + if err != nil { + panic(err) + } + private, err := x509.ParsePKCS8PrivateKey(decodeString) + if err != nil { + panic(err) + } + + h := crypto.Hash.New(crypto.SHA256) + h.Write([]byte(source)) + hashed := h.Sum(nil) + signature, err := hookRSA(rsa.SignPKCS1v15)(rand.Reader, private.(*rsa.PrivateKey), + crypto.SHA256, hashed) + if err != nil { + panic(err) + } + + return base64.StdEncoding.EncodeToString(signature) +} + +// GetMD5Base64 returns a string which has been base64 +func GetMD5Base64(bytes []byte) (base64Value string) { + md5Ctx := md5.New() + md5Ctx.Write(bytes) + md5Value := md5Ctx.Sum(nil) + base64Value = base64.StdEncoding.EncodeToString(md5Value) + return +} + +// GetTimeInFormatISO8601 returns a time string +func GetTimeInFormatISO8601() (timeStr string) { + gmt := time.FixedZone("GMT", 0) + + return time.Now().In(gmt).Format("2006-01-02T15:04:05Z") +} + +// GetURLFormedMap returns a url encoded string +func GetURLFormedMap(source map[string]string) (urlEncoded string) { + urlEncoder := url.Values{} + for key, value := range source { + urlEncoder.Add(key, value) + } + urlEncoded = urlEncoder.Encode() + return +} + +func newUUID() uuid { + ns := uuid{} + safeRandom(ns[:]) + u := newFromHash(md5.New(), ns, RandStringBytes(16)) + u[6] = (u[6] & 0x0f) | (byte(2) << 4) + u[8] = (u[8]&(0xff>>2) | (0x02 << 6)) + + return u +} + +func newFromHash(h hash.Hash, ns uuid, name string) uuid { + u := uuid{} + h.Write(ns[:]) + h.Write([]byte(name)) + copy(u[:], h.Sum(nil)) + + return u +} + +func safeRandom(dest []byte) { + if _, err := hookRead(rand.Read)(dest); err != nil { + panic(err) + } +} + +func (u uuid) String() string { + buf := make([]byte, 36) + + hex.Encode(buf[0:8], u[0:4]) + buf[8] = '-' + hex.Encode(buf[9:13], u[4:6]) + buf[13] = '-' + hex.Encode(buf[14:18], u[6:8]) + buf[18] = '-' + hex.Encode(buf[19:23], u[8:10]) + buf[23] = '-' + hex.Encode(buf[24:], u[10:]) + + return string(buf) +} diff --git a/vendor/github.com/clbanning/mxj/v2/.travis.yml b/vendor/github.com/clbanning/mxj/v2/.travis.yml new file mode 100644 index 000000000..9c8611554 --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/.travis.yml @@ -0,0 +1,4 @@ +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 new file mode 100644 index 000000000..1ada8807d --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/LICENSE @@ -0,0 +1,22 @@ +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 new file mode 100644 index 000000000..63970ee24 --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/anyxml.go @@ -0,0 +1,201 @@ +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 + ">"), 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(""); 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 + ">"), 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(``); 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 new file mode 100644 index 000000000..474575a41 --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/atomFeedString.xml @@ -0,0 +1,54 @@ + +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 &lt;link rel=&quot;hub&quot; href=&quot;hub-server&quot;&gt; 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&#39;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&#39;s actual URL in +the link rel=&quot;self&quot;, 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&#39;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 new file mode 100644 index 000000000..bede31265 --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/doc.go @@ -0,0 +1,138 @@ +// 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: + 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 new file mode 100644 index 000000000..eeb3d2501 --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/escapechars.go @@ -0,0 +1,93 @@ +// 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 '&amp;'. +// +/* + 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 "&amp;" +// - or "<" that is parsed to "&lt;". +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 new file mode 100644 index 000000000..07aeda43f --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/exists.go @@ -0,0 +1,9 @@ +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 new file mode 100644 index 000000000..27e06e1e8 --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/files.go @@ -0,0 +1,287 @@ +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 new file mode 100644 index 000000000..d18720044 --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/files_test.badjson @@ -0,0 +1,2 @@ +{ "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 new file mode 100644 index 000000000..4736ef973 --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/files_test.badxml @@ -0,0 +1,9 @@ + + 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 new file mode 100644 index 000000000..e9a3ddf40 --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/files_test.json @@ -0,0 +1,2 @@ +{ "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 new file mode 100644 index 000000000..65cf021fb --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/files_test.xml @@ -0,0 +1,9 @@ + + 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 new file mode 100644 index 000000000..2becb6a45 --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/files_test_dup.json @@ -0,0 +1 @@ +{"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 new file mode 100644 index 000000000..f68d22e28 --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/files_test_dup.xml @@ -0,0 +1 @@ +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 new file mode 100644 index 000000000..6fde15634 --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/files_test_indent.json @@ -0,0 +1,12 @@ +{ + "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 new file mode 100644 index 000000000..8c91a1dc2 --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/files_test_indent.xml @@ -0,0 +1,8 @@ + + for files.go + test + + doc + test case + some + \ No newline at end of file diff --git a/vendor/github.com/clbanning/mxj/v2/go.mod b/vendor/github.com/clbanning/mxj/v2/go.mod new file mode 100644 index 000000000..7bd84fe62 --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/go.mod @@ -0,0 +1,3 @@ +module github.com/clbanning/mxj/v2 + +go 1.15 diff --git a/vendor/github.com/clbanning/mxj/v2/gob.go b/vendor/github.com/clbanning/mxj/v2/gob.go new file mode 100644 index 000000000..d56c2fd6f --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/gob.go @@ -0,0 +1,35 @@ +// 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 new file mode 100644 index 000000000..eb2c05a18 --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/json.go @@ -0,0 +1,323 @@ +// 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 new file mode 100644 index 000000000..55620ca22 --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/keyvalues.go @@ -0,0 +1,668 @@ +// 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 new file mode 100644 index 000000000..cf413ebdd --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/leafnode.go @@ -0,0 +1,112 @@ +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 != "#text" { + 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 new file mode 100644 index 000000000..5b4fab216 --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/misc.go @@ -0,0 +1,86 @@ +// 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 new file mode 100644 index 000000000..f0592f06c --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/mxj.go @@ -0,0 +1,128 @@ +// 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 new file mode 100644 index 000000000..b29394905 --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/newmap.go @@ -0,0 +1,184 @@ +// 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 new file mode 100644 index 000000000..323a747d4 --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/readme.md @@ -0,0 +1,207 @@ +

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.3.2
+
+ +
+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

+ + 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': +
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)
+ +

Extract / modify 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: +
paths := mv.PathsForKey(key)
+path := mv.PathForKeyShortest(key)
+values, err := mv.ValuesForKey(key, subkeys)
+values, err := mv.ValuesForPath(path, subkeys)
+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.) +
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 new file mode 100644 index 000000000..8362ab17f --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/remove.go @@ -0,0 +1,37 @@ +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 new file mode 100644 index 000000000..4c655ed5d --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/rename.go @@ -0,0 +1,61 @@ +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 new file mode 100644 index 000000000..a297fc388 --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/set.go @@ -0,0 +1,26 @@ +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 new file mode 100644 index 000000000..b70715ebc --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/setfieldsep.go @@ -0,0 +1,20 @@ +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 new file mode 100644 index 000000000..8c0f2becb --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/songtext.xml @@ -0,0 +1,29 @@ + + 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 new file mode 100644 index 000000000..1e769560b --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/strict.go @@ -0,0 +1,30 @@ +// 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 new file mode 100644 index 000000000..9be636cdc --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/struct.go @@ -0,0 +1,54 @@ +// 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 new file mode 100644 index 000000000..9e10d84e8 --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/updatevalues.go @@ -0,0 +1,258 @@ +// 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 new file mode 100644 index 000000000..9aa042339 --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/xml.go @@ -0,0 +1,1410 @@ +// 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" +) + +// ------------------- 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{}{"#text": 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["#text"] = 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["#text"] = cast(tt, r, "#text") + } 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 { + return b.b[0], nil + } + 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 { + 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(`"); 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["#text"]; 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 == "#text" { + // 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(`"); 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 new file mode 100644 index 000000000..80632bd3c --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/xmlseq.go @@ -0,0 +1,877 @@ +// 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" + "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. +func NewMapXmlSeq(xmlVal []byte, cast ...bool) (MapSeq, error) { + var r bool + if len(cast) == 1 { + r = cast[0] + } + return xmlSeqToMap(xmlVal, r) +} + +// 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{}{"#text": cast(v.Value, r, ""), "#seq": i} + } else { + aa[v.Name.Local] = map[string]interface{}{"#text": cast(v.Value, r, ""), "#seq": i} + } + } + na["#attr"] = 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{})["#seq"] = seq + seq++ + case interface{}: // a non-nil simple element: string, float64, bool + v := map[string]interface{}{"#text": val, "#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: + 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["#text"] = cast(tt, r, "") + na["#seq"] = seq + seq++ + } + case xml.Comment: + if n == nil { // no root 'key' + n = map[string]interface{}{"#comment": string(t.(xml.Comment))} + return n, NoRoot + } + cm := make(map[string]interface{}, 2) + cm["#text"] = string(t.(xml.Comment)) + cm["#seq"] = seq + seq++ + na["#comment"] = cm + case xml.Directive: + if n == nil { // no root 'key' + n = map[string]interface{}{"#directive": string(t.(xml.Directive))} + return n, NoRoot + } + dm := make(map[string]interface{}, 2) + dm["#text"] = string(t.(xml.Directive)) + dm["#seq"] = seq + seq++ + na["#directive"] = dm + case xml.ProcInst: + if n == nil { + na = map[string]interface{}{"#target": t.(xml.ProcInst).Target, "#inst": string(t.(xml.ProcInst).Inst)} + n = map[string]interface{}{"#procinst": na} + return n, NoRoot + } + pm := make(map[string]interface{}, 3) + pm["#target"] = t.(xml.ProcInst).Target + pm["#inst"] = string(t.(xml.ProcInst).Inst) + pm["#seq"] = seq + seq++ + na["#procinst"] = 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 != "#comment" && key != "#directive" && key != "#procinst" { + *s += `<` + key + } + } + switch value.(type) { + case map[string]interface{}: + val := value.(map[string]interface{}) + + if key == "#comment" { + *s += `` + noEndTag = true + break + } + + if key == "#directive" { + *s += `` + noEndTag = true + break + } + + if key == "#procinst" { + *s += `` + noEndTag = true + break + } + + haveAttrs := false + // process attributes first + if v, ok := val["#attr"].(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["#text"].(type) { + case string: + if xmlEscapeChars { + ss = escapeChars(vv["#text"].(string)) + } else { + ss = vv["#text"].(string) + } + *s += ` ` + a.k + `="` + ss + `"` + case float64, bool, int, int32, int64, float32: + *s += ` ` + a.k + `="` + fmt.Sprintf("%v", vv["#text"]) + `"` + case []byte: + if xmlEscapeChars { + ss = escapeChars(string(vv["#text"].([]byte))) + } else { + ss = string(vv["#text"].([]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["#seq"] // have key + if v, ok := val["#text"]; 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 == "#attr" { // already processed + continue + } + if k == "#seq" { // 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 += `" + } else { + *s += `/>` + } + } + } else if !noEndTag { + if useGoXmlEmptyElemSyntax { + *s += `" + // *s += ">" + } 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{})["#seq"].(int); !ok { + if fiseq, ok = e[i].v.(map[string]interface{})["#seq"].(float64); ok { + iseq = int(fiseq) + } else { + iseq = 9999999 + } + } + + if jseq, ok = e[j].v.(map[string]interface{})["#seq"].(int); !ok { + if fjseq, ok = e[j].v.(map[string]interface{})["#seq"].(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 new file mode 100644 index 000000000..467fd0769 --- /dev/null +++ b/vendor/github.com/clbanning/mxj/v2/xmlseq2.go @@ -0,0 +1,18 @@ +// 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/json-iterator/go/README.md b/vendor/github.com/json-iterator/go/README.md index 50d56ffbf..52b111d5f 100644 --- a/vendor/github.com/json-iterator/go/README.md +++ b/vendor/github.com/json-iterator/go/README.md @@ -1,5 +1,5 @@ [![Sourcegraph](https://sourcegraph.com/github.com/json-iterator/go/-/badge.svg)](https://sourcegraph.com/github.com/json-iterator/go?badge) -[![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/json-iterator/go) +[![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](https://pkg.go.dev/github.com/json-iterator/go) [![Build Status](https://travis-ci.org/json-iterator/go.svg?branch=master)](https://travis-ci.org/json-iterator/go) [![codecov](https://codecov.io/gh/json-iterator/go/branch/master/graph/badge.svg)](https://codecov.io/gh/json-iterator/go) [![rcard](https://goreportcard.com/badge/github.com/json-iterator/go)](https://goreportcard.com/report/github.com/json-iterator/go) @@ -18,16 +18,16 @@ Source code: https://github.com/json-iterator/go-benchmark/blob/master/src/githu Raw Result (easyjson requires static code generation) -| | ns/op | allocation bytes | allocation times | -| --- | --- | --- | --- | -| std decode | 35510 ns/op | 1960 B/op | 99 allocs/op | -| easyjson decode | 8499 ns/op | 160 B/op | 4 allocs/op | -| jsoniter decode | 5623 ns/op | 160 B/op | 3 allocs/op | -| std encode | 2213 ns/op | 712 B/op | 5 allocs/op | -| easyjson encode | 883 ns/op | 576 B/op | 3 allocs/op | -| jsoniter encode | 837 ns/op | 384 B/op | 4 allocs/op | +| | ns/op | allocation bytes | allocation times | +| --------------- | ----------- | ---------------- | ---------------- | +| std decode | 35510 ns/op | 1960 B/op | 99 allocs/op | +| easyjson decode | 8499 ns/op | 160 B/op | 4 allocs/op | +| jsoniter decode | 5623 ns/op | 160 B/op | 3 allocs/op | +| std encode | 2213 ns/op | 712 B/op | 5 allocs/op | +| easyjson encode | 883 ns/op | 576 B/op | 3 allocs/op | +| jsoniter encode | 837 ns/op | 384 B/op | 4 allocs/op | -Always benchmark with your own workload. +Always benchmark with your own workload. The result depends heavily on the data input. # Usage @@ -41,10 +41,10 @@ import "encoding/json" json.Marshal(&data) ``` -with +with ```go -import "github.com/json-iterator/go" +import jsoniter "github.com/json-iterator/go" var json = jsoniter.ConfigCompatibleWithStandardLibrary json.Marshal(&data) @@ -60,7 +60,7 @@ json.Unmarshal(input, &data) with ```go -import "github.com/json-iterator/go" +import jsoniter "github.com/json-iterator/go" var json = jsoniter.ConfigCompatibleWithStandardLibrary json.Unmarshal(input, &data) @@ -78,10 +78,10 @@ go get github.com/json-iterator/go Contributors -* [thockin](https://github.com/thockin) -* [mattn](https://github.com/mattn) -* [cch123](https://github.com/cch123) -* [Oleg Shaldybin](https://github.com/olegshaldybin) -* [Jason Toffaletti](https://github.com/toffaletti) +- [thockin](https://github.com/thockin) +- [mattn](https://github.com/mattn) +- [cch123](https://github.com/cch123) +- [Oleg Shaldybin](https://github.com/olegshaldybin) +- [Jason Toffaletti](https://github.com/toffaletti) Report issue or pull request, or email taowen@gmail.com, or [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/json-iterator/Lobby) diff --git a/vendor/github.com/json-iterator/go/any_str.go b/vendor/github.com/json-iterator/go/any_str.go index a4b93c78c..1f12f6612 100644 --- a/vendor/github.com/json-iterator/go/any_str.go +++ b/vendor/github.com/json-iterator/go/any_str.go @@ -64,7 +64,6 @@ func (any *stringAny) ToInt64() int64 { flag := 1 startPos := 0 - endPos := 0 if any.val[0] == '+' || any.val[0] == '-' { startPos = 1 } @@ -73,6 +72,7 @@ func (any *stringAny) ToInt64() int64 { flag = -1 } + endPos := startPos for i := startPos; i < len(any.val); i++ { if any.val[i] >= '0' && any.val[i] <= '9' { endPos = i + 1 @@ -98,7 +98,6 @@ func (any *stringAny) ToUint64() uint64 { } startPos := 0 - endPos := 0 if any.val[0] == '-' { return 0 @@ -107,6 +106,7 @@ func (any *stringAny) ToUint64() uint64 { startPos = 1 } + endPos := startPos for i := startPos; i < len(any.val); i++ { if any.val[i] >= '0' && any.val[i] <= '9' { endPos = i + 1 diff --git a/vendor/github.com/json-iterator/go/config.go b/vendor/github.com/json-iterator/go/config.go index 8c58fcba5..2adcdc3b7 100644 --- a/vendor/github.com/json-iterator/go/config.go +++ b/vendor/github.com/json-iterator/go/config.go @@ -183,11 +183,11 @@ func (cfg *frozenConfig) validateJsonRawMessage(extension EncoderExtension) { encoder := &funcEncoder{func(ptr unsafe.Pointer, stream *Stream) { rawMessage := *(*json.RawMessage)(ptr) iter := cfg.BorrowIterator([]byte(rawMessage)) + defer cfg.ReturnIterator(iter) iter.Read() - if iter.Error != nil { + if iter.Error != nil && iter.Error != io.EOF { stream.WriteRaw("null") } else { - cfg.ReturnIterator(iter) stream.WriteRaw(string(rawMessage)) } }, func(ptr unsafe.Pointer) bool { diff --git a/vendor/github.com/json-iterator/go/iter_object.go b/vendor/github.com/json-iterator/go/iter_object.go index b65137114..58ee89c84 100644 --- a/vendor/github.com/json-iterator/go/iter_object.go +++ b/vendor/github.com/json-iterator/go/iter_object.go @@ -150,7 +150,7 @@ func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool { if c == '}' { return iter.decrementDepth() } - iter.ReportError("ReadObjectCB", `expect " after }, but found `+string([]byte{c})) + iter.ReportError("ReadObjectCB", `expect " after {, but found `+string([]byte{c})) iter.decrementDepth() return false } @@ -206,7 +206,7 @@ func (iter *Iterator) ReadMapCB(callback func(*Iterator, string) bool) bool { if c == '}' { return iter.decrementDepth() } - iter.ReportError("ReadMapCB", `expect " after }, but found `+string([]byte{c})) + iter.ReportError("ReadMapCB", `expect " after {, but found `+string([]byte{c})) iter.decrementDepth() return false } diff --git a/vendor/github.com/json-iterator/go/reflect_extension.go b/vendor/github.com/json-iterator/go/reflect_extension.go index 80320cd64..74a97bfe5 100644 --- a/vendor/github.com/json-iterator/go/reflect_extension.go +++ b/vendor/github.com/json-iterator/go/reflect_extension.go @@ -475,7 +475,7 @@ func calcFieldNames(originalFieldName string, tagProvidedFieldName string, whole fieldNames = []string{tagProvidedFieldName} } // private? - isNotExported := unicode.IsLower(rune(originalFieldName[0])) + isNotExported := unicode.IsLower(rune(originalFieldName[0])) || originalFieldName[0] == '_' if isNotExported { fieldNames = []string{} } diff --git a/vendor/github.com/json-iterator/go/reflect_map.go b/vendor/github.com/json-iterator/go/reflect_map.go index 9e2b623fe..582967130 100644 --- a/vendor/github.com/json-iterator/go/reflect_map.go +++ b/vendor/github.com/json-iterator/go/reflect_map.go @@ -49,6 +49,33 @@ func decoderOfMapKey(ctx *ctx, typ reflect2.Type) ValDecoder { return decoder } } + + ptrType := reflect2.PtrTo(typ) + if ptrType.Implements(unmarshalerType) { + return &referenceDecoder{ + &unmarshalerDecoder{ + valType: ptrType, + }, + } + } + if typ.Implements(unmarshalerType) { + return &unmarshalerDecoder{ + valType: typ, + } + } + if ptrType.Implements(textUnmarshalerType) { + return &referenceDecoder{ + &textUnmarshalerDecoder{ + valType: ptrType, + }, + } + } + if typ.Implements(textUnmarshalerType) { + return &textUnmarshalerDecoder{ + valType: typ, + } + } + switch typ.Kind() { case reflect.String: return decoderOfType(ctx, reflect2.DefaultTypeOfKind(reflect.String)) @@ -63,31 +90,6 @@ func decoderOfMapKey(ctx *ctx, typ reflect2.Type) ValDecoder { typ = reflect2.DefaultTypeOfKind(typ.Kind()) return &numericMapKeyDecoder{decoderOfType(ctx, typ)} default: - ptrType := reflect2.PtrTo(typ) - if ptrType.Implements(unmarshalerType) { - return &referenceDecoder{ - &unmarshalerDecoder{ - valType: ptrType, - }, - } - } - if typ.Implements(unmarshalerType) { - return &unmarshalerDecoder{ - valType: typ, - } - } - if ptrType.Implements(textUnmarshalerType) { - return &referenceDecoder{ - &textUnmarshalerDecoder{ - valType: ptrType, - }, - } - } - if typ.Implements(textUnmarshalerType) { - return &textUnmarshalerDecoder{ - valType: typ, - } - } return &lazyErrorDecoder{err: fmt.Errorf("unsupported map key type: %v", typ)} } } @@ -103,6 +105,19 @@ func encoderOfMapKey(ctx *ctx, typ reflect2.Type) ValEncoder { return encoder } } + + if typ == textMarshalerType { + return &directTextMarshalerEncoder{ + stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")), + } + } + if typ.Implements(textMarshalerType) { + return &textMarshalerEncoder{ + valType: typ, + stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")), + } + } + switch typ.Kind() { case reflect.String: return encoderOfType(ctx, reflect2.DefaultTypeOfKind(reflect.String)) @@ -117,17 +132,6 @@ func encoderOfMapKey(ctx *ctx, typ reflect2.Type) ValEncoder { typ = reflect2.DefaultTypeOfKind(typ.Kind()) return &numericMapKeyEncoder{encoderOfType(ctx, typ)} default: - if typ == textMarshalerType { - return &directTextMarshalerEncoder{ - stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")), - } - } - if typ.Implements(textMarshalerType) { - return &textMarshalerEncoder{ - valType: typ, - stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")), - } - } if typ.Kind() == reflect.Interface { return &dynamicMapKeyEncoder{ctx, typ} } @@ -163,10 +167,6 @@ func (decoder *mapDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { if c == '}' { return } - if c != '"' { - iter.ReportError("ReadMapCB", `expect " after }, but found `+string([]byte{c})) - return - } iter.unreadByte() key := decoder.keyType.UnsafeNew() decoder.keyDecoder.Decode(key, iter) diff --git a/vendor/github.com/json-iterator/go/reflect_optional.go b/vendor/github.com/json-iterator/go/reflect_optional.go index 43ec71d6d..fa71f4748 100644 --- a/vendor/github.com/json-iterator/go/reflect_optional.go +++ b/vendor/github.com/json-iterator/go/reflect_optional.go @@ -2,7 +2,6 @@ package jsoniter import ( "github.com/modern-go/reflect2" - "reflect" "unsafe" ) @@ -10,9 +9,6 @@ func decoderOfOptional(ctx *ctx, typ reflect2.Type) ValDecoder { ptrType := typ.(*reflect2.UnsafePtrType) elemType := ptrType.Elem() decoder := decoderOfType(ctx, elemType) - if ctx.prefix == "" && elemType.Kind() == reflect.Ptr { - return &dereferenceDecoder{elemType, decoder} - } return &OptionalDecoder{elemType, decoder} } diff --git a/vendor/github.com/json-iterator/go/reflect_struct_decoder.go b/vendor/github.com/json-iterator/go/reflect_struct_decoder.go index 5ad5cc561..d7eb0eb5c 100644 --- a/vendor/github.com/json-iterator/go/reflect_struct_decoder.go +++ b/vendor/github.com/json-iterator/go/reflect_struct_decoder.go @@ -507,7 +507,7 @@ func (decoder *generalStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) for c = ','; c == ','; c = iter.nextToken() { decoder.decodeOneField(ptr, iter) } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } if c != '}' { @@ -588,7 +588,7 @@ func (decoder *oneFieldStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } iter.decrementDepth() @@ -622,7 +622,7 @@ func (decoder *twoFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } iter.decrementDepth() @@ -660,7 +660,7 @@ func (decoder *threeFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterat break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } iter.decrementDepth() @@ -702,7 +702,7 @@ func (decoder *fourFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterato break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } iter.decrementDepth() @@ -748,7 +748,7 @@ func (decoder *fiveFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterato break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } iter.decrementDepth() @@ -798,7 +798,7 @@ func (decoder *sixFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } iter.decrementDepth() @@ -852,7 +852,7 @@ func (decoder *sevenFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterat break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } iter.decrementDepth() @@ -910,7 +910,7 @@ func (decoder *eightFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterat break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } iter.decrementDepth() @@ -972,7 +972,7 @@ func (decoder *nineFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterato break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } iter.decrementDepth() @@ -1038,7 +1038,7 @@ func (decoder *tenFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } iter.decrementDepth() diff --git a/vendor/github.com/json-iterator/go/stream.go b/vendor/github.com/json-iterator/go/stream.go index 17662fded..23d8a3ad6 100644 --- a/vendor/github.com/json-iterator/go/stream.go +++ b/vendor/github.com/json-iterator/go/stream.go @@ -103,14 +103,14 @@ func (stream *Stream) Flush() error { if stream.Error != nil { return stream.Error } - n, err := stream.out.Write(stream.buf) + _, err := stream.out.Write(stream.buf) if err != nil { if stream.Error == nil { stream.Error = err } return err } - stream.buf = stream.buf[n:] + stream.buf = stream.buf[:0] return nil } @@ -177,7 +177,6 @@ func (stream *Stream) WriteEmptyObject() { func (stream *Stream) WriteMore() { stream.writeByte(',') stream.writeIndention(0) - stream.Flush() } // WriteArrayStart write [ with possible indention diff --git a/vendor/github.com/tjfoc/gmsm/LICENSE b/vendor/github.com/tjfoc/gmsm/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/vendor/github.com/tjfoc/gmsm/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/tjfoc/gmsm/sm3/sm3.go b/vendor/github.com/tjfoc/gmsm/sm3/sm3.go new file mode 100644 index 000000000..1a610b993 --- /dev/null +++ b/vendor/github.com/tjfoc/gmsm/sm3/sm3.go @@ -0,0 +1,260 @@ +/* +Copyright Suzhou Tongji Fintech Research Institute 2017 All Rights Reserved. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package sm3 + +import ( + "encoding/binary" + "hash" +) + +type SM3 struct { + digest [8]uint32 // digest represents the partial evaluation of V + length uint64 // length of the message + unhandleMsg []byte // uint8 // +} + +func (sm3 *SM3) ff0(x, y, z uint32) uint32 { return x ^ y ^ z } + +func (sm3 *SM3) ff1(x, y, z uint32) uint32 { return (x & y) | (x & z) | (y & z) } + +func (sm3 *SM3) gg0(x, y, z uint32) uint32 { return x ^ y ^ z } + +func (sm3 *SM3) gg1(x, y, z uint32) uint32 { return (x & y) | (^x & z) } + +func (sm3 *SM3) p0(x uint32) uint32 { return x ^ sm3.leftRotate(x, 9) ^ sm3.leftRotate(x, 17) } + +func (sm3 *SM3) p1(x uint32) uint32 { return x ^ sm3.leftRotate(x, 15) ^ sm3.leftRotate(x, 23) } + +func (sm3 *SM3) leftRotate(x uint32, i uint32) uint32 { return (x<<(i%32) | x>>(32-i%32)) } + +func (sm3 *SM3) pad() []byte { + msg := sm3.unhandleMsg + msg = append(msg, 0x80) // Append '1' + blockSize := 64 // Append until the resulting message length (in bits) is congruent to 448 (mod 512) + for len(msg)%blockSize != 56 { + msg = append(msg, 0x00) + } + // append message length + msg = append(msg, uint8(sm3.length>>56&0xff)) + msg = append(msg, uint8(sm3.length>>48&0xff)) + msg = append(msg, uint8(sm3.length>>40&0xff)) + msg = append(msg, uint8(sm3.length>>32&0xff)) + msg = append(msg, uint8(sm3.length>>24&0xff)) + msg = append(msg, uint8(sm3.length>>16&0xff)) + msg = append(msg, uint8(sm3.length>>8&0xff)) + msg = append(msg, uint8(sm3.length>>0&0xff)) + + if len(msg)%64 != 0 { + panic("------SM3 Pad: error msgLen =") + } + return msg +} + +func (sm3 *SM3) update(msg []byte, nblocks int) { + var w [68]uint32 + var w1 [64]uint32 + + a, b, c, d, e, f, g, h := sm3.digest[0], sm3.digest[1], sm3.digest[2], sm3.digest[3], sm3.digest[4], sm3.digest[5], sm3.digest[6], sm3.digest[7] + for len(msg) >= 64 { + for i := 0; i < 16; i++ { + w[i] = binary.BigEndian.Uint32(msg[4*i : 4*(i+1)]) + } + for i := 16; i < 68; i++ { + w[i] = sm3.p1(w[i-16]^w[i-9]^sm3.leftRotate(w[i-3], 15)) ^ sm3.leftRotate(w[i-13], 7) ^ w[i-6] + } + for i := 0; i < 64; i++ { + w1[i] = w[i] ^ w[i+4] + } + A, B, C, D, E, F, G, H := a, b, c, d, e, f, g, h + for i := 0; i < 16; i++ { + SS1 := sm3.leftRotate(sm3.leftRotate(A, 12)+E+sm3.leftRotate(0x79cc4519, uint32(i)), 7) + SS2 := SS1 ^ sm3.leftRotate(A, 12) + TT1 := sm3.ff0(A, B, C) + D + SS2 + w1[i] + TT2 := sm3.gg0(E, F, G) + H + SS1 + w[i] + D = C + C = sm3.leftRotate(B, 9) + B = A + A = TT1 + H = G + G = sm3.leftRotate(F, 19) + F = E + E = sm3.p0(TT2) + } + for i := 16; i < 64; i++ { + SS1 := sm3.leftRotate(sm3.leftRotate(A, 12)+E+sm3.leftRotate(0x7a879d8a, uint32(i)), 7) + SS2 := SS1 ^ sm3.leftRotate(A, 12) + TT1 := sm3.ff1(A, B, C) + D + SS2 + w1[i] + TT2 := sm3.gg1(E, F, G) + H + SS1 + w[i] + D = C + C = sm3.leftRotate(B, 9) + B = A + A = TT1 + H = G + G = sm3.leftRotate(F, 19) + F = E + E = sm3.p0(TT2) + } + a ^= A + b ^= B + c ^= C + d ^= D + e ^= E + f ^= F + g ^= G + h ^= H + msg = msg[64:] + } + sm3.digest[0], sm3.digest[1], sm3.digest[2], sm3.digest[3], sm3.digest[4], sm3.digest[5], sm3.digest[6], sm3.digest[7] = a, b, c, d, e, f, g, h +} +func (sm3 *SM3) update2(msg []byte, nblocks int)([8]uint32){ + var w [68]uint32 + var w1 [64]uint32 + + a, b, c, d, e, f, g, h := sm3.digest[0], sm3.digest[1], sm3.digest[2], sm3.digest[3], sm3.digest[4], sm3.digest[5], sm3.digest[6], sm3.digest[7] + for len(msg) >= 64 { + for i := 0; i < 16; i++ { + w[i] = binary.BigEndian.Uint32(msg[4*i : 4*(i+1)]) + } + for i := 16; i < 68; i++ { + w[i] = sm3.p1(w[i-16]^w[i-9]^sm3.leftRotate(w[i-3], 15)) ^ sm3.leftRotate(w[i-13], 7) ^ w[i-6] + } + for i := 0; i < 64; i++ { + w1[i] = w[i] ^ w[i+4] + } + A, B, C, D, E, F, G, H := a, b, c, d, e, f, g, h + for i := 0; i < 16; i++ { + SS1 := sm3.leftRotate(sm3.leftRotate(A, 12)+E+sm3.leftRotate(0x79cc4519, uint32(i)), 7) + SS2 := SS1 ^ sm3.leftRotate(A, 12) + TT1 := sm3.ff0(A, B, C) + D + SS2 + w1[i] + TT2 := sm3.gg0(E, F, G) + H + SS1 + w[i] + D = C + C = sm3.leftRotate(B, 9) + B = A + A = TT1 + H = G + G = sm3.leftRotate(F, 19) + F = E + E = sm3.p0(TT2) + } + for i := 16; i < 64; i++ { + SS1 := sm3.leftRotate(sm3.leftRotate(A, 12)+E+sm3.leftRotate(0x7a879d8a, uint32(i)), 7) + SS2 := SS1 ^ sm3.leftRotate(A, 12) + TT1 := sm3.ff1(A, B, C) + D + SS2 + w1[i] + TT2 := sm3.gg1(E, F, G) + H + SS1 + w[i] + D = C + C = sm3.leftRotate(B, 9) + B = A + A = TT1 + H = G + G = sm3.leftRotate(F, 19) + F = E + E = sm3.p0(TT2) + } + a ^= A + b ^= B + c ^= C + d ^= D + e ^= E + f ^= F + g ^= G + h ^= H + msg = msg[64:] + } + var digest [8]uint32 + digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7] = a, b, c, d, e, f, g, h + return digest +} +func New() hash.Hash { + var sm3 SM3 + + sm3.Reset() + return &sm3 +} + +// BlockSize, required by the hash.Hash interface. +// BlockSize returns the hash's underlying block size. +// The Write method must be able to accept any amount +// of data, but it may operate more efficiently if all writes +// are a multiple of the block size. +func (sm3 *SM3) BlockSize() int { return 64 } + +// Size, required by the hash.Hash interface. +// Size returns the number of bytes Sum will return. +func (sm3 *SM3) Size() int { return 32 } + +// Reset clears the internal state by zeroing bytes in the state buffer. +// This can be skipped for a newly-created hash state; the default zero-allocated state is correct. +func (sm3 *SM3) Reset() { + // Reset digest + sm3.digest[0] = 0x7380166f + sm3.digest[1] = 0x4914b2b9 + sm3.digest[2] = 0x172442d7 + sm3.digest[3] = 0xda8a0600 + sm3.digest[4] = 0xa96f30bc + sm3.digest[5] = 0x163138aa + sm3.digest[6] = 0xe38dee4d + sm3.digest[7] = 0xb0fb0e4e + + sm3.length = 0 // Reset numberic states + sm3.unhandleMsg = []byte{} +} + +// Write, required by the hash.Hash interface. +// Write (via the embedded io.Writer interface) adds more data to the running hash. +// It never returns an error. +func (sm3 *SM3) Write(p []byte) (int, error) { + toWrite := len(p) + sm3.length += uint64(len(p) * 8) + msg := append(sm3.unhandleMsg, p...) + nblocks := len(msg) / sm3.BlockSize() + sm3.update(msg, nblocks) + // Update unhandleMsg + sm3.unhandleMsg = msg[nblocks*sm3.BlockSize():] + + return toWrite, nil +} + +// Sum, required by the hash.Hash interface. +// Sum appends the current hash to b and returns the resulting slice. +// It does not change the underlying hash state. +func (sm3 *SM3) Sum(in []byte) []byte { + sm3.Write(in) + msg := sm3.pad() + //Finialize + digest:=sm3.update2(msg, len(msg)/sm3.BlockSize()) + + // save hash to in + needed := sm3.Size() + if cap(in)-len(in) < needed { + newIn := make([]byte, len(in), len(in)+needed) + copy(newIn, in) + in = newIn + } + out := in[len(in) : len(in)+needed] + for i := 0; i < 8; i++ { + binary.BigEndian.PutUint32(out[i*4:], digest[i]) + } + return out + +} + +func Sm3Sum(data []byte) []byte { + var sm3 SM3 + + sm3.Reset() + sm3.Write(data) + return sm3.Sum(nil) +} diff --git a/vendor/github.com/yuin/goldmark/extension/typographer.go b/vendor/github.com/yuin/goldmark/extension/typographer.go index fc4040209..c3b975109 100644 --- a/vendor/github.com/yuin/goldmark/extension/typographer.go +++ b/vendor/github.com/yuin/goldmark/extension/typographer.go @@ -197,7 +197,10 @@ func (s *typographerParser) Parse(parent gast.Node, block text.Reader, pc parser if s.Substitutions[Apostrophe] != nil { // Handle decade abbrevations such as '90s if d.CanOpen && !d.CanClose && len(line) > 3 && util.IsNumeric(line[1]) && util.IsNumeric(line[2]) && line[3] == 's' { - after := util.ToRune(line, 4) + after := rune(' ') + if len(line) > 4 { + after = util.ToRune(line, 4) + } if len(line) == 3 || unicode.IsSpace(after) || unicode.IsPunct(after) { node := gast.NewString(s.Substitutions[Apostrophe]) node.SetCode(true) @@ -207,7 +210,7 @@ func (s *typographerParser) Parse(parent gast.Node, block text.Reader, pc parser } // Convert normal apostrophes. This is probably more flexible than necessary but // converts any apostrophe in between two alphanumerics. - if len(line) > 1 && (unicode.IsDigit(before) || unicode.IsLetter(before)) && (util.IsAlphaNumeric(line[1])) { + if len(line) > 1 && (unicode.IsDigit(before) || unicode.IsLetter(before)) && (unicode.IsLetter(util.ToRune(line, 1))) { node := gast.NewString(s.Substitutions[Apostrophe]) node.SetCode(true) block.Advance(1) diff --git a/vendor/github.com/yuin/goldmark/parser/atx_heading.go b/vendor/github.com/yuin/goldmark/parser/atx_heading.go index 0b63fabc0..a631e0b1f 100644 --- a/vendor/github.com/yuin/goldmark/parser/atx_heading.go +++ b/vendor/github.com/yuin/goldmark/parser/atx_heading.go @@ -232,7 +232,7 @@ func parseLastLineAttributes(node ast.Node, reader text.Reader, pc Context) { } lr.Advance(1) } - if ok && util.IsBlank(line[end.Stop:]) { + if ok && util.IsBlank(line[end.Start:]) { for _, attr := range attrs { node.SetAttribute(attr.Name, attr.Value) } diff --git a/vendor/golang.org/x/sync/semaphore/semaphore.go b/vendor/golang.org/x/sync/semaphore/semaphore.go index 7f096fef0..30f632c57 100644 --- a/vendor/golang.org/x/sync/semaphore/semaphore.go +++ b/vendor/golang.org/x/sync/semaphore/semaphore.go @@ -67,7 +67,12 @@ func (s *Weighted) Acquire(ctx context.Context, n int64) error { // fix up the queue, just pretend we didn't notice the cancelation. err = nil default: + isFront := s.waiters.Front() == elem s.waiters.Remove(elem) + // If we're at the front and there're extra tokens left, notify other waiters. + if isFront && s.size > s.cur { + s.notifyWaiters() + } } s.mu.Unlock() return err @@ -97,6 +102,11 @@ func (s *Weighted) Release(n int64) { s.mu.Unlock() panic("semaphore: released more than held") } + s.notifyWaiters() + s.mu.Unlock() +} + +func (s *Weighted) notifyWaiters() { for { next := s.waiters.Front() if next == nil { @@ -123,5 +133,4 @@ func (s *Weighted) Release(n int64) { s.waiters.Remove(next) close(w.ready) } - s.mu.Unlock() } diff --git a/vendor/gopkg.in/ini.v1/Makefile b/vendor/gopkg.in/ini.v1/Makefile index af27ff076..f3b0dae2d 100644 --- a/vendor/gopkg.in/ini.v1/Makefile +++ b/vendor/gopkg.in/ini.v1/Makefile @@ -6,7 +6,7 @@ test: go test -v -cover -race bench: - go test -v -cover -race -test.bench=. -test.benchmem + go test -v -cover -test.bench=. -test.benchmem vet: go vet diff --git a/vendor/gopkg.in/ini.v1/README.md b/vendor/gopkg.in/ini.v1/README.md index 3d6d3cfc0..5d65658b2 100644 --- a/vendor/gopkg.in/ini.v1/README.md +++ b/vendor/gopkg.in/ini.v1/README.md @@ -1,6 +1,9 @@ # INI -[![Build Status](https://img.shields.io/travis/go-ini/ini/master.svg?style=for-the-badge&logo=travis)](https://travis-ci.org/go-ini/ini) [![Sourcegraph](https://img.shields.io/badge/view%20on-Sourcegraph-brightgreen.svg?style=for-the-badge&logo=sourcegraph)](https://sourcegraph.com/github.com/go-ini/ini) +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/go-ini/ini/Go?logo=github&style=for-the-badge)](https://github.com/go-ini/ini/actions?query=workflow%3AGo) +[![codecov](https://img.shields.io/codecov/c/github/go-ini/ini/master?logo=codecov&style=for-the-badge)](https://codecov.io/gh/go-ini/ini) +[![GoDoc](https://img.shields.io/badge/GoDoc-Reference-blue?style=for-the-badge&logo=go)](https://pkg.go.dev/github.com/go-ini/ini?tab=doc) +[![Sourcegraph](https://img.shields.io/badge/view%20on-Sourcegraph-brightgreen.svg?style=for-the-badge&logo=sourcegraph)](https://sourcegraph.com/github.com/go-ini/ini) ![](https://avatars0.githubusercontent.com/u/10216035?v=3&s=200) @@ -8,7 +11,7 @@ Package ini provides INI file read and write functionality in Go. ## Features -- Load from multiple data sources(`[]byte`, file and `io.ReadCloser`) with overwrites. +- Load from multiple data sources(file, `[]byte`, `io.Reader` and `io.ReadCloser`) with overwrites. - Read with recursion values. - Read with parent-child sections. - Read with auto-increment key names. @@ -33,6 +36,7 @@ Please add `-u` flag to update in the future. - [Getting Started](https://ini.unknwon.io/docs/intro/getting_started) - [API Documentation](https://gowalker.org/gopkg.in/ini.v1) +- 中国大陆镜像:https://ini.unknwon.cn ## License diff --git a/vendor/gopkg.in/ini.v1/codecov.yml b/vendor/gopkg.in/ini.v1/codecov.yml new file mode 100644 index 000000000..fc947f230 --- /dev/null +++ b/vendor/gopkg.in/ini.v1/codecov.yml @@ -0,0 +1,9 @@ +coverage: + range: "60...95" + status: + project: + default: + threshold: 1% + +comment: + layout: 'diff, files' diff --git a/vendor/gopkg.in/ini.v1/data_source.go b/vendor/gopkg.in/ini.v1/data_source.go index dc0277ec6..c3a541f1d 100644 --- a/vendor/gopkg.in/ini.v1/data_source.go +++ b/vendor/gopkg.in/ini.v1/data_source.go @@ -68,6 +68,8 @@ func parseDataSource(source interface{}) (dataSource, error) { return &sourceData{s}, nil case io.ReadCloser: return &sourceReadCloser{s}, nil + case io.Reader: + return &sourceReadCloser{ioutil.NopCloser(s)}, nil default: return nil, fmt.Errorf("error parsing data source: unknown type %q", s) } diff --git a/vendor/gopkg.in/ini.v1/file.go b/vendor/gopkg.in/ini.v1/file.go index 017b77c8b..f95606f90 100644 --- a/vendor/gopkg.in/ini.v1/file.go +++ b/vendor/gopkg.in/ini.v1/file.go @@ -25,7 +25,7 @@ import ( "sync" ) -// File represents a combination of a or more INI file(s) in memory. +// File represents a combination of one or more INI files in memory. type File struct { options LoadOptions dataSources []dataSource @@ -36,8 +36,12 @@ type File struct { // To keep data in order. sectionList []string + // To keep track of the index of a section with same name. + // This meta list is only used with non-unique section names are allowed. + sectionIndexes []int + // Actual data is stored here. - sections map[string]*Section + sections map[string][]*Section NameMapper ValueMapper @@ -48,27 +52,37 @@ func newFile(dataSources []dataSource, opts LoadOptions) *File { if len(opts.KeyValueDelimiters) == 0 { opts.KeyValueDelimiters = "=:" } + if len(opts.KeyValueDelimiterOnWrite) == 0 { + opts.KeyValueDelimiterOnWrite = "=" + } + return &File{ BlockMode: true, dataSources: dataSources, - sections: make(map[string]*Section), - sectionList: make([]string, 0, 10), + sections: make(map[string][]*Section), options: opts, } } // Empty returns an empty file object. -func Empty() *File { - // Ignore error here, we sure our data is good. - f, _ := Load([]byte("")) +func Empty(opts ...LoadOptions) *File { + var opt LoadOptions + if len(opts) > 0 { + opt = opts[0] + } + + // Ignore error here, we are sure our data is good. + f, _ := LoadSources(opt, []byte("")) return f } // NewSection creates a new section. func (f *File) NewSection(name string) (*Section, error) { if len(name) == 0 { - return nil, errors.New("error creating new section: empty section name") - } else if f.options.Insensitive && name != DefaultSection { + return nil, errors.New("empty section name") + } + + if f.options.Insensitive && name != DefaultSection { name = strings.ToLower(name) } @@ -77,13 +91,20 @@ func (f *File) NewSection(name string) (*Section, error) { defer f.lock.Unlock() } - if inSlice(name, f.sectionList) { - return f.sections[name], nil + if !f.options.AllowNonUniqueSections && inSlice(name, f.sectionList) { + return f.sections[name][0], nil } f.sectionList = append(f.sectionList, name) - f.sections[name] = newSection(f, name) - return f.sections[name], nil + + // NOTE: Append to indexes must happen before appending to sections, + // otherwise index will have off-by-one problem. + f.sectionIndexes = append(f.sectionIndexes, len(f.sections[name])) + + sec := newSection(f, name) + f.sections[name] = append(f.sections[name], sec) + + return sec, nil } // NewRawSection creates a new section with an unparseable body. @@ -110,6 +131,16 @@ func (f *File) NewSections(names ...string) (err error) { // GetSection returns section by given name. func (f *File) GetSection(name string) (*Section, error) { + secs, err := f.SectionsByName(name) + if err != nil { + return nil, err + } + + return secs[0], err +} + +// SectionsByName returns all sections with given name. +func (f *File) SectionsByName(name string) ([]*Section, error) { if len(name) == 0 { name = DefaultSection } @@ -122,11 +153,12 @@ func (f *File) GetSection(name string) (*Section, error) { defer f.lock.RUnlock() } - sec := f.sections[name] - if sec == nil { - return nil, fmt.Errorf("section '%s' does not exist", name) + secs := f.sections[name] + if len(secs) == 0 { + return nil, fmt.Errorf("section %q does not exist", name) } - return sec, nil + + return secs, nil } // Section assumes named section exists and returns a zero-value when not. @@ -141,6 +173,19 @@ func (f *File) Section(name string) *Section { return sec } +// SectionWithIndex assumes named section exists and returns a new section when not. +func (f *File) SectionWithIndex(name string, index int) *Section { + secs, err := f.SectionsByName(name) + if err != nil || len(secs) <= index { + // NOTE: It's OK here because the only possible error is empty section name, + // but if it's empty, this piece of code won't be executed. + newSec, _ := f.NewSection(name) + return newSec + } + + return secs[index] +} + // Sections returns a list of Section stored in the current instance. func (f *File) Sections() []*Section { if f.BlockMode { @@ -150,7 +195,7 @@ func (f *File) Sections() []*Section { sections := make([]*Section, len(f.sectionList)) for i, name := range f.sectionList { - sections[i] = f.sections[name] + sections[i] = f.sections[name][f.sectionIndexes[i]] } return sections } @@ -167,24 +212,70 @@ func (f *File) SectionStrings() []string { return list } -// DeleteSection deletes a section. +// DeleteSection deletes a section or all sections with given name. func (f *File) DeleteSection(name string) { - if f.BlockMode { - f.lock.Lock() - defer f.lock.Unlock() + secs, err := f.SectionsByName(name) + if err != nil { + return + } + + for i := 0; i < len(secs); i++ { + // For non-unique sections, it is always needed to remove the first one so + // in the next iteration, the subsequent section continue having index 0. + // Ignoring the error as index 0 never returns an error. + _ = f.DeleteSectionWithIndex(name, 0) + } +} + +// DeleteSectionWithIndex deletes a section with given name and index. +func (f *File) DeleteSectionWithIndex(name string, index int) error { + if !f.options.AllowNonUniqueSections && index != 0 { + return fmt.Errorf("delete section with non-zero index is only allowed when non-unique sections is enabled") } if len(name) == 0 { name = DefaultSection } + if f.options.Insensitive { + name = strings.ToLower(name) + } + + if f.BlockMode { + f.lock.Lock() + defer f.lock.Unlock() + } + + // Count occurrences of the sections + occurrences := 0 + + sectionListCopy := make([]string, len(f.sectionList)) + copy(sectionListCopy, f.sectionList) + + for i, s := range sectionListCopy { + if s != name { + continue + } - for i, s := range f.sectionList { - if s == name { + if occurrences == index { + if len(f.sections[name]) <= 1 { + delete(f.sections, name) // The last one in the map + } else { + f.sections[name] = append(f.sections[name][:index], f.sections[name][index+1:]...) + } + + // Fix section lists f.sectionList = append(f.sectionList[:i], f.sectionList[i+1:]...) - delete(f.sections, name) - return + f.sectionIndexes = append(f.sectionIndexes[:i], f.sectionIndexes[i+1:]...) + + } else if occurrences > index { + // Fix the indices of all following sections with this name. + f.sectionIndexes[i-1]-- } + + occurrences++ } + + return nil } func (f *File) reload(s dataSource) error { @@ -203,7 +294,7 @@ func (f *File) Reload() (err error) { if err = f.reload(s); err != nil { // In loose mode, we create an empty default section for nonexistent files. if os.IsNotExist(err) && f.options.Loose { - f.parse(bytes.NewBuffer(nil)) + _ = f.parse(bytes.NewBuffer(nil)) continue } return err @@ -230,16 +321,16 @@ func (f *File) Append(source interface{}, others ...interface{}) error { } func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) { - equalSign := DefaultFormatLeft + "=" + DefaultFormatRight + equalSign := DefaultFormatLeft + f.options.KeyValueDelimiterOnWrite + DefaultFormatRight if PrettyFormat || PrettyEqual { - equalSign = " = " + equalSign = fmt.Sprintf(" %s ", f.options.KeyValueDelimiterOnWrite) } // Use buffer to make sure target is safe until finish encoding. buf := bytes.NewBuffer(nil) for i, sname := range f.sectionList { - sec := f.Section(sname) + sec := f.SectionWithIndex(sname, f.sectionIndexes[i]) if len(sec.Comment) > 0 { // Support multiline comments lines := strings.Split(sec.Comment, LineBreak) @@ -282,7 +373,7 @@ func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) { } // Count and generate alignment length and buffer spaces using the - // longest key. Keys may be modifed if they contain certain characters so + // longest key. Keys may be modified if they contain certain characters so // we need to take that into account in our calculation. alignLength := 0 if PrettyFormat { diff --git a/vendor/gopkg.in/ini.v1/ini.go b/vendor/gopkg.in/ini.v1/ini.go index fe913a0fd..2961543f9 100644 --- a/vendor/gopkg.in/ini.v1/ini.go +++ b/vendor/gopkg.in/ini.v1/ini.go @@ -18,8 +18,10 @@ package ini import ( + "os" "regexp" "runtime" + "strings" ) const ( @@ -29,14 +31,8 @@ const ( // Maximum allowed depth when recursively substituing variable names. depthValues = 99 - version = "1.52.0" ) -// Version returns current package version literal. -func Version() string { - return version -} - var ( // LineBreak is the delimiter to determine or compose a new line. // This variable will be changed to "\r\n" automatically on Windows at package init time. @@ -61,8 +57,10 @@ var ( DefaultFormatRight = "" ) +var inTest = len(os.Args) > 0 && strings.HasSuffix(strings.TrimSuffix(os.Args[0], ".exe"), ".test") + func init() { - if runtime.GOOS == "windows" { + if runtime.GOOS == "windows" && !inTest { LineBreak = "\r\n" } } @@ -109,12 +107,16 @@ type LoadOptions struct { UnparseableSections []string // KeyValueDelimiters is the sequence of delimiters that are used to separate key and value. By default, it is "=:". KeyValueDelimiters string + // KeyValueDelimiters is the delimiter that are used to separate key and value output. By default, it is "=". + KeyValueDelimiterOnWrite string // PreserveSurroundedQuote indicates whether to preserve surrounded quote (single and double quotes). PreserveSurroundedQuote bool // DebugFunc is called to collect debug information (currently only useful to debug parsing Python-style multiline values). DebugFunc DebugFunc // ReaderBufferSize is the buffer size of the reader in bytes. ReaderBufferSize int + // AllowNonUniqueSections indicates whether to allow sections with the same name multiple times. + AllowNonUniqueSections bool } // DebugFunc is the type of function called to log parse events. diff --git a/vendor/gopkg.in/ini.v1/key.go b/vendor/gopkg.in/ini.v1/key.go index 3c197410f..8baafd9ea 100644 --- a/vendor/gopkg.in/ini.v1/key.go +++ b/vendor/gopkg.in/ini.v1/key.go @@ -686,99 +686,127 @@ func (k *Key) StrictTimes(delim string) ([]time.Time, error) { // parseBools transforms strings to bools. func (k *Key) parseBools(strs []string, addInvalid, returnOnInvalid bool) ([]bool, error) { vals := make([]bool, 0, len(strs)) - for _, str := range strs { + parser := func(str string) (interface{}, error) { val, err := parseBool(str) - if err != nil && returnOnInvalid { - return nil, err - } - if err == nil || addInvalid { - vals = append(vals, val) + return val, err + } + rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser) + if err == nil { + for _, val := range rawVals { + vals = append(vals, val.(bool)) } } - return vals, nil + return vals, err } // parseFloat64s transforms strings to float64s. func (k *Key) parseFloat64s(strs []string, addInvalid, returnOnInvalid bool) ([]float64, error) { vals := make([]float64, 0, len(strs)) - for _, str := range strs { + parser := func(str string) (interface{}, error) { val, err := strconv.ParseFloat(str, 64) - if err != nil && returnOnInvalid { - return nil, err - } - if err == nil || addInvalid { - vals = append(vals, val) + return val, err + } + rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser) + if err == nil { + for _, val := range rawVals { + vals = append(vals, val.(float64)) } } - return vals, nil + return vals, err } // parseInts transforms strings to ints. func (k *Key) parseInts(strs []string, addInvalid, returnOnInvalid bool) ([]int, error) { vals := make([]int, 0, len(strs)) - for _, str := range strs { - valInt64, err := strconv.ParseInt(str, 0, 64) - val := int(valInt64) - if err != nil && returnOnInvalid { - return nil, err - } - if err == nil || addInvalid { - vals = append(vals, val) + parser := func(str string) (interface{}, error) { + val, err := strconv.ParseInt(str, 0, 64) + return val, err + } + rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser) + if err == nil { + for _, val := range rawVals { + vals = append(vals, int(val.(int64))) } } - return vals, nil + return vals, err } // parseInt64s transforms strings to int64s. func (k *Key) parseInt64s(strs []string, addInvalid, returnOnInvalid bool) ([]int64, error) { vals := make([]int64, 0, len(strs)) - for _, str := range strs { + parser := func(str string) (interface{}, error) { val, err := strconv.ParseInt(str, 0, 64) - if err != nil && returnOnInvalid { - return nil, err - } - if err == nil || addInvalid { - vals = append(vals, val) + return val, err + } + + rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser) + if err == nil { + for _, val := range rawVals { + vals = append(vals, val.(int64)) } } - return vals, nil + return vals, err } // parseUints transforms strings to uints. func (k *Key) parseUints(strs []string, addInvalid, returnOnInvalid bool) ([]uint, error) { vals := make([]uint, 0, len(strs)) - for _, str := range strs { - val, err := strconv.ParseUint(str, 0, 0) - if err != nil && returnOnInvalid { - return nil, err - } - if err == nil || addInvalid { - vals = append(vals, uint(val)) + parser := func(str string) (interface{}, error) { + val, err := strconv.ParseUint(str, 0, 64) + return val, err + } + + rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser) + if err == nil { + for _, val := range rawVals { + vals = append(vals, uint(val.(uint64))) } } - return vals, nil + return vals, err } // parseUint64s transforms strings to uint64s. func (k *Key) parseUint64s(strs []string, addInvalid, returnOnInvalid bool) ([]uint64, error) { vals := make([]uint64, 0, len(strs)) - for _, str := range strs { + parser := func(str string) (interface{}, error) { val, err := strconv.ParseUint(str, 0, 64) - if err != nil && returnOnInvalid { - return nil, err - } - if err == nil || addInvalid { - vals = append(vals, val) + return val, err + } + rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser) + if err == nil { + for _, val := range rawVals { + vals = append(vals, val.(uint64)) } } - return vals, nil + return vals, err } + +type Parser func(str string) (interface{}, error) + + // parseTimesFormat transforms strings to times in given format. func (k *Key) parseTimesFormat(format string, strs []string, addInvalid, returnOnInvalid bool) ([]time.Time, error) { vals := make([]time.Time, 0, len(strs)) - for _, str := range strs { + parser := func(str string) (interface{}, error) { val, err := time.Parse(format, str) + return val, err + } + rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser) + if err == nil { + for _, val := range rawVals { + vals = append(vals, val.(time.Time)) + } + } + return vals, err +} + + +// doParse transforms strings to different types +func (k *Key) doParse(strs []string, addInvalid, returnOnInvalid bool, parser Parser) ([]interface{}, error) { + vals := make([]interface{}, 0, len(strs)) + for _, str := range strs { + val, err := parser(str) if err != nil && returnOnInvalid { return nil, err } diff --git a/vendor/gopkg.in/ini.v1/parser.go b/vendor/gopkg.in/ini.v1/parser.go index c0a93a62a..ea6c08b02 100644 --- a/vendor/gopkg.in/ini.v1/parser.go +++ b/vendor/gopkg.in/ini.v1/parser.go @@ -84,7 +84,10 @@ func (p *parser) BOM() error { case mask[0] == 254 && mask[1] == 255: fallthrough case mask[0] == 255 && mask[1] == 254: - p.buf.Read(mask) + _, err = p.buf.Read(mask) + if err != nil { + return err + } case mask[0] == 239 && mask[1] == 187: mask, err := p.buf.Peek(3) if err != nil && err != io.EOF { @@ -93,7 +96,10 @@ func (p *parser) BOM() error { return nil } if mask[2] == 191 { - p.buf.Read(mask) + _, err = p.buf.Read(mask) + if err != nil { + return err + } } } return nil @@ -135,7 +141,7 @@ func readKeyName(delimiters string, in []byte) (string, int, error) { } // Get out key name - endIdx := -1 + var endIdx int if len(keyQuote) > 0 { startIdx := len(keyQuote) // FIXME: fail case -> """"""name"""=value @@ -181,7 +187,7 @@ func (p *parser) readMultilines(line, val, valQuote string) (string, error) { } val += next if p.isEOF { - return "", fmt.Errorf("missing closing key quote from '%s' to '%s'", line, next) + return "", fmt.Errorf("missing closing key quote from %q to %q", line, next) } } return val, nil @@ -413,7 +419,10 @@ func (f *File) parse(reader io.Reader) (err error) { if f.options.AllowNestedValues && isLastValueEmpty && len(line) > 0 { if line[0] == ' ' || line[0] == '\t' { - lastRegularKey.addNestedValue(string(bytes.TrimSpace(line))) + err = lastRegularKey.addNestedValue(string(bytes.TrimSpace(line))) + if err != nil { + return err + } continue } } @@ -460,7 +469,7 @@ func (f *File) parse(reader io.Reader) (err error) { inUnparseableSection = false for i := range f.options.UnparseableSections { if f.options.UnparseableSections[i] == name || - (f.options.Insensitive && strings.ToLower(f.options.UnparseableSections[i]) == strings.ToLower(name)) { + (f.options.Insensitive && strings.EqualFold(f.options.UnparseableSections[i], name)) { inUnparseableSection = true continue } diff --git a/vendor/gopkg.in/ini.v1/section.go b/vendor/gopkg.in/ini.v1/section.go index 0bd3e1301..6ba5ac290 100644 --- a/vendor/gopkg.in/ini.v1/section.go +++ b/vendor/gopkg.in/ini.v1/section.go @@ -131,7 +131,7 @@ func (s *Section) GetKey(name string) (*Key, error) { } break } - return nil, fmt.Errorf("error when getting key of section '%s': key '%s' not exists", s.name, name) + return nil, fmt.Errorf("error when getting key of section %q: key %q not exists", s.name, name) } return key, nil } @@ -249,7 +249,7 @@ func (s *Section) ChildSections() []*Section { children := make([]*Section, 0, 3) for _, name := range s.f.sectionList { if strings.HasPrefix(name, prefix) { - children = append(children, s.f.sections[name]) + children = append(children, s.f.sections[name]...) } } return children diff --git a/vendor/gopkg.in/ini.v1/struct.go b/vendor/gopkg.in/ini.v1/struct.go index a0c3ad5a4..1df547190 100644 --- a/vendor/gopkg.in/ini.v1/struct.go +++ b/vendor/gopkg.in/ini.v1/struct.go @@ -258,13 +258,13 @@ func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim stri case reflect.Slice: return setSliceWithProperType(key, field, delim, allowShadow, isStrict) default: - return fmt.Errorf("unsupported type '%s'", t) + return fmt.Errorf("unsupported type %q", t) } return nil } -func parseTagOptions(tag string) (rawName string, omitEmpty bool, allowShadow bool) { - opts := strings.SplitN(tag, ",", 3) +func parseTagOptions(tag string) (rawName string, omitEmpty bool, allowShadow bool, allowNonUnique bool) { + opts := strings.SplitN(tag, ",", 4) rawName = opts[0] if len(opts) > 1 { omitEmpty = opts[1] == "omitempty" @@ -272,10 +272,13 @@ func parseTagOptions(tag string) (rawName string, omitEmpty bool, allowShadow bo if len(opts) > 2 { allowShadow = opts[2] == "allowshadow" } - return rawName, omitEmpty, allowShadow + if len(opts) > 3 { + allowNonUnique = opts[3] == "nonunique" + } + return rawName, omitEmpty, allowShadow, allowNonUnique } -func (s *Section) mapTo(val reflect.Value, isStrict bool) error { +func (s *Section) mapToField(val reflect.Value, isStrict bool) error { if val.Kind() == reflect.Ptr { val = val.Elem() } @@ -290,7 +293,7 @@ func (s *Section) mapTo(val reflect.Value, isStrict bool) error { continue } - rawName, _, allowShadow := parseTagOptions(tag) + rawName, _, allowShadow, allowNonUnique := parseTagOptions(tag) fieldName := s.parseFieldName(tpField.Name, rawName) if len(fieldName) == 0 || !field.CanSet() { continue @@ -310,49 +313,87 @@ func (s *Section) mapTo(val reflect.Value, isStrict bool) error { if isStructPtr && field.IsNil() { field.Set(reflect.New(tpField.Type.Elem())) } - if err = sec.mapTo(field, isStrict); err != nil { - return fmt.Errorf("error mapping field %q: %v", fieldName, err) + if err = sec.mapToField(field, isStrict); err != nil { + return fmt.Errorf("map to field %q: %v", fieldName, err) } continue } } + + // Map non-unique sections + if allowNonUnique && tpField.Type.Kind() == reflect.Slice { + newField, err := s.mapToSlice(fieldName, field, isStrict) + if err != nil { + return fmt.Errorf("map to slice %q: %v", fieldName, err) + } + + field.Set(newField) + continue + } + if key, err := s.GetKey(fieldName); err == nil { delim := parseDelim(tpField.Tag.Get("delim")) if err = setWithProperType(tpField.Type, key, field, delim, allowShadow, isStrict); err != nil { - return fmt.Errorf("error mapping field %q: %v", fieldName, err) + return fmt.Errorf("set field %q: %v", fieldName, err) } } } return nil } -// MapTo maps section to given struct. -func (s *Section) MapTo(v interface{}) error { +// mapToSlice maps all sections with the same name and returns the new value. +// The type of the Value must be a slice. +func (s *Section) mapToSlice(secName string, val reflect.Value, isStrict bool) (reflect.Value, error) { + secs, err := s.f.SectionsByName(secName) + if err != nil { + return reflect.Value{}, err + } + + typ := val.Type().Elem() + for _, sec := range secs { + elem := reflect.New(typ) + if err = sec.mapToField(elem, isStrict); err != nil { + return reflect.Value{}, fmt.Errorf("map to field from section %q: %v", secName, err) + } + + val = reflect.Append(val, elem.Elem()) + } + return val, nil +} + +// mapTo maps a section to object v. +func (s *Section) mapTo(v interface{}, isStrict bool) error { typ := reflect.TypeOf(v) val := reflect.ValueOf(v) if typ.Kind() == reflect.Ptr { typ = typ.Elem() val = val.Elem() } else { - return errors.New("cannot map to non-pointer struct") + return errors.New("not a pointer to a struct") } - return s.mapTo(val, false) + if typ.Kind() == reflect.Slice { + newField, err := s.mapToSlice(s.name, val, isStrict) + if err != nil { + return err + } + + val.Set(newField) + return nil + } + + return s.mapToField(val, isStrict) +} + +// MapTo maps section to given struct. +func (s *Section) MapTo(v interface{}) error { + return s.mapTo(v, false) } // StrictMapTo maps section to given struct in strict mode, // which returns all possible error including value parsing error. func (s *Section) StrictMapTo(v interface{}) error { - typ := reflect.TypeOf(v) - val := reflect.ValueOf(v) - if typ.Kind() == reflect.Ptr { - typ = typ.Elem() - val = val.Elem() - } else { - return errors.New("cannot map to non-pointer struct") - } - - return s.mapTo(val, true) + return s.mapTo(v, true) } // MapTo maps file to given struct. @@ -430,7 +471,7 @@ func reflectSliceWithProperType(key *Key, field reflect.Value, delim string, all if i == 0 { keyWithShadows = newKey(key.s, key.name, val) } else { - keyWithShadows.AddShadow(val) + _ = keyWithShadows.AddShadow(val) } } key = keyWithShadows @@ -483,7 +524,7 @@ func reflectWithProperType(t reflect.Type, key *Key, field reflect.Value, delim return reflectWithProperType(t.Elem(), key, field.Elem(), delim, allowShadow) } default: - return fmt.Errorf("unsupported type '%s'", t) + return fmt.Errorf("unsupported type %q", t) } return nil } @@ -523,6 +564,10 @@ func (s *Section) reflectFrom(val reflect.Value) error { typ := val.Type() for i := 0; i < typ.NumField(); i++ { + if !val.Field(i).CanInterface() { + continue + } + field := val.Field(i) tpField := typ.Field(i) @@ -531,7 +576,7 @@ func (s *Section) reflectFrom(val reflect.Value) error { continue } - rawName, omitEmpty, allowShadow := parseTagOptions(tag) + rawName, omitEmpty, allowShadow, allowNonUnique := parseTagOptions(tag) if omitEmpty && isEmptyValue(field) { continue } @@ -560,11 +605,41 @@ func (s *Section) reflectFrom(val reflect.Value) error { } if err = sec.reflectFrom(field); err != nil { - return fmt.Errorf("error reflecting field %q: %v", fieldName, err) + return fmt.Errorf("reflect from field %q: %v", fieldName, err) + } + continue + } + + if allowNonUnique && tpField.Type.Kind() == reflect.Slice { + slice := field.Slice(0, field.Len()) + if field.Len() == 0 { + return nil + } + sliceOf := field.Type().Elem().Kind() + + for i := 0; i < field.Len(); i++ { + if sliceOf != reflect.Struct && sliceOf != reflect.Ptr { + return fmt.Errorf("field %q is not a slice of pointer or struct", fieldName) + } + + sec, err := s.f.NewSection(fieldName) + if err != nil { + return err + } + + // Add comment from comment tag + if len(sec.Comment) == 0 { + sec.Comment = tpField.Tag.Get("comment") + } + + if err := sec.reflectFrom(slice.Index(i)); err != nil { + return fmt.Errorf("reflect from field %q: %v", fieldName, err) + } } continue } + // Note: Same reason as section. key, err := s.GetKey(fieldName) if err != nil { key, _ = s.NewKey(fieldName, "") @@ -577,22 +652,56 @@ func (s *Section) reflectFrom(val reflect.Value) error { delim := parseDelim(tpField.Tag.Get("delim")) if err = reflectWithProperType(tpField.Type, key, field, delim, allowShadow); err != nil { - return fmt.Errorf("error reflecting field %q: %v", fieldName, err) + return fmt.Errorf("reflect field %q: %v", fieldName, err) } } return nil } -// ReflectFrom reflects secion from given struct. +// ReflectFrom reflects section from given struct. It overwrites existing ones. func (s *Section) ReflectFrom(v interface{}) error { typ := reflect.TypeOf(v) val := reflect.ValueOf(v) + + if s.name != DefaultSection && s.f.options.AllowNonUniqueSections && + (typ.Kind() == reflect.Slice || typ.Kind() == reflect.Ptr) { + // Clear sections to make sure none exists before adding the new ones + s.f.DeleteSection(s.name) + + if typ.Kind() == reflect.Ptr { + sec, err := s.f.NewSection(s.name) + if err != nil { + return err + } + return sec.reflectFrom(val.Elem()) + } + + slice := val.Slice(0, val.Len()) + sliceOf := val.Type().Elem().Kind() + if sliceOf != reflect.Ptr { + return fmt.Errorf("not a slice of pointers") + } + + for i := 0; i < slice.Len(); i++ { + sec, err := s.f.NewSection(s.name) + if err != nil { + return err + } + + err = sec.reflectFrom(slice.Index(i)) + if err != nil { + return fmt.Errorf("reflect from %dth field: %v", i, err) + } + } + + return nil + } + if typ.Kind() == reflect.Ptr { - typ = typ.Elem() val = val.Elem() } else { - return errors.New("cannot reflect from non-pointer struct") + return errors.New("not a pointer to a struct") } return s.reflectFrom(val) diff --git a/vendor/modules.txt b/vendor/modules.txt index 343224c59..383109df4 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -100,6 +100,34 @@ github.com/RichardKnop/redsync # github.com/RoaringBitmap/roaring v0.4.23 ## explicit github.com/RoaringBitmap/roaring +# github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 +github.com/alibabacloud-go/alibabacloud-gateway-spi/client +# github.com/alibabacloud-go/darabonba-openapi v0.1.18 +## explicit +github.com/alibabacloud-go/darabonba-openapi/client +# github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68 +github.com/alibabacloud-go/debug/debug +# github.com/alibabacloud-go/dysmsapi-20170525/v2 v2.0.9 +## explicit +github.com/alibabacloud-go/dysmsapi-20170525/v2/client +# github.com/alibabacloud-go/endpoint-util v1.1.0 +github.com/alibabacloud-go/endpoint-util/service +# github.com/alibabacloud-go/openapi-util v0.0.11 +github.com/alibabacloud-go/openapi-util/service +# github.com/alibabacloud-go/tea v1.1.17 +## explicit +github.com/alibabacloud-go/tea/tea +github.com/alibabacloud-go/tea/utils +# github.com/alibabacloud-go/tea-utils v1.4.3 +github.com/alibabacloud-go/tea-utils/service +# github.com/alibabacloud-go/tea-xml v1.1.2 +## explicit +github.com/alibabacloud-go/tea-xml/service +# github.com/aliyun/credentials-go v1.1.2 +github.com/aliyun/credentials-go/credentials +github.com/aliyun/credentials-go/credentials/request +github.com/aliyun/credentials-go/credentials/response +github.com/aliyun/credentials-go/credentials/utils # github.com/andybalholm/cascadia v1.0.0 github.com/andybalholm/cascadia # github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 @@ -214,6 +242,9 @@ github.com/boombuler/barcode/utils github.com/bradfitz/gomemcache/memcache # github.com/chris-ramon/douceur v0.2.0 github.com/chris-ramon/douceur/parser +# github.com/clbanning/mxj/v2 v2.5.5 +## explicit +github.com/clbanning/mxj/v2 # github.com/couchbase/gomemcached v0.0.0-20191004160342-7b5da2ec40b2 ## explicit github.com/couchbase/gomemcached @@ -515,7 +546,7 @@ github.com/jmespath/go-jmespath ## explicit # github.com/joho/godotenv v1.3.0 ## explicit -# github.com/json-iterator/go v1.1.9 +# github.com/json-iterator/go v1.1.10 github.com/json-iterator/go # github.com/kballard/go-shellquote v0.0.0-20170619183022-cd60e84ee657 ## explicit @@ -778,6 +809,8 @@ github.com/syndtr/goleveldb/leveldb/util # github.com/tinylib/msgp v1.1.2 ## explicit github.com/tinylib/msgp/msgp +# github.com/tjfoc/gmsm v1.3.2 +github.com/tjfoc/gmsm/sm3 # github.com/toqueteos/trie v1.0.0 github.com/toqueteos/trie # github.com/toqueteos/webbrowser v1.2.0 @@ -815,7 +848,7 @@ github.com/xdg/stringprep # github.com/yohcop/openid-go v1.0.0 ## explicit github.com/yohcop/openid-go -# github.com/yuin/goldmark v1.1.27 +# github.com/yuin/goldmark v1.1.30 ## explicit github.com/yuin/goldmark github.com/yuin/goldmark/ast @@ -878,7 +911,7 @@ go.opencensus.io/trace go.opencensus.io/trace/internal go.opencensus.io/trace/propagation go.opencensus.io/trace/tracestate -# golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 +# golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 ## explicit golang.org/x/crypto/acme golang.org/x/crypto/acme/autocert @@ -933,7 +966,7 @@ golang.org/x/oauth2/google golang.org/x/oauth2/internal golang.org/x/oauth2/jws golang.org/x/oauth2/jwt -# golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e +# golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a golang.org/x/sync/errgroup golang.org/x/sync/semaphore # golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f @@ -1118,7 +1151,7 @@ gopkg.in/asn1-ber.v1 # gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df ## explicit gopkg.in/gomail.v2 -# gopkg.in/ini.v1 v1.52.0 +# gopkg.in/ini.v1 v1.56.0 ## explicit gopkg.in/ini.v1 # gopkg.in/ldap.v3 v3.0.2 From 453616bcf54145d489fe3cd4ea04041199f4f57c Mon Sep 17 00:00:00 2001 From: ychao_1983 Date: Thu, 2 Jun 2022 17:35:13 +0800 Subject: [PATCH 02/45] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 3 + go.sum | 4 + models/login_source.go | 8 + models/user.go | 27 + modules/auth/user_form.go | 33 + modules/context/auth.go | 4 + modules/public/dynamic.go | 43 + modules/public/static.go | 29 + modules/redis/redis_client/client.go | 54 + modules/setting/phone.go | 4 + modules/setting/slideimage.go | 12 + modules/slideimage/slideimage.go | 307 ++++++ options/locale/locale_en-US.ini | 13 + options/locale/locale_zh-CN.ini | 16 + public/img/phone/phone.go | 98 ++ routers/init.go | 3 + routers/routes/routes.go | 13 + routers/user/auth.go | 207 +++- routers/user/setting/profile.go | 14 + services/phone/phone.go | 98 ++ .../disintegration/imaging/.travis.yml | 12 + .../github.com/disintegration/imaging/LICENSE | 21 + .../disintegration/imaging/README.md | 226 ++++ .../disintegration/imaging/adjust.go | 253 +++++ .../disintegration/imaging/convolution.go | 148 +++ .../github.com/disintegration/imaging/doc.go | 7 + .../disintegration/imaging/effects.go | 169 +++ .../github.com/disintegration/imaging/go.mod | 3 + .../github.com/disintegration/imaging/go.sum | 3 + .../disintegration/imaging/histogram.go | 52 + .../github.com/disintegration/imaging/io.go | 444 ++++++++ .../disintegration/imaging/resize.go | 595 +++++++++++ .../disintegration/imaging/scanner.go | 285 +++++ .../disintegration/imaging/tools.go | 249 +++++ .../disintegration/imaging/transform.go | 268 +++++ .../disintegration/imaging/utils.go | 167 +++ vendor/golang.org/x/image/AUTHORS | 3 + vendor/golang.org/x/image/CONTRIBUTORS | 3 + vendor/golang.org/x/image/LICENSE | 27 + vendor/golang.org/x/image/PATENTS | 22 + vendor/golang.org/x/image/bmp/reader.go | 213 ++++ vendor/golang.org/x/image/bmp/writer.go | 262 +++++ vendor/golang.org/x/image/ccitt/reader.go | 697 ++++++++++++ vendor/golang.org/x/image/ccitt/table.go | 989 ++++++++++++++++++ vendor/golang.org/x/image/ccitt/writer.go | 102 ++ vendor/golang.org/x/image/tiff/buffer.go | 69 ++ vendor/golang.org/x/image/tiff/compress.go | 58 + vendor/golang.org/x/image/tiff/consts.go | 149 +++ vendor/golang.org/x/image/tiff/fuzz.go | 29 + vendor/golang.org/x/image/tiff/lzw/reader.go | 272 +++++ vendor/golang.org/x/image/tiff/reader.go | 706 +++++++++++++ vendor/golang.org/x/image/tiff/writer.go | 438 ++++++++ vendor/gopkg.in/ini.v1/.travis.yml | 20 - vendor/modules.txt | 10 + 54 files changed, 7937 insertions(+), 24 deletions(-) create mode 100644 modules/setting/slideimage.go create mode 100644 modules/slideimage/slideimage.go create mode 100644 public/img/phone/phone.go create mode 100644 services/phone/phone.go create mode 100644 vendor/github.com/disintegration/imaging/.travis.yml create mode 100644 vendor/github.com/disintegration/imaging/LICENSE create mode 100644 vendor/github.com/disintegration/imaging/README.md create mode 100644 vendor/github.com/disintegration/imaging/adjust.go create mode 100644 vendor/github.com/disintegration/imaging/convolution.go create mode 100644 vendor/github.com/disintegration/imaging/doc.go create mode 100644 vendor/github.com/disintegration/imaging/effects.go create mode 100644 vendor/github.com/disintegration/imaging/go.mod create mode 100644 vendor/github.com/disintegration/imaging/go.sum create mode 100644 vendor/github.com/disintegration/imaging/histogram.go create mode 100644 vendor/github.com/disintegration/imaging/io.go create mode 100644 vendor/github.com/disintegration/imaging/resize.go create mode 100644 vendor/github.com/disintegration/imaging/scanner.go create mode 100644 vendor/github.com/disintegration/imaging/tools.go create mode 100644 vendor/github.com/disintegration/imaging/transform.go create mode 100644 vendor/github.com/disintegration/imaging/utils.go create mode 100644 vendor/golang.org/x/image/AUTHORS create mode 100644 vendor/golang.org/x/image/CONTRIBUTORS create mode 100644 vendor/golang.org/x/image/LICENSE create mode 100644 vendor/golang.org/x/image/PATENTS create mode 100644 vendor/golang.org/x/image/bmp/reader.go create mode 100644 vendor/golang.org/x/image/bmp/writer.go create mode 100644 vendor/golang.org/x/image/ccitt/reader.go create mode 100644 vendor/golang.org/x/image/ccitt/table.go create mode 100644 vendor/golang.org/x/image/ccitt/writer.go create mode 100644 vendor/golang.org/x/image/tiff/buffer.go create mode 100644 vendor/golang.org/x/image/tiff/compress.go create mode 100644 vendor/golang.org/x/image/tiff/consts.go create mode 100644 vendor/golang.org/x/image/tiff/fuzz.go create mode 100644 vendor/golang.org/x/image/tiff/lzw/reader.go create mode 100644 vendor/golang.org/x/image/tiff/reader.go create mode 100644 vendor/golang.org/x/image/tiff/writer.go delete mode 100644 vendor/gopkg.in/ini.v1/.travis.yml diff --git a/go.mod b/go.mod index 0a9d0226c..387a34520 100755 --- a/go.mod +++ b/go.mod @@ -25,6 +25,7 @@ require ( github.com/alibabacloud-go/darabonba-openapi v0.1.18 github.com/alibabacloud-go/dysmsapi-20170525/v2 v2.0.9 github.com/alibabacloud-go/tea v1.1.17 + github.com/alibabacloud-go/tea-utils v1.4.3 github.com/alibabacloud-go/tea-xml v1.1.2 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect github.com/blevesearch/bleve v1.0.7 @@ -35,6 +36,7 @@ require ( github.com/cznic/strutil v0.0.0-20181122101858-275e90344537 // indirect github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc github.com/dgrijalva/jwt-go v3.2.0+incompatible + github.com/disintegration/imaging v1.6.2 github.com/dustin/go-humanize v1.0.0 github.com/editorconfig/editorconfig-core-go/v2 v2.1.1 github.com/elliotchance/orderedmap v1.4.0 @@ -61,6 +63,7 @@ require ( github.com/golang/protobuf v1.4.1 // indirect github.com/gomodule/redigo v2.0.0+incompatible github.com/google/go-github/v24 v24.0.1 + github.com/google/uuid v1.1.1 github.com/gorilla/context v1.1.1 github.com/gorilla/websocket v1.4.0 github.com/hashicorp/go-retryablehttp v0.6.6 // indirect diff --git a/go.sum b/go.sum index 9293fd947..d55d7af48 100755 --- a/go.sum +++ b/go.sum @@ -201,6 +201,8 @@ github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xb github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= +github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= @@ -852,6 +854,8 @@ golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxT golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a h1:gHevYm0pO4QUbwy8Dmdr01R5r1BuKtfYqRqF0h/Cbh0= golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U= +golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181217174547-8f45f776aaf1/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= diff --git a/models/login_source.go b/models/login_source.go index ca2aa5a4d..5641e5c59 100755 --- a/models/login_source.go +++ b/models/login_source.go @@ -766,6 +766,14 @@ func UserSignIn(username, password string) (*User, error) { if err != nil { return nil, err } + //email和用户名方式没找到,用手机号查找 + if !hasUser { + user = &User{PhoneNumber: strings.TrimSpace(username)} + hasUser, err = x.Get(user) + if err != nil { + return nil, err + } + } if hasUser { switch user.LoginType { diff --git a/models/user.go b/models/user.go index 7d4c8ce34..5e7ddad86 100755 --- a/models/user.go +++ b/models/user.go @@ -183,6 +183,8 @@ type User struct { //Wechat WechatOpenId string `xorm:"INDEX"` WechatBindUnix timeutil.TimeStamp + //Mobile phone + PhoneNumber string `xorm:"UNIQUE"` } // SearchOrganizationsOptions options to filter organizations @@ -1441,6 +1443,31 @@ func getUserByName(e Engine, name string) (*User, error) { return u, nil } +func GetUserByPhoneNumber(phoneNumber string) (*User, error) { + return getUserByPhoneNumber(x, phoneNumber) +} + +func getUserByPhoneNumber(e Engine, phoneNumber string) (*User, error) { + u := &User{PhoneNumber: phoneNumber} + has, err := e.Get(u) + if err != nil { + return nil, err + } else if !has { + return nil, ErrUserNotExist{0, "", 0} + } + return u, nil +} + +func IsUserByPhoneNumberExist(phoneNumber string) (bool, error) { + return isUserByPhoneNumberExist(x, phoneNumber) + +} + +func isUserByPhoneNumberExist(e Engine, phoneNumber string) (bool, error) { + return e.Where("phone_number = ?", phoneNumber).Exist(&User{}) + +} + // GetUserEmailsByNames returns a list of e-mails corresponds to names of users // that have their email notifications set to enabled or onmention. func GetUserEmailsByNames(names []string) []string { diff --git a/modules/auth/user_form.go b/modules/auth/user_form.go index 86771b9f8..23e3e5b71 100755 --- a/modules/auth/user_form.go +++ b/modules/auth/user_form.go @@ -81,6 +81,8 @@ type RegisterForm struct { UserName string `binding:"Required;AlphaDashDot;MaxSize(40)"` Email string `binding:"Required;Email;MaxSize(254)"` Password string `binding:"MaxSize(255)"` + PhoneNumber string `binding:"MaxSize(20)"` + VerifyCode string `binding:"MaxSize(10)"` Retype string GRecaptchaResponse string `form:"g-recaptcha-response"` } @@ -209,6 +211,8 @@ type UpdateProfileForm struct { Location string `binding:"MaxSize(50)"` Language string `binding:"Size(5)"` Description string `binding:"MaxSize(255)"` + PhoneNumber string `binding:"MaxSize(20)"` + VerifyCode string `binding:"MaxSize(10)"` } // Validate validates the fields @@ -364,3 +368,32 @@ type U2FDeleteForm struct { func (f *U2FDeleteForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { return validate(errs, ctx.Data, f, ctx.Locale) } + +type PhoneNumberForm struct { + PhoneNumber string `binding:"Required;MaxSize(20)"` + IsSignUp bool `binding:"Required"` + SlideID string `binding:"Required;MaxSize(100)"` +} + +func (f *PhoneNumberForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { + return validate(errs, ctx.Data, f, ctx.Locale) +} + +type PhoneNumberCodeForm struct { + PhoneNumber string `binding:"Required;MaxSize(20)"` + VerifyCode string `binding:"Required;MaxSize(10)"` + Remember bool +} + +func (f *PhoneNumberCodeForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { + return validate(errs, ctx.Data, f, ctx.Locale) +} + +type SlideImageForm struct { + SlideID string `binding:"Required"` + X int `binding:"Required"` +} + +func (f *SlideImageForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { + return validate(errs, ctx.Data, f, ctx.Locale) +} diff --git a/modules/context/auth.go b/modules/context/auth.go index 287823dea..e296535d4 100755 --- a/modules/context/auth.go +++ b/modules/context/auth.go @@ -53,6 +53,10 @@ func Toggle(options *ToggleOptions) macaron.Handler { ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") ctx.HTML(200, "user/auth/prohibit_login") return + } else if ctx.User.IsActive && ctx.User.PhoneNumber == "" { + ctx.Data["Title"] = ctx.Tr("phone.bind_phone") + ctx.HTML(200, "user/auth/bind_phone") + return } if ctx.User.MustChangePassword { diff --git a/modules/public/dynamic.go b/modules/public/dynamic.go index 07f83e1f6..4f10895a1 100644 --- a/modules/public/dynamic.go +++ b/modules/public/dynamic.go @@ -7,10 +7,53 @@ package public import ( + "fmt" + "io/ioutil" + "os" + "path" + + "code.gitea.io/gitea/modules/setting" "gitea.com/macaron/macaron" + "github.com/unknwon/com" ) // Static implements the macaron static handler for serving assets. func Static(opts *Options) macaron.Handler { return opts.staticHandler(opts.Directory) } + +func Dir(name string) ([]string, error) { + + var ( + result []string + ) + + staticDir := path.Join(setting.StaticRootPath, "public", name) + + if com.IsDir(staticDir) { + files, err := com.StatDir(staticDir, true) + + if err != nil { + return []string{}, fmt.Errorf("Failed to read img directory. %v", err) + } + + result = append(result, files...) + } + + return result, nil +} + +func Asset(name string) ([]byte, error) { + + staticPath := path.Join(setting.StaticRootPath, "public", name) + + if com.IsFile(staticPath) { + f, err := os.Open(staticPath) + defer f.Close() + + if err == nil { + return ioutil.ReadAll(f) + } + } + return nil, fmt.Errorf("Asset file does not exist: %s", name) +} diff --git a/modules/public/static.go b/modules/public/static.go index 76050632c..6eac8a751 100644 --- a/modules/public/static.go +++ b/modules/public/static.go @@ -7,6 +7,7 @@ package public import ( + "fmt" "io/ioutil" "gitea.com/macaron/macaron" @@ -20,6 +21,16 @@ func Static(opts *Options) macaron.Handler { return opts.staticHandler("") } +func Dir(name string) ([]string, error) { + + files, err := AssetDir(name) + if err != nil { + return []string{}, fmt.Errorf("Failed to read embedded directory. %v", err) + } + + return files, nil +} + func Asset(name string) ([]byte, error) { f, err := Assets.Open("/" + name) if err != nil { @@ -29,6 +40,24 @@ func Asset(name string) ([]byte, error) { return ioutil.ReadAll(f) } +func AssetDir(dirName string) ([]string, error) { + d, err := Assets.Open(dirName) + if err != nil { + return nil, err + } + defer d.Close() + + files, err := d.Readdir(-1) + if err != nil { + return nil, err + } + var results = make([]string, 0, len(files)) + for _, file := range files { + results = append(results, file.Name()) + } + return results, nil +} + func AssetNames() []string { realFS := Assets.(vfsgen۰FS) var results = make([]string, 0, len(realFS)) diff --git a/modules/redis/redis_client/client.go b/modules/redis/redis_client/client.go index 437aecdae..f5b152c0b 100644 --- a/modules/redis/redis_client/client.go +++ b/modules/redis/redis_client/client.go @@ -41,6 +41,60 @@ func Setnx(key, value string, timeout time.Duration) (bool, error) { } +func SETNX(conn redis.Conn, key, value string, seconds int) (bool, error) { + reply, err := conn.Do("SET", key, value, "NX", "EX", seconds) + return redis.Bool(reply, err) + +} + +func HSETNX(conn redis.Conn, key, subKey string, value interface{}) error { + _, err := conn.Do("HSETNX", key, subKey, value) + return err + +} + +func HGET(conn redis.Conn, key, subKey string) (interface{}, error) { + return conn.Do("HGET", key, subKey) +} +func EXISTS(conn redis.Conn, key string) (bool, error) { + + reply, err := conn.Do("EXISTS", key) + return redis.Bool(reply, err) + +} + +func HEXISTS(conn redis.Conn, key string, subKey string) (bool, error) { + + reply, err := conn.Do("HEXISTS", key, subKey) + return redis.Bool(reply, err) + +} + +func Expire(conn redis.Conn, key string, seconds int) error { + _, err := conn.Do("EXPIRE", key, seconds) + return err + +} + +func HINCRBY(conn redis.Conn, key, subKey string, value int) error { + _, err := conn.Do("HINCRBY", key, subKey, value) + return err +} +func GET(conn redis.Conn, key string) (interface{}, error) { + return conn.Do("GET", key) +} + +func Ttl(conn redis.Conn, key string) (int, error) { + + reply, err := conn.Do("TTL", key) + if err != nil { + return 0, err + } + n, _ := strconv.Atoi(fmt.Sprint(reply)) + return n, nil + +} + func Get(key string) (string, error) { redisClient := labelmsg.Get() defer redisClient.Close() diff --git a/modules/setting/phone.go b/modules/setting/phone.go index 37f9d6c26..cbf088547 100644 --- a/modules/setting/phone.go +++ b/modules/setting/phone.go @@ -12,6 +12,7 @@ type Phone struct { SignName string TemplateCode string CodeTimeout int + ManualTimeout int RetryInterval int MaxRetryTimes int } @@ -25,6 +26,9 @@ func newPhoneService() { sec := Cfg.Section("phone") // Check phone setting. if !sec.Key("ENABLED").MustBool() { + PhoneService = &Phone{ + Enabled: sec.Key("ENABLED").MustBool(), + } return } diff --git a/modules/setting/slideimage.go b/modules/setting/slideimage.go new file mode 100644 index 000000000..c68beb185 --- /dev/null +++ b/modules/setting/slideimage.go @@ -0,0 +1,12 @@ +package setting + +import ( + "image" +) + +var ( + // the original images for generate slide image + SlideImagesBg []*image.Image + SlideMaskImage *image.Image + SlideImagesCount int +) diff --git a/modules/slideimage/slideimage.go b/modules/slideimage/slideimage.go new file mode 100644 index 000000000..302729649 --- /dev/null +++ b/modules/slideimage/slideimage.go @@ -0,0 +1,307 @@ +package slideimage + +import ( + "bytes" + "image" + "image/png" + "math" + "math/rand" + "path" + "strconv" + "strings" + "time" + + "code.gitea.io/gitea/modules/labelmsg" + "github.com/gomodule/redigo/redis" + + "code.gitea.io/gitea/modules/public" + + "code.gitea.io/gitea/modules/log" + + "code.gitea.io/gitea/modules/setting" + + "code.gitea.io/gitea/modules/redis/redis_client" + + "gitea.com/macaron/macaron" + "github.com/disintegration/imaging" + "github.com/google/uuid" +) + +type SlideImage struct { + SubURL string + URLPrefix string + SampleImages int + StdWidth int + StdHeight int + MaskSize int + ImageY int + ImageXStart int + MinImageX int + Expiration int + Tolerance int + CachePrefix string + CacheManualPrefix string +} + +type Options struct { + // Suburl path. Default is empty. + SubURL string + // URL prefix of getting captcha pictures. Default is "/slideimage/". + URLPrefix string + //Default is 4 + SampleImages int + //Image width default 391 + StdWidth int + //Image Height default 196 + StdHeight int + // default 51 + MaskSize int + // default 125 + ImageY int + //default 0 + ImageXStart int + //容忍的误差 default 2px + Tolerance int + // default 150 + MinImageX int + // default 600 seconds + Expiration int + //default slide: + CachePrefix string + //default mslide: 验证通过,在缓存中记录已进行过人工操作,然后在发送验证码之前再进行校验是否进行了人工操作。 + CacheManualPrefix string +} + +func NewSlideImage(opt Options) *SlideImage { + return &SlideImage{ + SubURL: opt.SubURL, + URLPrefix: opt.URLPrefix, + SampleImages: opt.SampleImages, + StdWidth: opt.StdWidth, + StdHeight: opt.StdHeight, + MaskSize: opt.MaskSize, + ImageY: opt.ImageY, + ImageXStart: opt.ImageXStart, + Tolerance: opt.Tolerance, + MinImageX: opt.MinImageX, + Expiration: opt.Expiration, + CachePrefix: opt.CachePrefix, + } +} + +func prepareOptions(options []Options) Options { + var opt Options + if len(options) > 0 { + opt = options[0] + } + + opt.SubURL = strings.TrimSuffix(opt.SubURL, "/") + + // Defaults. + if len(opt.URLPrefix) == 0 { + opt.URLPrefix = "/slideimage/" + } else if opt.URLPrefix[len(opt.URLPrefix)-1] != '/' { + opt.URLPrefix += "/" + } + if opt.SampleImages == 0 { + opt.SampleImages = 4 + } + if opt.StdWidth == 0 { + opt.StdWidth = 391 + } + if opt.StdHeight == 0 { + opt.StdHeight = 196 + } + if opt.MaskSize == 0 { + opt.MaskSize = 51 + } + if opt.ImageY == 0 { + opt.ImageY = 75 + } + if opt.ImageXStart == 0 { + opt.ImageXStart = 2 + } + + if opt.Tolerance == 0 { + opt.Tolerance = 2 + } + + if opt.MinImageX == 0 { + opt.MinImageX = 150 + } + if opt.Expiration == 0 { + opt.Expiration = 600 + } + + if len(opt.CachePrefix) == 0 { + opt.CachePrefix = "slide:" + } + if len(opt.CacheManualPrefix) == 0 { + opt.CacheManualPrefix = "mslide:" + } + + return opt +} + +func (s *SlideImage) key(id string) string { + return s.CachePrefix + id +} +func (s *SlideImage) mkey(id string) string { + return s.CacheManualPrefix + id +} + +func (s *SlideImage) VerifyManual(id string) (bool, error) { + redisConn := labelmsg.Get() + defer redisConn.Close() + return redis_client.EXISTS(redisConn, s.mkey(id)) +} + +func (s *SlideImage) Verify(id string, x int) bool { + redisConn := labelmsg.Get() + defer redisConn.Close() + + v, err := redis_client.GET(redisConn, s.key(id)) + v1, err := redis.String(v, err) + if err != nil { + log.Warn("redis err", err) + return false + } + if v1 == "" { + return false + } + values := strings.Split(v1, "-") + imageRandX, _ := strconv.Atoi(values[1]) + if int(math.Abs(float64(imageRandX-x))) <= s.Tolerance { + redis_client.SETNX(redisConn, s.mkey(id), "1", s.Expiration) + } + return false + +} + +func (s *SlideImage) CreateCode() (string, int, int) { + nums := rand.Intn(s.SampleImages) + imageId := uuid.New().String() + + //获取随机x坐标 + imageRandX := rand.Intn(s.StdWidth - s.MaskSize - 3) + if imageRandX < s.MinImageX { + imageRandX += s.MinImageX + } + redis_client.Setex(s.key(imageId), strconv.Itoa(nums)+"-"+strconv.Itoa(imageRandX), time.Second*time.Duration(s.Expiration)) + return imageId, nums, imageRandX +} + +func SlideImager(options ...Options) macaron.Handler { + return func(ctx *macaron.Context) { + slideImage := NewSlideImage(prepareOptions(options)) + + if strings.HasPrefix(ctx.Req.URL.Path, slideImage.URLPrefix) { + id := path.Base(ctx.Req.URL.Path) + if i := strings.Index(id, "."); i > -1 { + id = id[:i] + } + isScreenshot := strings.HasSuffix(id, "screenshot") + if isScreenshot { + id = strings.TrimSuffix(id, "screenshot") + } + + key := slideImage.key(id) + v, err := redis_client.Get(key) + + if err != nil || v == "" { + ctx.Status(404) + ctx.Write([]byte("not found")) + //png.Encode(ctx.Resp, *setting.SlideImagesBg[0]) + return + } + + values := strings.Split(v, "-") + + imageIndex, _ := strconv.Atoi(values[0]) + imageRandX, _ := strconv.Atoi(values[1]) + imageBg := setting.SlideImagesBg[imageIndex] + + maxPotion := image.Point{ + X: imageRandX + slideImage.MaskSize, + Y: slideImage.ImageY + slideImage.MaskSize, + } + minPotion := image.Point{ + X: imageRandX, + Y: slideImage.ImageY, + } + subimg := image.Rectangle{ + Max: maxPotion, + Min: minPotion, + } + + if isScreenshot { + data := imaging.Crop(*imageBg, subimg) + png.Encode(ctx.Resp, data) + + } else { + data := imaging.Overlay(*imageBg, *setting.SlideMaskImage, minPotion, 1.0) + png.Encode(ctx.Resp, data) + } + ctx.Status(200) + return + + } + ctx.Data["SlideImageInfo"] = slideImage + ctx.Data["EnablePhone"] = setting.PhoneService.Enabled + ctx.Map(slideImage) + + } +} + +func InitSlideImage() { + if setting.PhoneService.Enabled { + + filenames, err := public.Dir(path.Join("img", "slide", "bg")) + if err != nil { + panic("Slide Image Service init failed") + } + maskFileName, err := public.Dir(path.Join("img", "slide", "mask")) + if err != nil { + panic("Slide Image Service init failed") + } + + for _, filename := range filenames { + if strings.HasSuffix(filename, ".png") { + content, err := public.Asset(path.Join("img", "slide", "bg", filename)) + + if err != nil { + log.Warn("can not open "+filename, err) + continue + } + + m, err := png.Decode(bytes.NewReader(content)) + + if err != nil { + log.Warn("can not decode "+filename, err) + continue + } + setting.SlideImagesBg = append(setting.SlideImagesBg, &m) + } + + } + setting.SlideImagesCount = len(setting.SlideImagesBg) + if setting.SlideImagesCount == 0 { + panic("Slide Image Service init failed") + } + + maskContent, err := public.Asset(path.Join("img", "slide", "mask", maskFileName[0])) + + if err != nil { + panic("Slide Image Service init failed") + } + + MaskImage, err := png.Decode(bytes.NewReader(maskContent)) + if err != nil { + panic("Slide Image Service init failed") + } + setting.SlideMaskImage = &MaskImage + + log.Info("Slide Image Service Enabled") + + } +} diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index c52a369ce..52878cd7f 100755 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -374,6 +374,19 @@ authorization_failed = Authorization failed authorization_failed_desc = The authorization failed because we detected an invalid request. Please contact the maintainer of the app you've tried to authorize. disable_forgot_password_mail = Account recovery is disabled. Please contact your site administrator. sspi_auth_failed = SSPI authentication failed +[phone] +format_err=The format of phone number is wrong. +query_err=Fail to query phone number, can not send verify code, please try again later. +already_register=The phone number is already registered. +not_register=The phone number is not registered. +not_modify=The phone number is not updated. +max_times=One phone number can not send verify code more than %s times. +too_fast=Send too frequently, please try again later. +manual_first=Please slide to finish the jigsaw first. +verify_code_fail=Please input right verify code. +bind_phone=Please Bind Your Phone +bind_phone_fail=Fail to bind phone number, please try again later. + [mail] activate_account = Please activate your account diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index cb1c7565a..875c1d5ba 100755 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -378,6 +378,22 @@ authorization_failed=授权失败 authorization_failed_desc=授权失败,这是一个无效的请求。请联系尝试授权应用的管理员。 disable_forgot_password_mail = Account recovery is disabled. Please contact your site administrator. sspi_auth_failed=SSPI 认证失败 +[phone] +format_err=手机号格式错误。 +query_err=查询手机号失败,无法发送,请稍后再试。 +already_register=手机号已被注册 +not_register=手机号未注册 +not_modify=手机号未修改 +max_times=一个手机号发送验证码次数每天不能超过%s次。 +too_fast=验证码发送太频繁,请稍后再试。 +manual_first=请先拖动滑块填充拼图。 +verify_code_fail=请输入正确的短信验证码。 +bind_phone=请绑定手机号 +bind_phone_fail=绑定手机号失败,请稍后再试。 + + + + [mail] activate_account=请激活您的帐户 diff --git a/public/img/phone/phone.go b/public/img/phone/phone.go new file mode 100644 index 000000000..a617ab7e7 --- /dev/null +++ b/public/img/phone/phone.go @@ -0,0 +1,98 @@ +package phone + +import ( + "fmt" + "time" + + "code.gitea.io/gitea/modules/log" + + "code.gitea.io/gitea/modules/phone" + "code.gitea.io/gitea/modules/redis/redis_client" + "code.gitea.io/gitea/modules/setting" + + "github.com/gomodule/redigo/redis" +) + +//验证码存储前缀 使用时%s用手机号替代 +const CODE_PREFIX = "P_C:%s" + +//手机号发送验证码次数Hkey,%s对应日期, 存储在hset中,值是hashet,记录手机号和发送次数 +const TIMES_PREFIX = "P_T:%s" + +func GetPhoneNumberSendTimes(conn redis.Conn, phoneNumber string) (int, error) { + i, err := redis_client.HGET(conn, GetPhoneTimesHKey(), phoneNumber) + + return redis.Int(i, err) +} + +func GetPhoneCodeTTL(conn redis.Conn, phoneNumber string) (int, error) { + return redis_client.Ttl(conn, GetPhoneCodeKey(phoneNumber)) + +} +func SendVerifyCode(conn redis.Conn, phoneNumber string) error { + timesKey := GetPhoneTimesHKey() + exists, err := redis_client.EXISTS(conn, timesKey) + if err != nil { + return err + } + code := phone.GenerateVerifyCode(setting.PhoneService.VerifyCodeLength) + err = phone.SendVerifyCode(phoneNumber, code) + if err != nil { + return err + } + redis_client.SETNX(conn, GetPhoneCodeKey(phoneNumber), code, setting.PhoneService.CodeTimeout) + if !exists { + err = redis_client.HSETNX(conn, timesKey, phoneNumber, 1) + if err != nil { + return err + } + err = redis_client.Expire(conn, timesKey, getRemainSecondOfDay(time.Now())) + if err != nil { + return err + } + + } else { + timesPhoneExists, err := redis_client.HEXISTS(conn, timesKey, phoneNumber) + if err != nil { + return err + } + + if timesPhoneExists { + err = redis_client.HINCRBY(conn, timesKey, phoneNumber, 1) + } else { + err = redis_client.HSETNX(conn, timesKey, phoneNumber, 1) + } + if err != nil { + return err + } + + } + return nil + +} + +func IsVerifyCodeRight(phoneNumer string, verifyCode string) bool { + if phoneNumer == "" { + return false + } + value, err := redis_client.Get(GetPhoneCodeKey(phoneNumer)) + if err != nil { + log.Warn("redis err", err) + return false + } else { + return value == verifyCode + } +} + +func GetPhoneCodeKey(phoneNumber string) string { + return fmt.Sprintf(CODE_PREFIX, phoneNumber) +} + +func GetPhoneTimesHKey() string { + today := time.Now().Format("2006-01-02") + return fmt.Sprintf(TIMES_PREFIX, today) +} + +func getRemainSecondOfDay(t time.Time) int { + return 86400 - 60*60*t.Hour() + 60*t.Minute() + t.Second() +} diff --git a/routers/init.go b/routers/init.go index eab513c78..7470b1922 100755 --- a/routers/init.go +++ b/routers/init.go @@ -10,6 +10,8 @@ import ( "strings" "time" + "code.gitea.io/gitea/modules/slideimage" + "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/migrations" "code.gitea.io/gitea/modules/auth/sso" @@ -57,6 +59,7 @@ func checkRunMode() { // NewServices init new services func NewServices() { setting.NewServices() + slideimage.InitSlideImage() if err := storage.Init(); err != nil { log.Fatal("storage init failed: %v", err) } diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 7ba8fe61a..a4127314a 100755 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -12,6 +12,8 @@ import ( "text/template" "time" + "code.gitea.io/gitea/modules/slideimage" + "code.gitea.io/gitea/routers/image" "code.gitea.io/gitea/routers/authentication" @@ -233,6 +235,10 @@ func NewMacaron() *macaron.Macaron { m.Use(captcha.Captchaer(captcha.Options{ SubURL: setting.AppSubURL, })) + m.Use(slideimage.SlideImager(slideimage.Options{ + SubURL: setting.AppSubURL, + SampleImages: setting.SlideImagesCount, + })) m.Use(session.Sessioner(session.Options{ Provider: setting.SessionConfig.Provider, ProviderConfig: setting.SessionConfig.ProviderConfig, @@ -363,6 +369,9 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("/login", user.SignIn) m.Get("/login/cloud_brain", user.SignInCloudBrain) m.Post("/login", bindIgnErr(auth.SignInForm{}), user.SignInPost) + + m.Get("/login/phone", user.SignInPhone) + m.Post("/login/phone", bindIgnErr(auth.PhoneNumberCodeForm{}), user.SignInPhonePost) m.Group("", func() { m.Combo("/login/openid"). Get(user.SignInOpenID). @@ -402,6 +411,10 @@ func RegisterRoutes(m *macaron.Macaron) { }, reqSignOut) m.Any("/user/events", reqSignIn, events.Events) + m.Get("/slideImage", user.CreateSlideImageInfo) + m.Post("/verifySlideImage", bindIgnErr(auth.SlideImageForm{}), user.VerifySlideImage) + m.Post("/sendVerifyCode", bindIgnErr(auth.PhoneNumberForm{}), user.SendVerifyCode) + m.Post("/bindPhone", reqSignIn, bindIgnErr(auth.PhoneNumberCodeForm{}), user.BindPhone) m.Group("/login/oauth", func() { m.Get("/authorize", bindIgnErr(auth.AuthorizationForm{}), user.AuthorizeOAuth) diff --git a/routers/user/auth.go b/routers/user/auth.go index a02cf24dc..c3af2b8d4 100755 --- a/routers/user/auth.go +++ b/routers/user/auth.go @@ -11,6 +11,14 @@ import ( "net/http" "strings" + "code.gitea.io/gitea/modules/slideimage" + + phoneService "code.gitea.io/gitea/services/phone" + + "code.gitea.io/gitea/modules/labelmsg" + + "code.gitea.io/gitea/modules/phone" + "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/auth/oauth2" @@ -38,6 +46,7 @@ const ( tplSignIn base.TplName = "user/auth/signin" // tplSignIn template for sign in page tplSignInCloudBrain base.TplName = "user/auth/signin_cloud_brain" + tplSignInPhone base.TplName = "user/auth/signin_phone" // tplSignUp template path for sign up page tplSignUp base.TplName = "user/auth/signup" // TplActivate template path for activate user @@ -176,6 +185,55 @@ func SignInCloudBrain(ctx *context.Context) { ctx.HTML(200, tplSignInCloudBrain) } +func SignInPhone(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("sign_in") + // Check auto-login. + if checkAutoLogin(ctx) { + return + } + + ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login" + ctx.Data["PageIsSignIn"] = true + ctx.Data["PageIsPhoneLogin"] = true + + ctx.HTML(200, tplSignInPhone) +} + +func SignInPhonePost(ctx *context.Context, form auth.PhoneNumberCodeForm) { + ctx.Data["Title"] = ctx.Tr("sign_in") + ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login" + ctx.Data["PageIsSignIn"] = true + ctx.Data["PageIsLogin"] = true + ctx.Data["EnablePhone"] = setting.PhoneService.Enabled + ctx.Data["Title"] = ctx.Tr("sign_in") + ctx.Data["IsCourse"] = ctx.QueryBool("course") + + if ctx.HasError() { + ctx.HTML(200, tplSignInPhone) + return + } + + if !phoneService.IsVerifyCodeRight(strings.TrimSpace(form.PhoneNumber), strings.TrimSpace(form.VerifyCode)) { + ctx.RenderWithErr(ctx.Tr("phone.verify_code_fail"), tplSignInPhone, &form) + } + + u, err := models.GetUserByPhoneNumber(strings.TrimSpace(form.PhoneNumber)) + + if err != nil { + if models.IsErrUserNotExist(err) { + ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplSignInPhone, &form) + log.Info("Failed authentication attempt for %s from %s", form.PhoneNumber, ctx.RemoteAddr()) + } else { + ctx.ServerError("UserSignIn", err) + } + return + } + models.SaveLoginInfoToDb(ctx.Req.Request, u) + + handleSignIn(ctx, u, form.Remember) + +} + // SignInPost response for sign in request func SignInPost(ctx *context.Context, form auth.SignInForm) { ctx.Data["Title"] = ctx.Tr("sign_in") @@ -1155,11 +1213,21 @@ func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterFo return } + if setting.PhoneService.Enabled { + phoneNumber := strings.TrimSpace(form.PhoneNumber) + verifyCode := strings.TrimSpace(form.VerifyCode) + if !phoneService.IsVerifyCodeRight(phoneNumber, verifyCode) { + ctx.RenderWithErr(ctx.Tr("phone.verify_code_fail"), tplSignUp, &form) + return + } + } + u := &models.User{ - Name: form.UserName, - Email: form.Email, - Passwd: form.Password, - IsActive: !setting.Service.RegisterEmailConfirm, + Name: form.UserName, + Email: form.Email, + Passwd: form.Password, + PhoneNumber: strings.TrimSpace(form.PhoneNumber), + IsActive: !setting.Service.RegisterEmailConfirm, } if err := models.CreateUser(u); err != nil { switch { @@ -1612,3 +1680,134 @@ func MustChangePasswordPost(ctx *context.Context, cpt *captcha.Captcha, form aut ctx.Redirect(setting.AppSubURL + "/") } + +func CreateSlideImageInfo(ctx *context.Context, slideImage *slideimage.SlideImage) { + id, _, _ := slideImage.CreateCode() + ctx.JSON(http.StatusOK, models.BaseMessage{0, id}) +} + +func VerifySlideImage(ctx *context.Context, slideImage *slideimage.SlideImage, form auth.SlideImageForm) { + if slideImage.Verify(form.SlideID, form.X) { + ctx.JSON(http.StatusOK, models.BaseOKMessage) + } else { + ctx.JSON(http.StatusOK, models.BaseErrorMessage("")) + } +} + +func BindPhone(ctx *context.Context, form auth.PhoneNumberCodeForm) { + if strings.TrimSpace(form.PhoneNumber) != "" && strings.TrimSpace(form.VerifyCode) != "" && phoneService.IsVerifyCodeRight(strings.TrimSpace(form.PhoneNumber), strings.TrimSpace(form.VerifyCode)) { + + ctx.User.PhoneNumber = strings.TrimSpace(form.PhoneNumber) + if err := models.UpdateUserSetting(ctx.User); err != nil { + ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.bind_phone_fail"))) + return + } + ctx.JSON(http.StatusOK, models.BaseOKMessage) + return + } + + ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.verify_code_fail"))) + +} + +func SendVerifyCode(ctx *context.Context, slideImage *slideimage.SlideImage, form auth.PhoneNumberForm) { + phoneNumber := strings.TrimSpace(form.PhoneNumber) + + if !phone.IsValidPhoneNumber(phoneNumber) { + ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.format_err"))) + return + } + + hasManual, err := slideImage.VerifyManual(form.SlideID) + if err != nil { + log.Warn("redis err", err) + ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.query_err"))) + return + + } + if !hasManual { + ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.query_err"))) + return + } + + if !ctx.IsSigned { + has, err := models.IsUserByPhoneNumberExist(phoneNumber) + if err != nil { + log.Warn("sql err", err) + ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.query_err"))) + return + } + + if form.IsSignUp { //注册 + + if has { + ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.already_register"))) + return + } + } else { //手机号验证码登录 + if !has { + ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.not_register"))) + return + } + + } + + } else { //修改手机号 + u, err := models.GetUserByPhoneNumber(phoneNumber) + if err != nil && !models.IsErrUserNotExist(err) { + log.Warn("sql err", err) + ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.query_err"))) + return + } + + if u != nil { + + if u.ID == ctx.User.ID { //没有修改手机号 + ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.not_modify"))) + return + } else { //修改的手机已经被别的用户注册 + ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.already_register"))) + return + } + + } + + } + + redisConn := labelmsg.Get() + defer redisConn.Close() + + sendTimes, err := phoneService.GetPhoneNumberSendTimes(redisConn, phoneNumber) + if err != nil { + log.Warn("redis err", err) + ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.query_err"))) + return + + } + if sendTimes >= setting.PhoneService.MaxRetryTimes { + ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.max_times", setting.PhoneService.MaxRetryTimes))) + return + + } + + ttl, err := phoneService.GetPhoneCodeTTL(redisConn, phoneNumber) + if err != nil { + log.Warn("redis err", err) + ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.query_err"))) + return + + } + if setting.PhoneService.CodeTimeout-ttl < setting.PhoneService.RetryInterval { + ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.too_fast"))) + return + } + err = phoneService.SendVerifyCode(redisConn, phoneNumber) + if err != nil { + log.Warn("send code or redis err", err) + ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.query_err"))) + return + } + + ctx.JSON(http.StatusOK, models.BaseOKMessage) + +} diff --git a/routers/user/setting/profile.go b/routers/user/setting/profile.go index 3333a8cc4..aade07f2f 100755 --- a/routers/user/setting/profile.go +++ b/routers/user/setting/profile.go @@ -11,6 +11,8 @@ import ( "io/ioutil" "strings" + phoneService "code.gitea.io/gitea/services/phone" + "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" @@ -89,6 +91,18 @@ func ProfilePost(ctx *context.Context, form auth.UpdateProfileForm) { return } + if setting.PhoneService.Enabled { + if strings.TrimSpace(form.PhoneNumber) != ctx.User.PhoneNumber { + if phoneService.IsVerifyCodeRight(strings.TrimSpace(form.PhoneNumber), strings.TrimSpace(form.VerifyCode)) { + ctx.User.PhoneNumber = strings.TrimSpace(form.PhoneNumber) + } else { + ctx.Flash.Error(ctx.Tr("phone.verify_code_fail")) + ctx.Redirect(setting.AppSubURL + "/user/settings") + return + } + } + } + ctx.User.FullName = form.FullName ctx.User.KeepEmailPrivate = form.KeepEmailPrivate diff --git a/services/phone/phone.go b/services/phone/phone.go new file mode 100644 index 000000000..a617ab7e7 --- /dev/null +++ b/services/phone/phone.go @@ -0,0 +1,98 @@ +package phone + +import ( + "fmt" + "time" + + "code.gitea.io/gitea/modules/log" + + "code.gitea.io/gitea/modules/phone" + "code.gitea.io/gitea/modules/redis/redis_client" + "code.gitea.io/gitea/modules/setting" + + "github.com/gomodule/redigo/redis" +) + +//验证码存储前缀 使用时%s用手机号替代 +const CODE_PREFIX = "P_C:%s" + +//手机号发送验证码次数Hkey,%s对应日期, 存储在hset中,值是hashet,记录手机号和发送次数 +const TIMES_PREFIX = "P_T:%s" + +func GetPhoneNumberSendTimes(conn redis.Conn, phoneNumber string) (int, error) { + i, err := redis_client.HGET(conn, GetPhoneTimesHKey(), phoneNumber) + + return redis.Int(i, err) +} + +func GetPhoneCodeTTL(conn redis.Conn, phoneNumber string) (int, error) { + return redis_client.Ttl(conn, GetPhoneCodeKey(phoneNumber)) + +} +func SendVerifyCode(conn redis.Conn, phoneNumber string) error { + timesKey := GetPhoneTimesHKey() + exists, err := redis_client.EXISTS(conn, timesKey) + if err != nil { + return err + } + code := phone.GenerateVerifyCode(setting.PhoneService.VerifyCodeLength) + err = phone.SendVerifyCode(phoneNumber, code) + if err != nil { + return err + } + redis_client.SETNX(conn, GetPhoneCodeKey(phoneNumber), code, setting.PhoneService.CodeTimeout) + if !exists { + err = redis_client.HSETNX(conn, timesKey, phoneNumber, 1) + if err != nil { + return err + } + err = redis_client.Expire(conn, timesKey, getRemainSecondOfDay(time.Now())) + if err != nil { + return err + } + + } else { + timesPhoneExists, err := redis_client.HEXISTS(conn, timesKey, phoneNumber) + if err != nil { + return err + } + + if timesPhoneExists { + err = redis_client.HINCRBY(conn, timesKey, phoneNumber, 1) + } else { + err = redis_client.HSETNX(conn, timesKey, phoneNumber, 1) + } + if err != nil { + return err + } + + } + return nil + +} + +func IsVerifyCodeRight(phoneNumer string, verifyCode string) bool { + if phoneNumer == "" { + return false + } + value, err := redis_client.Get(GetPhoneCodeKey(phoneNumer)) + if err != nil { + log.Warn("redis err", err) + return false + } else { + return value == verifyCode + } +} + +func GetPhoneCodeKey(phoneNumber string) string { + return fmt.Sprintf(CODE_PREFIX, phoneNumber) +} + +func GetPhoneTimesHKey() string { + today := time.Now().Format("2006-01-02") + return fmt.Sprintf(TIMES_PREFIX, today) +} + +func getRemainSecondOfDay(t time.Time) int { + return 86400 - 60*60*t.Hour() + 60*t.Minute() + t.Second() +} diff --git a/vendor/github.com/disintegration/imaging/.travis.yml b/vendor/github.com/disintegration/imaging/.travis.yml new file mode 100644 index 000000000..7ae5e4b25 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/.travis.yml @@ -0,0 +1,12 @@ +language: go +go: + - "1.10.x" + - "1.11.x" + - "1.12.x" + +before_install: + - go get github.com/mattn/goveralls + +script: + - go test -v -race -cover + - $GOPATH/bin/goveralls -service=travis-ci diff --git a/vendor/github.com/disintegration/imaging/LICENSE b/vendor/github.com/disintegration/imaging/LICENSE new file mode 100644 index 000000000..a4144a9d2 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2012 Grigory Dryapak + +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/disintegration/imaging/README.md b/vendor/github.com/disintegration/imaging/README.md new file mode 100644 index 000000000..a1fd764d2 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/README.md @@ -0,0 +1,226 @@ +# Imaging + +[![GoDoc](https://godoc.org/github.com/disintegration/imaging?status.svg)](https://godoc.org/github.com/disintegration/imaging) +[![Build Status](https://travis-ci.org/disintegration/imaging.svg?branch=master)](https://travis-ci.org/disintegration/imaging) +[![Coverage Status](https://coveralls.io/repos/github/disintegration/imaging/badge.svg?branch=master&service=github)](https://coveralls.io/github/disintegration/imaging?branch=master) +[![Go Report Card](https://goreportcard.com/badge/github.com/disintegration/imaging)](https://goreportcard.com/report/github.com/disintegration/imaging) + +Package imaging provides basic image processing functions (resize, rotate, crop, brightness/contrast adjustments, etc.). + +All the image processing functions provided by the package accept any image type that implements `image.Image` interface +as an input, and return a new image of `*image.NRGBA` type (32bit RGBA colors, non-premultiplied alpha). + +## Installation + + go get -u github.com/disintegration/imaging + +## Documentation + +http://godoc.org/github.com/disintegration/imaging + +## Usage examples + +A few usage examples can be found below. See the documentation for the full list of supported functions. + +### Image resizing + +```go +// Resize srcImage to size = 128x128px using the Lanczos filter. +dstImage128 := imaging.Resize(srcImage, 128, 128, imaging.Lanczos) + +// Resize srcImage to width = 800px preserving the aspect ratio. +dstImage800 := imaging.Resize(srcImage, 800, 0, imaging.Lanczos) + +// Scale down srcImage to fit the 800x600px bounding box. +dstImageFit := imaging.Fit(srcImage, 800, 600, imaging.Lanczos) + +// Resize and crop the srcImage to fill the 100x100px area. +dstImageFill := imaging.Fill(srcImage, 100, 100, imaging.Center, imaging.Lanczos) +``` + +Imaging supports image resizing using various resampling filters. The most notable ones: +- `Lanczos` - A high-quality resampling filter for photographic images yielding sharp results. +- `CatmullRom` - A sharp cubic filter that is faster than Lanczos filter while providing similar results. +- `MitchellNetravali` - A cubic filter that produces smoother results with less ringing artifacts than CatmullRom. +- `Linear` - Bilinear resampling filter, produces smooth output. Faster than cubic filters. +- `Box` - Simple and fast averaging filter appropriate for downscaling. When upscaling it's similar to NearestNeighbor. +- `NearestNeighbor` - Fastest resampling filter, no antialiasing. + +The full list of supported filters: NearestNeighbor, Box, Linear, Hermite, MitchellNetravali, CatmullRom, BSpline, Gaussian, Lanczos, Hann, Hamming, Blackman, Bartlett, Welch, Cosine. Custom filters can be created using ResampleFilter struct. + +**Resampling filters comparison** + +Original image: + +![srcImage](testdata/branches.png) + +The same image resized from 600x400px to 150x100px using different resampling filters. +From faster (lower quality) to slower (higher quality): + +Filter | Resize result +--------------------------|--------------------------------------------- +`imaging.NearestNeighbor` | ![dstImage](testdata/out_resize_nearest.png) +`imaging.Linear` | ![dstImage](testdata/out_resize_linear.png) +`imaging.CatmullRom` | ![dstImage](testdata/out_resize_catrom.png) +`imaging.Lanczos` | ![dstImage](testdata/out_resize_lanczos.png) + + +### Gaussian Blur + +```go +dstImage := imaging.Blur(srcImage, 0.5) +``` + +Sigma parameter allows to control the strength of the blurring effect. + +Original image | Sigma = 0.5 | Sigma = 1.5 +-----------------------------------|----------------------------------------|--------------------------------------- +![srcImage](testdata/flowers_small.png) | ![dstImage](testdata/out_blur_0.5.png) | ![dstImage](testdata/out_blur_1.5.png) + +### Sharpening + +```go +dstImage := imaging.Sharpen(srcImage, 0.5) +``` + +`Sharpen` uses gaussian function internally. Sigma parameter allows to control the strength of the sharpening effect. + +Original image | Sigma = 0.5 | Sigma = 1.5 +-----------------------------------|-------------------------------------------|------------------------------------------ +![srcImage](testdata/flowers_small.png) | ![dstImage](testdata/out_sharpen_0.5.png) | ![dstImage](testdata/out_sharpen_1.5.png) + +### Gamma correction + +```go +dstImage := imaging.AdjustGamma(srcImage, 0.75) +``` + +Original image | Gamma = 0.75 | Gamma = 1.25 +-----------------------------------|------------------------------------------|----------------------------------------- +![srcImage](testdata/flowers_small.png) | ![dstImage](testdata/out_gamma_0.75.png) | ![dstImage](testdata/out_gamma_1.25.png) + +### Contrast adjustment + +```go +dstImage := imaging.AdjustContrast(srcImage, 20) +``` + +Original image | Contrast = 15 | Contrast = -15 +-----------------------------------|--------------------------------------------|------------------------------------------- +![srcImage](testdata/flowers_small.png) | ![dstImage](testdata/out_contrast_p15.png) | ![dstImage](testdata/out_contrast_m15.png) + +### Brightness adjustment + +```go +dstImage := imaging.AdjustBrightness(srcImage, 20) +``` + +Original image | Brightness = 10 | Brightness = -10 +-----------------------------------|----------------------------------------------|--------------------------------------------- +![srcImage](testdata/flowers_small.png) | ![dstImage](testdata/out_brightness_p10.png) | ![dstImage](testdata/out_brightness_m10.png) + +### Saturation adjustment + +```go +dstImage := imaging.AdjustSaturation(srcImage, 20) +``` + +Original image | Saturation = 30 | Saturation = -30 +-----------------------------------|----------------------------------------------|--------------------------------------------- +![srcImage](testdata/flowers_small.png) | ![dstImage](testdata/out_saturation_p30.png) | ![dstImage](testdata/out_saturation_m30.png) + +## FAQ + +### Incorrect image orientation after processing (e.g. an image appears rotated after resizing) + +Most probably, the given image contains the EXIF orientation tag. +The stadard `image/*` packages do not support loading and saving +this kind of information. To fix the issue, try opening images with +the `AutoOrientation` decode option. If this option is set to `true`, +the image orientation is changed after decoding, according to the +orientation tag (if present). Here's the example: + +```go +img, err := imaging.Open("test.jpg", imaging.AutoOrientation(true)) +``` + +### What's the difference between `imaging` and `gift` packages? + +[imaging](https://github.com/disintegration/imaging) +is designed to be a lightweight and simple image manipulation package. +It provides basic image processing functions and a few helper functions +such as `Open` and `Save`. It consistently returns *image.NRGBA image +type (8 bits per channel, RGBA). + +[gift](https://github.com/disintegration/gift) +supports more advanced image processing, for example, sRGB/Linear color +space conversions. It also supports different output image types +(e.g. 16 bits per channel) and provides easy-to-use API for chaining +multiple processing steps together. + +## Example code + +```go +package main + +import ( + "image" + "image/color" + "log" + + "github.com/disintegration/imaging" +) + +func main() { + // Open a test image. + src, err := imaging.Open("testdata/flowers.png") + if err != nil { + log.Fatalf("failed to open image: %v", err) + } + + // Crop the original image to 300x300px size using the center anchor. + src = imaging.CropAnchor(src, 300, 300, imaging.Center) + + // Resize the cropped image to width = 200px preserving the aspect ratio. + src = imaging.Resize(src, 200, 0, imaging.Lanczos) + + // Create a blurred version of the image. + img1 := imaging.Blur(src, 5) + + // Create a grayscale version of the image with higher contrast and sharpness. + img2 := imaging.Grayscale(src) + img2 = imaging.AdjustContrast(img2, 20) + img2 = imaging.Sharpen(img2, 2) + + // Create an inverted version of the image. + img3 := imaging.Invert(src) + + // Create an embossed version of the image using a convolution filter. + img4 := imaging.Convolve3x3( + src, + [9]float64{ + -1, -1, 0, + -1, 1, 1, + 0, 1, 1, + }, + nil, + ) + + // Create a new image and paste the four produced images into it. + dst := imaging.New(400, 400, color.NRGBA{0, 0, 0, 0}) + dst = imaging.Paste(dst, img1, image.Pt(0, 0)) + dst = imaging.Paste(dst, img2, image.Pt(0, 200)) + dst = imaging.Paste(dst, img3, image.Pt(200, 0)) + dst = imaging.Paste(dst, img4, image.Pt(200, 200)) + + // Save the resulting image as JPEG. + err = imaging.Save(dst, "testdata/out_example.jpg") + if err != nil { + log.Fatalf("failed to save image: %v", err) + } +} +``` + +Output: + +![dstImage](testdata/out_example.jpg) diff --git a/vendor/github.com/disintegration/imaging/adjust.go b/vendor/github.com/disintegration/imaging/adjust.go new file mode 100644 index 000000000..daaf1de86 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/adjust.go @@ -0,0 +1,253 @@ +package imaging + +import ( + "image" + "image/color" + "math" +) + +// Grayscale produces a grayscale version of the image. +func Grayscale(img image.Image) *image.NRGBA { + src := newScanner(img) + dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h)) + parallel(0, src.h, func(ys <-chan int) { + for y := range ys { + i := y * dst.Stride + src.scan(0, y, src.w, y+1, dst.Pix[i:i+src.w*4]) + for x := 0; x < src.w; x++ { + d := dst.Pix[i : i+3 : i+3] + r := d[0] + g := d[1] + b := d[2] + f := 0.299*float64(r) + 0.587*float64(g) + 0.114*float64(b) + y := uint8(f + 0.5) + d[0] = y + d[1] = y + d[2] = y + i += 4 + } + } + }) + return dst +} + +// Invert produces an inverted (negated) version of the image. +func Invert(img image.Image) *image.NRGBA { + src := newScanner(img) + dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h)) + parallel(0, src.h, func(ys <-chan int) { + for y := range ys { + i := y * dst.Stride + src.scan(0, y, src.w, y+1, dst.Pix[i:i+src.w*4]) + for x := 0; x < src.w; x++ { + d := dst.Pix[i : i+3 : i+3] + d[0] = 255 - d[0] + d[1] = 255 - d[1] + d[2] = 255 - d[2] + i += 4 + } + } + }) + return dst +} + +// AdjustSaturation changes the saturation of the image using the percentage parameter and returns the adjusted image. +// The percentage must be in the range (-100, 100). +// The percentage = 0 gives the original image. +// The percentage = 100 gives the image with the saturation value doubled for each pixel. +// The percentage = -100 gives the image with the saturation value zeroed for each pixel (grayscale). +// +// Examples: +// dstImage = imaging.AdjustSaturation(srcImage, 25) // Increase image saturation by 25%. +// dstImage = imaging.AdjustSaturation(srcImage, -10) // Decrease image saturation by 10%. +// +func AdjustSaturation(img image.Image, percentage float64) *image.NRGBA { + percentage = math.Min(math.Max(percentage, -100), 100) + multiplier := 1 + percentage/100 + + return AdjustFunc(img, func(c color.NRGBA) color.NRGBA { + h, s, l := rgbToHSL(c.R, c.G, c.B) + s *= multiplier + if s > 1 { + s = 1 + } + r, g, b := hslToRGB(h, s, l) + return color.NRGBA{r, g, b, c.A} + }) +} + +// AdjustContrast changes the contrast of the image using the percentage parameter and returns the adjusted image. +// The percentage must be in range (-100, 100). The percentage = 0 gives the original image. +// The percentage = -100 gives solid gray image. +// +// Examples: +// +// dstImage = imaging.AdjustContrast(srcImage, -10) // Decrease image contrast by 10%. +// dstImage = imaging.AdjustContrast(srcImage, 20) // Increase image contrast by 20%. +// +func AdjustContrast(img image.Image, percentage float64) *image.NRGBA { + percentage = math.Min(math.Max(percentage, -100.0), 100.0) + lut := make([]uint8, 256) + + v := (100.0 + percentage) / 100.0 + for i := 0; i < 256; i++ { + switch { + case 0 <= v && v <= 1: + lut[i] = clamp((0.5 + (float64(i)/255.0-0.5)*v) * 255.0) + case 1 < v && v < 2: + lut[i] = clamp((0.5 + (float64(i)/255.0-0.5)*(1/(2.0-v))) * 255.0) + default: + lut[i] = uint8(float64(i)/255.0+0.5) * 255 + } + } + + return adjustLUT(img, lut) +} + +// AdjustBrightness changes the brightness of the image using the percentage parameter and returns the adjusted image. +// The percentage must be in range (-100, 100). The percentage = 0 gives the original image. +// The percentage = -100 gives solid black image. The percentage = 100 gives solid white image. +// +// Examples: +// +// dstImage = imaging.AdjustBrightness(srcImage, -15) // Decrease image brightness by 15%. +// dstImage = imaging.AdjustBrightness(srcImage, 10) // Increase image brightness by 10%. +// +func AdjustBrightness(img image.Image, percentage float64) *image.NRGBA { + percentage = math.Min(math.Max(percentage, -100.0), 100.0) + lut := make([]uint8, 256) + + shift := 255.0 * percentage / 100.0 + for i := 0; i < 256; i++ { + lut[i] = clamp(float64(i) + shift) + } + + return adjustLUT(img, lut) +} + +// AdjustGamma performs a gamma correction on the image and returns the adjusted image. +// Gamma parameter must be positive. Gamma = 1.0 gives the original image. +// Gamma less than 1.0 darkens the image and gamma greater than 1.0 lightens it. +// +// Example: +// +// dstImage = imaging.AdjustGamma(srcImage, 0.7) +// +func AdjustGamma(img image.Image, gamma float64) *image.NRGBA { + e := 1.0 / math.Max(gamma, 0.0001) + lut := make([]uint8, 256) + + for i := 0; i < 256; i++ { + lut[i] = clamp(math.Pow(float64(i)/255.0, e) * 255.0) + } + + return adjustLUT(img, lut) +} + +// AdjustSigmoid changes the contrast of the image using a sigmoidal function and returns the adjusted image. +// It's a non-linear contrast change useful for photo adjustments as it preserves highlight and shadow detail. +// The midpoint parameter is the midpoint of contrast that must be between 0 and 1, typically 0.5. +// The factor parameter indicates how much to increase or decrease the contrast, typically in range (-10, 10). +// If the factor parameter is positive the image contrast is increased otherwise the contrast is decreased. +// +// Examples: +// +// dstImage = imaging.AdjustSigmoid(srcImage, 0.5, 3.0) // Increase the contrast. +// dstImage = imaging.AdjustSigmoid(srcImage, 0.5, -3.0) // Decrease the contrast. +// +func AdjustSigmoid(img image.Image, midpoint, factor float64) *image.NRGBA { + if factor == 0 { + return Clone(img) + } + + lut := make([]uint8, 256) + a := math.Min(math.Max(midpoint, 0.0), 1.0) + b := math.Abs(factor) + sig0 := sigmoid(a, b, 0) + sig1 := sigmoid(a, b, 1) + e := 1.0e-6 + + if factor > 0 { + for i := 0; i < 256; i++ { + x := float64(i) / 255.0 + sigX := sigmoid(a, b, x) + f := (sigX - sig0) / (sig1 - sig0) + lut[i] = clamp(f * 255.0) + } + } else { + for i := 0; i < 256; i++ { + x := float64(i) / 255.0 + arg := math.Min(math.Max((sig1-sig0)*x+sig0, e), 1.0-e) + f := a - math.Log(1.0/arg-1.0)/b + lut[i] = clamp(f * 255.0) + } + } + + return adjustLUT(img, lut) +} + +func sigmoid(a, b, x float64) float64 { + return 1 / (1 + math.Exp(b*(a-x))) +} + +// adjustLUT applies the given lookup table to the colors of the image. +func adjustLUT(img image.Image, lut []uint8) *image.NRGBA { + src := newScanner(img) + dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h)) + lut = lut[0:256] + parallel(0, src.h, func(ys <-chan int) { + for y := range ys { + i := y * dst.Stride + src.scan(0, y, src.w, y+1, dst.Pix[i:i+src.w*4]) + for x := 0; x < src.w; x++ { + d := dst.Pix[i : i+3 : i+3] + d[0] = lut[d[0]] + d[1] = lut[d[1]] + d[2] = lut[d[2]] + i += 4 + } + } + }) + return dst +} + +// AdjustFunc applies the fn function to each pixel of the img image and returns the adjusted image. +// +// Example: +// +// dstImage = imaging.AdjustFunc( +// srcImage, +// func(c color.NRGBA) color.NRGBA { +// // Shift the red channel by 16. +// r := int(c.R) + 16 +// if r > 255 { +// r = 255 +// } +// return color.NRGBA{uint8(r), c.G, c.B, c.A} +// } +// ) +// +func AdjustFunc(img image.Image, fn func(c color.NRGBA) color.NRGBA) *image.NRGBA { + src := newScanner(img) + dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h)) + parallel(0, src.h, func(ys <-chan int) { + for y := range ys { + i := y * dst.Stride + src.scan(0, y, src.w, y+1, dst.Pix[i:i+src.w*4]) + for x := 0; x < src.w; x++ { + d := dst.Pix[i : i+4 : i+4] + r := d[0] + g := d[1] + b := d[2] + a := d[3] + c := fn(color.NRGBA{r, g, b, a}) + d[0] = c.R + d[1] = c.G + d[2] = c.B + d[3] = c.A + i += 4 + } + } + }) + return dst +} diff --git a/vendor/github.com/disintegration/imaging/convolution.go b/vendor/github.com/disintegration/imaging/convolution.go new file mode 100644 index 000000000..11eddc162 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/convolution.go @@ -0,0 +1,148 @@ +package imaging + +import ( + "image" +) + +// ConvolveOptions are convolution parameters. +type ConvolveOptions struct { + // If Normalize is true the kernel is normalized before convolution. + Normalize bool + + // If Abs is true the absolute value of each color channel is taken after convolution. + Abs bool + + // Bias is added to each color channel value after convolution. + Bias int +} + +// Convolve3x3 convolves the image with the specified 3x3 convolution kernel. +// Default parameters are used if a nil *ConvolveOptions is passed. +func Convolve3x3(img image.Image, kernel [9]float64, options *ConvolveOptions) *image.NRGBA { + return convolve(img, kernel[:], options) +} + +// Convolve5x5 convolves the image with the specified 5x5 convolution kernel. +// Default parameters are used if a nil *ConvolveOptions is passed. +func Convolve5x5(img image.Image, kernel [25]float64, options *ConvolveOptions) *image.NRGBA { + return convolve(img, kernel[:], options) +} + +func convolve(img image.Image, kernel []float64, options *ConvolveOptions) *image.NRGBA { + src := toNRGBA(img) + w := src.Bounds().Max.X + h := src.Bounds().Max.Y + dst := image.NewNRGBA(image.Rect(0, 0, w, h)) + + if w < 1 || h < 1 { + return dst + } + + if options == nil { + options = &ConvolveOptions{} + } + + if options.Normalize { + normalizeKernel(kernel) + } + + type coef struct { + x, y int + k float64 + } + var coefs []coef + var m int + + switch len(kernel) { + case 9: + m = 1 + case 25: + m = 2 + } + + i := 0 + for y := -m; y <= m; y++ { + for x := -m; x <= m; x++ { + if kernel[i] != 0 { + coefs = append(coefs, coef{x: x, y: y, k: kernel[i]}) + } + i++ + } + } + + parallel(0, h, func(ys <-chan int) { + for y := range ys { + for x := 0; x < w; x++ { + var r, g, b float64 + for _, c := range coefs { + ix := x + c.x + if ix < 0 { + ix = 0 + } else if ix >= w { + ix = w - 1 + } + + iy := y + c.y + if iy < 0 { + iy = 0 + } else if iy >= h { + iy = h - 1 + } + + off := iy*src.Stride + ix*4 + s := src.Pix[off : off+3 : off+3] + r += float64(s[0]) * c.k + g += float64(s[1]) * c.k + b += float64(s[2]) * c.k + } + + if options.Abs { + if r < 0 { + r = -r + } + if g < 0 { + g = -g + } + if b < 0 { + b = -b + } + } + + if options.Bias != 0 { + r += float64(options.Bias) + g += float64(options.Bias) + b += float64(options.Bias) + } + + srcOff := y*src.Stride + x*4 + dstOff := y*dst.Stride + x*4 + d := dst.Pix[dstOff : dstOff+4 : dstOff+4] + d[0] = clamp(r) + d[1] = clamp(g) + d[2] = clamp(b) + d[3] = src.Pix[srcOff+3] + } + } + }) + + return dst +} + +func normalizeKernel(kernel []float64) { + var sum, sumpos float64 + for i := range kernel { + sum += kernel[i] + if kernel[i] > 0 { + sumpos += kernel[i] + } + } + if sum != 0 { + for i := range kernel { + kernel[i] /= sum + } + } else if sumpos != 0 { + for i := range kernel { + kernel[i] /= sumpos + } + } +} diff --git a/vendor/github.com/disintegration/imaging/doc.go b/vendor/github.com/disintegration/imaging/doc.go new file mode 100644 index 000000000..c98c91250 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/doc.go @@ -0,0 +1,7 @@ +/* +Package imaging provides basic image processing functions (resize, rotate, crop, brightness/contrast adjustments, etc.). + +All the image processing functions provided by the package accept any image type that implements image.Image interface +as an input, and return a new image of *image.NRGBA type (32bit RGBA colors, non-premultiplied alpha). +*/ +package imaging diff --git a/vendor/github.com/disintegration/imaging/effects.go b/vendor/github.com/disintegration/imaging/effects.go new file mode 100644 index 000000000..47316b701 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/effects.go @@ -0,0 +1,169 @@ +package imaging + +import ( + "image" + "math" +) + +func gaussianBlurKernel(x, sigma float64) float64 { + return math.Exp(-(x*x)/(2*sigma*sigma)) / (sigma * math.Sqrt(2*math.Pi)) +} + +// Blur produces a blurred version of the image using a Gaussian function. +// Sigma parameter must be positive and indicates how much the image will be blurred. +// +// Example: +// +// dstImage := imaging.Blur(srcImage, 3.5) +// +func Blur(img image.Image, sigma float64) *image.NRGBA { + if sigma <= 0 { + return Clone(img) + } + + radius := int(math.Ceil(sigma * 3.0)) + kernel := make([]float64, radius+1) + + for i := 0; i <= radius; i++ { + kernel[i] = gaussianBlurKernel(float64(i), sigma) + } + + return blurVertical(blurHorizontal(img, kernel), kernel) +} + +func blurHorizontal(img image.Image, kernel []float64) *image.NRGBA { + src := newScanner(img) + dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h)) + radius := len(kernel) - 1 + + parallel(0, src.h, func(ys <-chan int) { + scanLine := make([]uint8, src.w*4) + scanLineF := make([]float64, len(scanLine)) + for y := range ys { + src.scan(0, y, src.w, y+1, scanLine) + for i, v := range scanLine { + scanLineF[i] = float64(v) + } + for x := 0; x < src.w; x++ { + min := x - radius + if min < 0 { + min = 0 + } + max := x + radius + if max > src.w-1 { + max = src.w - 1 + } + var r, g, b, a, wsum float64 + for ix := min; ix <= max; ix++ { + i := ix * 4 + weight := kernel[absint(x-ix)] + wsum += weight + s := scanLineF[i : i+4 : i+4] + wa := s[3] * weight + r += s[0] * wa + g += s[1] * wa + b += s[2] * wa + a += wa + } + if a != 0 { + aInv := 1 / a + j := y*dst.Stride + x*4 + d := dst.Pix[j : j+4 : j+4] + d[0] = clamp(r * aInv) + d[1] = clamp(g * aInv) + d[2] = clamp(b * aInv) + d[3] = clamp(a / wsum) + } + } + } + }) + + return dst +} + +func blurVertical(img image.Image, kernel []float64) *image.NRGBA { + src := newScanner(img) + dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h)) + radius := len(kernel) - 1 + + parallel(0, src.w, func(xs <-chan int) { + scanLine := make([]uint8, src.h*4) + scanLineF := make([]float64, len(scanLine)) + for x := range xs { + src.scan(x, 0, x+1, src.h, scanLine) + for i, v := range scanLine { + scanLineF[i] = float64(v) + } + for y := 0; y < src.h; y++ { + min := y - radius + if min < 0 { + min = 0 + } + max := y + radius + if max > src.h-1 { + max = src.h - 1 + } + var r, g, b, a, wsum float64 + for iy := min; iy <= max; iy++ { + i := iy * 4 + weight := kernel[absint(y-iy)] + wsum += weight + s := scanLineF[i : i+4 : i+4] + wa := s[3] * weight + r += s[0] * wa + g += s[1] * wa + b += s[2] * wa + a += wa + } + if a != 0 { + aInv := 1 / a + j := y*dst.Stride + x*4 + d := dst.Pix[j : j+4 : j+4] + d[0] = clamp(r * aInv) + d[1] = clamp(g * aInv) + d[2] = clamp(b * aInv) + d[3] = clamp(a / wsum) + } + } + } + }) + + return dst +} + +// Sharpen produces a sharpened version of the image. +// Sigma parameter must be positive and indicates how much the image will be sharpened. +// +// Example: +// +// dstImage := imaging.Sharpen(srcImage, 3.5) +// +func Sharpen(img image.Image, sigma float64) *image.NRGBA { + if sigma <= 0 { + return Clone(img) + } + + src := newScanner(img) + dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h)) + blurred := Blur(img, sigma) + + parallel(0, src.h, func(ys <-chan int) { + scanLine := make([]uint8, src.w*4) + for y := range ys { + src.scan(0, y, src.w, y+1, scanLine) + j := y * dst.Stride + for i := 0; i < src.w*4; i++ { + val := int(scanLine[i])<<1 - int(blurred.Pix[j]) + if val < 0 { + val = 0 + } else if val > 0xff { + val = 0xff + } + dst.Pix[j] = uint8(val) + j++ + } + } + }) + + return dst +} diff --git a/vendor/github.com/disintegration/imaging/go.mod b/vendor/github.com/disintegration/imaging/go.mod new file mode 100644 index 000000000..a870810ee --- /dev/null +++ b/vendor/github.com/disintegration/imaging/go.mod @@ -0,0 +1,3 @@ +module github.com/disintegration/imaging + +require golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 diff --git a/vendor/github.com/disintegration/imaging/go.sum b/vendor/github.com/disintegration/imaging/go.sum new file mode 100644 index 000000000..17bf7381e --- /dev/null +++ b/vendor/github.com/disintegration/imaging/go.sum @@ -0,0 +1,3 @@ +golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U= +golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/vendor/github.com/disintegration/imaging/histogram.go b/vendor/github.com/disintegration/imaging/histogram.go new file mode 100644 index 000000000..c547fe822 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/histogram.go @@ -0,0 +1,52 @@ +package imaging + +import ( + "image" + "sync" +) + +// Histogram returns a normalized histogram of an image. +// +// Resulting histogram is represented as an array of 256 floats, where +// histogram[i] is a probability of a pixel being of a particular luminance i. +func Histogram(img image.Image) [256]float64 { + var mu sync.Mutex + var histogram [256]float64 + var total float64 + + src := newScanner(img) + if src.w == 0 || src.h == 0 { + return histogram + } + + parallel(0, src.h, func(ys <-chan int) { + var tmpHistogram [256]float64 + var tmpTotal float64 + scanLine := make([]uint8, src.w*4) + for y := range ys { + src.scan(0, y, src.w, y+1, scanLine) + i := 0 + for x := 0; x < src.w; x++ { + s := scanLine[i : i+3 : i+3] + r := s[0] + g := s[1] + b := s[2] + y := 0.299*float32(r) + 0.587*float32(g) + 0.114*float32(b) + tmpHistogram[int(y+0.5)]++ + tmpTotal++ + i += 4 + } + } + mu.Lock() + for i := 0; i < 256; i++ { + histogram[i] += tmpHistogram[i] + } + total += tmpTotal + mu.Unlock() + }) + + for i := 0; i < 256; i++ { + histogram[i] = histogram[i] / total + } + return histogram +} diff --git a/vendor/github.com/disintegration/imaging/io.go b/vendor/github.com/disintegration/imaging/io.go new file mode 100644 index 000000000..f6c6da86b --- /dev/null +++ b/vendor/github.com/disintegration/imaging/io.go @@ -0,0 +1,444 @@ +package imaging + +import ( + "encoding/binary" + "errors" + "image" + "image/draw" + "image/gif" + "image/jpeg" + "image/png" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "golang.org/x/image/bmp" + "golang.org/x/image/tiff" +) + +type fileSystem interface { + Create(string) (io.WriteCloser, error) + Open(string) (io.ReadCloser, error) +} + +type localFS struct{} + +func (localFS) Create(name string) (io.WriteCloser, error) { return os.Create(name) } +func (localFS) Open(name string) (io.ReadCloser, error) { return os.Open(name) } + +var fs fileSystem = localFS{} + +type decodeConfig struct { + autoOrientation bool +} + +var defaultDecodeConfig = decodeConfig{ + autoOrientation: false, +} + +// DecodeOption sets an optional parameter for the Decode and Open functions. +type DecodeOption func(*decodeConfig) + +// AutoOrientation returns a DecodeOption that sets the auto-orientation mode. +// If auto-orientation is enabled, the image will be transformed after decoding +// according to the EXIF orientation tag (if present). By default it's disabled. +func AutoOrientation(enabled bool) DecodeOption { + return func(c *decodeConfig) { + c.autoOrientation = enabled + } +} + +// Decode reads an image from r. +func Decode(r io.Reader, opts ...DecodeOption) (image.Image, error) { + cfg := defaultDecodeConfig + for _, option := range opts { + option(&cfg) + } + + if !cfg.autoOrientation { + img, _, err := image.Decode(r) + return img, err + } + + var orient orientation + pr, pw := io.Pipe() + r = io.TeeReader(r, pw) + done := make(chan struct{}) + go func() { + defer close(done) + orient = readOrientation(pr) + io.Copy(ioutil.Discard, pr) + }() + + img, _, err := image.Decode(r) + pw.Close() + <-done + if err != nil { + return nil, err + } + + return fixOrientation(img, orient), nil +} + +// Open loads an image from file. +// +// Examples: +// +// // Load an image from file. +// img, err := imaging.Open("test.jpg") +// +// // Load an image and transform it depending on the EXIF orientation tag (if present). +// img, err := imaging.Open("test.jpg", imaging.AutoOrientation(true)) +// +func Open(filename string, opts ...DecodeOption) (image.Image, error) { + file, err := fs.Open(filename) + if err != nil { + return nil, err + } + defer file.Close() + return Decode(file, opts...) +} + +// Format is an image file format. +type Format int + +// Image file formats. +const ( + JPEG Format = iota + PNG + GIF + TIFF + BMP +) + +var formatExts = map[string]Format{ + "jpg": JPEG, + "jpeg": JPEG, + "png": PNG, + "gif": GIF, + "tif": TIFF, + "tiff": TIFF, + "bmp": BMP, +} + +var formatNames = map[Format]string{ + JPEG: "JPEG", + PNG: "PNG", + GIF: "GIF", + TIFF: "TIFF", + BMP: "BMP", +} + +func (f Format) String() string { + return formatNames[f] +} + +// ErrUnsupportedFormat means the given image format is not supported. +var ErrUnsupportedFormat = errors.New("imaging: unsupported image format") + +// FormatFromExtension parses image format from filename extension: +// "jpg" (or "jpeg"), "png", "gif", "tif" (or "tiff") and "bmp" are supported. +func FormatFromExtension(ext string) (Format, error) { + if f, ok := formatExts[strings.ToLower(strings.TrimPrefix(ext, "."))]; ok { + return f, nil + } + return -1, ErrUnsupportedFormat +} + +// FormatFromFilename parses image format from filename: +// "jpg" (or "jpeg"), "png", "gif", "tif" (or "tiff") and "bmp" are supported. +func FormatFromFilename(filename string) (Format, error) { + ext := filepath.Ext(filename) + return FormatFromExtension(ext) +} + +type encodeConfig struct { + jpegQuality int + gifNumColors int + gifQuantizer draw.Quantizer + gifDrawer draw.Drawer + pngCompressionLevel png.CompressionLevel +} + +var defaultEncodeConfig = encodeConfig{ + jpegQuality: 95, + gifNumColors: 256, + gifQuantizer: nil, + gifDrawer: nil, + pngCompressionLevel: png.DefaultCompression, +} + +// EncodeOption sets an optional parameter for the Encode and Save functions. +type EncodeOption func(*encodeConfig) + +// JPEGQuality returns an EncodeOption that sets the output JPEG quality. +// Quality ranges from 1 to 100 inclusive, higher is better. Default is 95. +func JPEGQuality(quality int) EncodeOption { + return func(c *encodeConfig) { + c.jpegQuality = quality + } +} + +// GIFNumColors returns an EncodeOption that sets the maximum number of colors +// used in the GIF-encoded image. It ranges from 1 to 256. Default is 256. +func GIFNumColors(numColors int) EncodeOption { + return func(c *encodeConfig) { + c.gifNumColors = numColors + } +} + +// GIFQuantizer returns an EncodeOption that sets the quantizer that is used to produce +// a palette of the GIF-encoded image. +func GIFQuantizer(quantizer draw.Quantizer) EncodeOption { + return func(c *encodeConfig) { + c.gifQuantizer = quantizer + } +} + +// GIFDrawer returns an EncodeOption that sets the drawer that is used to convert +// the source image to the desired palette of the GIF-encoded image. +func GIFDrawer(drawer draw.Drawer) EncodeOption { + return func(c *encodeConfig) { + c.gifDrawer = drawer + } +} + +// PNGCompressionLevel returns an EncodeOption that sets the compression level +// of the PNG-encoded image. Default is png.DefaultCompression. +func PNGCompressionLevel(level png.CompressionLevel) EncodeOption { + return func(c *encodeConfig) { + c.pngCompressionLevel = level + } +} + +// Encode writes the image img to w in the specified format (JPEG, PNG, GIF, TIFF or BMP). +func Encode(w io.Writer, img image.Image, format Format, opts ...EncodeOption) error { + cfg := defaultEncodeConfig + for _, option := range opts { + option(&cfg) + } + + switch format { + case JPEG: + if nrgba, ok := img.(*image.NRGBA); ok && nrgba.Opaque() { + rgba := &image.RGBA{ + Pix: nrgba.Pix, + Stride: nrgba.Stride, + Rect: nrgba.Rect, + } + return jpeg.Encode(w, rgba, &jpeg.Options{Quality: cfg.jpegQuality}) + } + return jpeg.Encode(w, img, &jpeg.Options{Quality: cfg.jpegQuality}) + + case PNG: + encoder := png.Encoder{CompressionLevel: cfg.pngCompressionLevel} + return encoder.Encode(w, img) + + case GIF: + return gif.Encode(w, img, &gif.Options{ + NumColors: cfg.gifNumColors, + Quantizer: cfg.gifQuantizer, + Drawer: cfg.gifDrawer, + }) + + case TIFF: + return tiff.Encode(w, img, &tiff.Options{Compression: tiff.Deflate, Predictor: true}) + + case BMP: + return bmp.Encode(w, img) + } + + return ErrUnsupportedFormat +} + +// Save saves the image to file with the specified filename. +// The format is determined from the filename extension: +// "jpg" (or "jpeg"), "png", "gif", "tif" (or "tiff") and "bmp" are supported. +// +// Examples: +// +// // Save the image as PNG. +// err := imaging.Save(img, "out.png") +// +// // Save the image as JPEG with optional quality parameter set to 80. +// err := imaging.Save(img, "out.jpg", imaging.JPEGQuality(80)) +// +func Save(img image.Image, filename string, opts ...EncodeOption) (err error) { + f, err := FormatFromFilename(filename) + if err != nil { + return err + } + file, err := fs.Create(filename) + if err != nil { + return err + } + err = Encode(file, img, f, opts...) + errc := file.Close() + if err == nil { + err = errc + } + return err +} + +// orientation is an EXIF flag that specifies the transformation +// that should be applied to image to display it correctly. +type orientation int + +const ( + orientationUnspecified = 0 + orientationNormal = 1 + orientationFlipH = 2 + orientationRotate180 = 3 + orientationFlipV = 4 + orientationTranspose = 5 + orientationRotate270 = 6 + orientationTransverse = 7 + orientationRotate90 = 8 +) + +// readOrientation tries to read the orientation EXIF flag from image data in r. +// If the EXIF data block is not found or the orientation flag is not found +// or any other error occures while reading the data, it returns the +// orientationUnspecified (0) value. +func readOrientation(r io.Reader) orientation { + const ( + markerSOI = 0xffd8 + markerAPP1 = 0xffe1 + exifHeader = 0x45786966 + byteOrderBE = 0x4d4d + byteOrderLE = 0x4949 + orientationTag = 0x0112 + ) + + // Check if JPEG SOI marker is present. + var soi uint16 + if err := binary.Read(r, binary.BigEndian, &soi); err != nil { + return orientationUnspecified + } + if soi != markerSOI { + return orientationUnspecified // Missing JPEG SOI marker. + } + + // Find JPEG APP1 marker. + for { + var marker, size uint16 + if err := binary.Read(r, binary.BigEndian, &marker); err != nil { + return orientationUnspecified + } + if err := binary.Read(r, binary.BigEndian, &size); err != nil { + return orientationUnspecified + } + if marker>>8 != 0xff { + return orientationUnspecified // Invalid JPEG marker. + } + if marker == markerAPP1 { + break + } + if size < 2 { + return orientationUnspecified // Invalid block size. + } + if _, err := io.CopyN(ioutil.Discard, r, int64(size-2)); err != nil { + return orientationUnspecified + } + } + + // Check if EXIF header is present. + var header uint32 + if err := binary.Read(r, binary.BigEndian, &header); err != nil { + return orientationUnspecified + } + if header != exifHeader { + return orientationUnspecified + } + if _, err := io.CopyN(ioutil.Discard, r, 2); err != nil { + return orientationUnspecified + } + + // Read byte order information. + var ( + byteOrderTag uint16 + byteOrder binary.ByteOrder + ) + if err := binary.Read(r, binary.BigEndian, &byteOrderTag); err != nil { + return orientationUnspecified + } + switch byteOrderTag { + case byteOrderBE: + byteOrder = binary.BigEndian + case byteOrderLE: + byteOrder = binary.LittleEndian + default: + return orientationUnspecified // Invalid byte order flag. + } + if _, err := io.CopyN(ioutil.Discard, r, 2); err != nil { + return orientationUnspecified + } + + // Skip the EXIF offset. + var offset uint32 + if err := binary.Read(r, byteOrder, &offset); err != nil { + return orientationUnspecified + } + if offset < 8 { + return orientationUnspecified // Invalid offset value. + } + if _, err := io.CopyN(ioutil.Discard, r, int64(offset-8)); err != nil { + return orientationUnspecified + } + + // Read the number of tags. + var numTags uint16 + if err := binary.Read(r, byteOrder, &numTags); err != nil { + return orientationUnspecified + } + + // Find the orientation tag. + for i := 0; i < int(numTags); i++ { + var tag uint16 + if err := binary.Read(r, byteOrder, &tag); err != nil { + return orientationUnspecified + } + if tag != orientationTag { + if _, err := io.CopyN(ioutil.Discard, r, 10); err != nil { + return orientationUnspecified + } + continue + } + if _, err := io.CopyN(ioutil.Discard, r, 6); err != nil { + return orientationUnspecified + } + var val uint16 + if err := binary.Read(r, byteOrder, &val); err != nil { + return orientationUnspecified + } + if val < 1 || val > 8 { + return orientationUnspecified // Invalid tag value. + } + return orientation(val) + } + return orientationUnspecified // Missing orientation tag. +} + +// fixOrientation applies a transform to img corresponding to the given orientation flag. +func fixOrientation(img image.Image, o orientation) image.Image { + switch o { + case orientationNormal: + case orientationFlipH: + img = FlipH(img) + case orientationFlipV: + img = FlipV(img) + case orientationRotate90: + img = Rotate90(img) + case orientationRotate180: + img = Rotate180(img) + case orientationRotate270: + img = Rotate270(img) + case orientationTranspose: + img = Transpose(img) + case orientationTransverse: + img = Transverse(img) + } + return img +} diff --git a/vendor/github.com/disintegration/imaging/resize.go b/vendor/github.com/disintegration/imaging/resize.go new file mode 100644 index 000000000..706435e3d --- /dev/null +++ b/vendor/github.com/disintegration/imaging/resize.go @@ -0,0 +1,595 @@ +package imaging + +import ( + "image" + "math" +) + +type indexWeight struct { + index int + weight float64 +} + +func precomputeWeights(dstSize, srcSize int, filter ResampleFilter) [][]indexWeight { + du := float64(srcSize) / float64(dstSize) + scale := du + if scale < 1.0 { + scale = 1.0 + } + ru := math.Ceil(scale * filter.Support) + + out := make([][]indexWeight, dstSize) + tmp := make([]indexWeight, 0, dstSize*int(ru+2)*2) + + for v := 0; v < dstSize; v++ { + fu := (float64(v)+0.5)*du - 0.5 + + begin := int(math.Ceil(fu - ru)) + if begin < 0 { + begin = 0 + } + end := int(math.Floor(fu + ru)) + if end > srcSize-1 { + end = srcSize - 1 + } + + var sum float64 + for u := begin; u <= end; u++ { + w := filter.Kernel((float64(u) - fu) / scale) + if w != 0 { + sum += w + tmp = append(tmp, indexWeight{index: u, weight: w}) + } + } + if sum != 0 { + for i := range tmp { + tmp[i].weight /= sum + } + } + + out[v] = tmp + tmp = tmp[len(tmp):] + } + + return out +} + +// Resize resizes the image to the specified width and height using the specified resampling +// filter and returns the transformed image. If one of width or height is 0, the image aspect +// ratio is preserved. +// +// Example: +// +// dstImage := imaging.Resize(srcImage, 800, 600, imaging.Lanczos) +// +func Resize(img image.Image, width, height int, filter ResampleFilter) *image.NRGBA { + dstW, dstH := width, height + if dstW < 0 || dstH < 0 { + return &image.NRGBA{} + } + if dstW == 0 && dstH == 0 { + return &image.NRGBA{} + } + + srcW := img.Bounds().Dx() + srcH := img.Bounds().Dy() + if srcW <= 0 || srcH <= 0 { + return &image.NRGBA{} + } + + // If new width or height is 0 then preserve aspect ratio, minimum 1px. + if dstW == 0 { + tmpW := float64(dstH) * float64(srcW) / float64(srcH) + dstW = int(math.Max(1.0, math.Floor(tmpW+0.5))) + } + if dstH == 0 { + tmpH := float64(dstW) * float64(srcH) / float64(srcW) + dstH = int(math.Max(1.0, math.Floor(tmpH+0.5))) + } + + if filter.Support <= 0 { + // Nearest-neighbor special case. + return resizeNearest(img, dstW, dstH) + } + + if srcW != dstW && srcH != dstH { + return resizeVertical(resizeHorizontal(img, dstW, filter), dstH, filter) + } + if srcW != dstW { + return resizeHorizontal(img, dstW, filter) + } + if srcH != dstH { + return resizeVertical(img, dstH, filter) + } + return Clone(img) +} + +func resizeHorizontal(img image.Image, width int, filter ResampleFilter) *image.NRGBA { + src := newScanner(img) + dst := image.NewNRGBA(image.Rect(0, 0, width, src.h)) + weights := precomputeWeights(width, src.w, filter) + parallel(0, src.h, func(ys <-chan int) { + scanLine := make([]uint8, src.w*4) + for y := range ys { + src.scan(0, y, src.w, y+1, scanLine) + j0 := y * dst.Stride + for x := range weights { + var r, g, b, a float64 + for _, w := range weights[x] { + i := w.index * 4 + s := scanLine[i : i+4 : i+4] + aw := float64(s[3]) * w.weight + r += float64(s[0]) * aw + g += float64(s[1]) * aw + b += float64(s[2]) * aw + a += aw + } + if a != 0 { + aInv := 1 / a + j := j0 + x*4 + d := dst.Pix[j : j+4 : j+4] + d[0] = clamp(r * aInv) + d[1] = clamp(g * aInv) + d[2] = clamp(b * aInv) + d[3] = clamp(a) + } + } + } + }) + return dst +} + +func resizeVertical(img image.Image, height int, filter ResampleFilter) *image.NRGBA { + src := newScanner(img) + dst := image.NewNRGBA(image.Rect(0, 0, src.w, height)) + weights := precomputeWeights(height, src.h, filter) + parallel(0, src.w, func(xs <-chan int) { + scanLine := make([]uint8, src.h*4) + for x := range xs { + src.scan(x, 0, x+1, src.h, scanLine) + for y := range weights { + var r, g, b, a float64 + for _, w := range weights[y] { + i := w.index * 4 + s := scanLine[i : i+4 : i+4] + aw := float64(s[3]) * w.weight + r += float64(s[0]) * aw + g += float64(s[1]) * aw + b += float64(s[2]) * aw + a += aw + } + if a != 0 { + aInv := 1 / a + j := y*dst.Stride + x*4 + d := dst.Pix[j : j+4 : j+4] + d[0] = clamp(r * aInv) + d[1] = clamp(g * aInv) + d[2] = clamp(b * aInv) + d[3] = clamp(a) + } + } + } + }) + return dst +} + +// resizeNearest is a fast nearest-neighbor resize, no filtering. +func resizeNearest(img image.Image, width, height int) *image.NRGBA { + dst := image.NewNRGBA(image.Rect(0, 0, width, height)) + dx := float64(img.Bounds().Dx()) / float64(width) + dy := float64(img.Bounds().Dy()) / float64(height) + + if dx > 1 && dy > 1 { + src := newScanner(img) + parallel(0, height, func(ys <-chan int) { + for y := range ys { + srcY := int((float64(y) + 0.5) * dy) + dstOff := y * dst.Stride + for x := 0; x < width; x++ { + srcX := int((float64(x) + 0.5) * dx) + src.scan(srcX, srcY, srcX+1, srcY+1, dst.Pix[dstOff:dstOff+4]) + dstOff += 4 + } + } + }) + } else { + src := toNRGBA(img) + parallel(0, height, func(ys <-chan int) { + for y := range ys { + srcY := int((float64(y) + 0.5) * dy) + srcOff0 := srcY * src.Stride + dstOff := y * dst.Stride + for x := 0; x < width; x++ { + srcX := int((float64(x) + 0.5) * dx) + srcOff := srcOff0 + srcX*4 + copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4]) + dstOff += 4 + } + } + }) + } + + return dst +} + +// Fit scales down the image using the specified resample filter to fit the specified +// maximum width and height and returns the transformed image. +// +// Example: +// +// dstImage := imaging.Fit(srcImage, 800, 600, imaging.Lanczos) +// +func Fit(img image.Image, width, height int, filter ResampleFilter) *image.NRGBA { + maxW, maxH := width, height + + if maxW <= 0 || maxH <= 0 { + return &image.NRGBA{} + } + + srcBounds := img.Bounds() + srcW := srcBounds.Dx() + srcH := srcBounds.Dy() + + if srcW <= 0 || srcH <= 0 { + return &image.NRGBA{} + } + + if srcW <= maxW && srcH <= maxH { + return Clone(img) + } + + srcAspectRatio := float64(srcW) / float64(srcH) + maxAspectRatio := float64(maxW) / float64(maxH) + + var newW, newH int + if srcAspectRatio > maxAspectRatio { + newW = maxW + newH = int(float64(newW) / srcAspectRatio) + } else { + newH = maxH + newW = int(float64(newH) * srcAspectRatio) + } + + return Resize(img, newW, newH, filter) +} + +// Fill creates an image with the specified dimensions and fills it with the scaled source image. +// To achieve the correct aspect ratio without stretching, the source image will be cropped. +// +// Example: +// +// dstImage := imaging.Fill(srcImage, 800, 600, imaging.Center, imaging.Lanczos) +// +func Fill(img image.Image, width, height int, anchor Anchor, filter ResampleFilter) *image.NRGBA { + dstW, dstH := width, height + + if dstW <= 0 || dstH <= 0 { + return &image.NRGBA{} + } + + srcBounds := img.Bounds() + srcW := srcBounds.Dx() + srcH := srcBounds.Dy() + + if srcW <= 0 || srcH <= 0 { + return &image.NRGBA{} + } + + if srcW == dstW && srcH == dstH { + return Clone(img) + } + + if srcW >= 100 && srcH >= 100 { + return cropAndResize(img, dstW, dstH, anchor, filter) + } + return resizeAndCrop(img, dstW, dstH, anchor, filter) +} + +// cropAndResize crops the image to the smallest possible size that has the required aspect ratio using +// the given anchor point, then scales it to the specified dimensions and returns the transformed image. +// +// This is generally faster than resizing first, but may result in inaccuracies when used on small source images. +func cropAndResize(img image.Image, width, height int, anchor Anchor, filter ResampleFilter) *image.NRGBA { + dstW, dstH := width, height + + srcBounds := img.Bounds() + srcW := srcBounds.Dx() + srcH := srcBounds.Dy() + srcAspectRatio := float64(srcW) / float64(srcH) + dstAspectRatio := float64(dstW) / float64(dstH) + + var tmp *image.NRGBA + if srcAspectRatio < dstAspectRatio { + cropH := float64(srcW) * float64(dstH) / float64(dstW) + tmp = CropAnchor(img, srcW, int(math.Max(1, cropH)+0.5), anchor) + } else { + cropW := float64(srcH) * float64(dstW) / float64(dstH) + tmp = CropAnchor(img, int(math.Max(1, cropW)+0.5), srcH, anchor) + } + + return Resize(tmp, dstW, dstH, filter) +} + +// resizeAndCrop resizes the image to the smallest possible size that will cover the specified dimensions, +// crops the resized image to the specified dimensions using the given anchor point and returns +// the transformed image. +func resizeAndCrop(img image.Image, width, height int, anchor Anchor, filter ResampleFilter) *image.NRGBA { + dstW, dstH := width, height + + srcBounds := img.Bounds() + srcW := srcBounds.Dx() + srcH := srcBounds.Dy() + srcAspectRatio := float64(srcW) / float64(srcH) + dstAspectRatio := float64(dstW) / float64(dstH) + + var tmp *image.NRGBA + if srcAspectRatio < dstAspectRatio { + tmp = Resize(img, dstW, 0, filter) + } else { + tmp = Resize(img, 0, dstH, filter) + } + + return CropAnchor(tmp, dstW, dstH, anchor) +} + +// Thumbnail scales the image up or down using the specified resample filter, crops it +// to the specified width and hight and returns the transformed image. +// +// Example: +// +// dstImage := imaging.Thumbnail(srcImage, 100, 100, imaging.Lanczos) +// +func Thumbnail(img image.Image, width, height int, filter ResampleFilter) *image.NRGBA { + return Fill(img, width, height, Center, filter) +} + +// ResampleFilter specifies a resampling filter to be used for image resizing. +// +// General filter recommendations: +// +// - Lanczos +// A high-quality resampling filter for photographic images yielding sharp results. +// +// - CatmullRom +// A sharp cubic filter that is faster than Lanczos filter while providing similar results. +// +// - MitchellNetravali +// A cubic filter that produces smoother results with less ringing artifacts than CatmullRom. +// +// - Linear +// Bilinear resampling filter, produces a smooth output. Faster than cubic filters. +// +// - Box +// Simple and fast averaging filter appropriate for downscaling. +// When upscaling it's similar to NearestNeighbor. +// +// - NearestNeighbor +// Fastest resampling filter, no antialiasing. +// +type ResampleFilter struct { + Support float64 + Kernel func(float64) float64 +} + +// NearestNeighbor is a nearest-neighbor filter (no anti-aliasing). +var NearestNeighbor ResampleFilter + +// Box filter (averaging pixels). +var Box ResampleFilter + +// Linear filter. +var Linear ResampleFilter + +// Hermite cubic spline filter (BC-spline; B=0; C=0). +var Hermite ResampleFilter + +// MitchellNetravali is Mitchell-Netravali cubic filter (BC-spline; B=1/3; C=1/3). +var MitchellNetravali ResampleFilter + +// CatmullRom is a Catmull-Rom - sharp cubic filter (BC-spline; B=0; C=0.5). +var CatmullRom ResampleFilter + +// BSpline is a smooth cubic filter (BC-spline; B=1; C=0). +var BSpline ResampleFilter + +// Gaussian is a Gaussian blurring filter. +var Gaussian ResampleFilter + +// Bartlett is a Bartlett-windowed sinc filter (3 lobes). +var Bartlett ResampleFilter + +// Lanczos filter (3 lobes). +var Lanczos ResampleFilter + +// Hann is a Hann-windowed sinc filter (3 lobes). +var Hann ResampleFilter + +// Hamming is a Hamming-windowed sinc filter (3 lobes). +var Hamming ResampleFilter + +// Blackman is a Blackman-windowed sinc filter (3 lobes). +var Blackman ResampleFilter + +// Welch is a Welch-windowed sinc filter (parabolic window, 3 lobes). +var Welch ResampleFilter + +// Cosine is a Cosine-windowed sinc filter (3 lobes). +var Cosine ResampleFilter + +func bcspline(x, b, c float64) float64 { + var y float64 + x = math.Abs(x) + if x < 1.0 { + y = ((12-9*b-6*c)*x*x*x + (-18+12*b+6*c)*x*x + (6 - 2*b)) / 6 + } else if x < 2.0 { + y = ((-b-6*c)*x*x*x + (6*b+30*c)*x*x + (-12*b-48*c)*x + (8*b + 24*c)) / 6 + } + return y +} + +func sinc(x float64) float64 { + if x == 0 { + return 1 + } + return math.Sin(math.Pi*x) / (math.Pi * x) +} + +func init() { + NearestNeighbor = ResampleFilter{ + Support: 0.0, // special case - not applying the filter + } + + Box = ResampleFilter{ + Support: 0.5, + Kernel: func(x float64) float64 { + x = math.Abs(x) + if x <= 0.5 { + return 1.0 + } + return 0 + }, + } + + Linear = ResampleFilter{ + Support: 1.0, + Kernel: func(x float64) float64 { + x = math.Abs(x) + if x < 1.0 { + return 1.0 - x + } + return 0 + }, + } + + Hermite = ResampleFilter{ + Support: 1.0, + Kernel: func(x float64) float64 { + x = math.Abs(x) + if x < 1.0 { + return bcspline(x, 0.0, 0.0) + } + return 0 + }, + } + + MitchellNetravali = ResampleFilter{ + Support: 2.0, + Kernel: func(x float64) float64 { + x = math.Abs(x) + if x < 2.0 { + return bcspline(x, 1.0/3.0, 1.0/3.0) + } + return 0 + }, + } + + CatmullRom = ResampleFilter{ + Support: 2.0, + Kernel: func(x float64) float64 { + x = math.Abs(x) + if x < 2.0 { + return bcspline(x, 0.0, 0.5) + } + return 0 + }, + } + + BSpline = ResampleFilter{ + Support: 2.0, + Kernel: func(x float64) float64 { + x = math.Abs(x) + if x < 2.0 { + return bcspline(x, 1.0, 0.0) + } + return 0 + }, + } + + Gaussian = ResampleFilter{ + Support: 2.0, + Kernel: func(x float64) float64 { + x = math.Abs(x) + if x < 2.0 { + return math.Exp(-2 * x * x) + } + return 0 + }, + } + + Bartlett = ResampleFilter{ + Support: 3.0, + Kernel: func(x float64) float64 { + x = math.Abs(x) + if x < 3.0 { + return sinc(x) * (3.0 - x) / 3.0 + } + return 0 + }, + } + + Lanczos = ResampleFilter{ + Support: 3.0, + Kernel: func(x float64) float64 { + x = math.Abs(x) + if x < 3.0 { + return sinc(x) * sinc(x/3.0) + } + return 0 + }, + } + + Hann = ResampleFilter{ + Support: 3.0, + Kernel: func(x float64) float64 { + x = math.Abs(x) + if x < 3.0 { + return sinc(x) * (0.5 + 0.5*math.Cos(math.Pi*x/3.0)) + } + return 0 + }, + } + + Hamming = ResampleFilter{ + Support: 3.0, + Kernel: func(x float64) float64 { + x = math.Abs(x) + if x < 3.0 { + return sinc(x) * (0.54 + 0.46*math.Cos(math.Pi*x/3.0)) + } + return 0 + }, + } + + Blackman = ResampleFilter{ + Support: 3.0, + Kernel: func(x float64) float64 { + x = math.Abs(x) + if x < 3.0 { + return sinc(x) * (0.42 - 0.5*math.Cos(math.Pi*x/3.0+math.Pi) + 0.08*math.Cos(2.0*math.Pi*x/3.0)) + } + return 0 + }, + } + + Welch = ResampleFilter{ + Support: 3.0, + Kernel: func(x float64) float64 { + x = math.Abs(x) + if x < 3.0 { + return sinc(x) * (1.0 - (x * x / 9.0)) + } + return 0 + }, + } + + Cosine = ResampleFilter{ + Support: 3.0, + Kernel: func(x float64) float64 { + x = math.Abs(x) + if x < 3.0 { + return sinc(x) * math.Cos((math.Pi/2.0)*(x/3.0)) + } + return 0 + }, + } +} diff --git a/vendor/github.com/disintegration/imaging/scanner.go b/vendor/github.com/disintegration/imaging/scanner.go new file mode 100644 index 000000000..37d92cef8 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/scanner.go @@ -0,0 +1,285 @@ +package imaging + +import ( + "image" + "image/color" +) + +type scanner struct { + image image.Image + w, h int + palette []color.NRGBA +} + +func newScanner(img image.Image) *scanner { + s := &scanner{ + image: img, + w: img.Bounds().Dx(), + h: img.Bounds().Dy(), + } + if img, ok := img.(*image.Paletted); ok { + s.palette = make([]color.NRGBA, len(img.Palette)) + for i := 0; i < len(img.Palette); i++ { + s.palette[i] = color.NRGBAModel.Convert(img.Palette[i]).(color.NRGBA) + } + } + return s +} + +// scan scans the given rectangular region of the image into dst. +func (s *scanner) scan(x1, y1, x2, y2 int, dst []uint8) { + switch img := s.image.(type) { + case *image.NRGBA: + size := (x2 - x1) * 4 + j := 0 + i := y1*img.Stride + x1*4 + if size == 4 { + for y := y1; y < y2; y++ { + d := dst[j : j+4 : j+4] + s := img.Pix[i : i+4 : i+4] + d[0] = s[0] + d[1] = s[1] + d[2] = s[2] + d[3] = s[3] + j += size + i += img.Stride + } + } else { + for y := y1; y < y2; y++ { + copy(dst[j:j+size], img.Pix[i:i+size]) + j += size + i += img.Stride + } + } + + case *image.NRGBA64: + j := 0 + for y := y1; y < y2; y++ { + i := y*img.Stride + x1*8 + for x := x1; x < x2; x++ { + s := img.Pix[i : i+8 : i+8] + d := dst[j : j+4 : j+4] + d[0] = s[0] + d[1] = s[2] + d[2] = s[4] + d[3] = s[6] + j += 4 + i += 8 + } + } + + case *image.RGBA: + j := 0 + for y := y1; y < y2; y++ { + i := y*img.Stride + x1*4 + for x := x1; x < x2; x++ { + d := dst[j : j+4 : j+4] + a := img.Pix[i+3] + switch a { + case 0: + d[0] = 0 + d[1] = 0 + d[2] = 0 + d[3] = a + case 0xff: + s := img.Pix[i : i+4 : i+4] + d[0] = s[0] + d[1] = s[1] + d[2] = s[2] + d[3] = a + default: + s := img.Pix[i : i+4 : i+4] + r16 := uint16(s[0]) + g16 := uint16(s[1]) + b16 := uint16(s[2]) + a16 := uint16(a) + d[0] = uint8(r16 * 0xff / a16) + d[1] = uint8(g16 * 0xff / a16) + d[2] = uint8(b16 * 0xff / a16) + d[3] = a + } + j += 4 + i += 4 + } + } + + case *image.RGBA64: + j := 0 + for y := y1; y < y2; y++ { + i := y*img.Stride + x1*8 + for x := x1; x < x2; x++ { + s := img.Pix[i : i+8 : i+8] + d := dst[j : j+4 : j+4] + a := s[6] + switch a { + case 0: + d[0] = 0 + d[1] = 0 + d[2] = 0 + case 0xff: + d[0] = s[0] + d[1] = s[2] + d[2] = s[4] + default: + r32 := uint32(s[0])<<8 | uint32(s[1]) + g32 := uint32(s[2])<<8 | uint32(s[3]) + b32 := uint32(s[4])<<8 | uint32(s[5]) + a32 := uint32(s[6])<<8 | uint32(s[7]) + d[0] = uint8((r32 * 0xffff / a32) >> 8) + d[1] = uint8((g32 * 0xffff / a32) >> 8) + d[2] = uint8((b32 * 0xffff / a32) >> 8) + } + d[3] = a + j += 4 + i += 8 + } + } + + case *image.Gray: + j := 0 + for y := y1; y < y2; y++ { + i := y*img.Stride + x1 + for x := x1; x < x2; x++ { + c := img.Pix[i] + d := dst[j : j+4 : j+4] + d[0] = c + d[1] = c + d[2] = c + d[3] = 0xff + j += 4 + i++ + } + } + + case *image.Gray16: + j := 0 + for y := y1; y < y2; y++ { + i := y*img.Stride + x1*2 + for x := x1; x < x2; x++ { + c := img.Pix[i] + d := dst[j : j+4 : j+4] + d[0] = c + d[1] = c + d[2] = c + d[3] = 0xff + j += 4 + i += 2 + } + } + + case *image.YCbCr: + j := 0 + x1 += img.Rect.Min.X + x2 += img.Rect.Min.X + y1 += img.Rect.Min.Y + y2 += img.Rect.Min.Y + + hy := img.Rect.Min.Y / 2 + hx := img.Rect.Min.X / 2 + for y := y1; y < y2; y++ { + iy := (y-img.Rect.Min.Y)*img.YStride + (x1 - img.Rect.Min.X) + + var yBase int + switch img.SubsampleRatio { + case image.YCbCrSubsampleRatio444, image.YCbCrSubsampleRatio422: + yBase = (y - img.Rect.Min.Y) * img.CStride + case image.YCbCrSubsampleRatio420, image.YCbCrSubsampleRatio440: + yBase = (y/2 - hy) * img.CStride + } + + for x := x1; x < x2; x++ { + var ic int + switch img.SubsampleRatio { + case image.YCbCrSubsampleRatio444, image.YCbCrSubsampleRatio440: + ic = yBase + (x - img.Rect.Min.X) + case image.YCbCrSubsampleRatio422, image.YCbCrSubsampleRatio420: + ic = yBase + (x/2 - hx) + default: + ic = img.COffset(x, y) + } + + yy1 := int32(img.Y[iy]) * 0x10101 + cb1 := int32(img.Cb[ic]) - 128 + cr1 := int32(img.Cr[ic]) - 128 + + r := yy1 + 91881*cr1 + if uint32(r)&0xff000000 == 0 { + r >>= 16 + } else { + r = ^(r >> 31) + } + + g := yy1 - 22554*cb1 - 46802*cr1 + if uint32(g)&0xff000000 == 0 { + g >>= 16 + } else { + g = ^(g >> 31) + } + + b := yy1 + 116130*cb1 + if uint32(b)&0xff000000 == 0 { + b >>= 16 + } else { + b = ^(b >> 31) + } + + d := dst[j : j+4 : j+4] + d[0] = uint8(r) + d[1] = uint8(g) + d[2] = uint8(b) + d[3] = 0xff + + iy++ + j += 4 + } + } + + case *image.Paletted: + j := 0 + for y := y1; y < y2; y++ { + i := y*img.Stride + x1 + for x := x1; x < x2; x++ { + c := s.palette[img.Pix[i]] + d := dst[j : j+4 : j+4] + d[0] = c.R + d[1] = c.G + d[2] = c.B + d[3] = c.A + j += 4 + i++ + } + } + + default: + j := 0 + b := s.image.Bounds() + x1 += b.Min.X + x2 += b.Min.X + y1 += b.Min.Y + y2 += b.Min.Y + for y := y1; y < y2; y++ { + for x := x1; x < x2; x++ { + r16, g16, b16, a16 := s.image.At(x, y).RGBA() + d := dst[j : j+4 : j+4] + switch a16 { + case 0xffff: + d[0] = uint8(r16 >> 8) + d[1] = uint8(g16 >> 8) + d[2] = uint8(b16 >> 8) + d[3] = 0xff + case 0: + d[0] = 0 + d[1] = 0 + d[2] = 0 + d[3] = 0 + default: + d[0] = uint8(((r16 * 0xffff) / a16) >> 8) + d[1] = uint8(((g16 * 0xffff) / a16) >> 8) + d[2] = uint8(((b16 * 0xffff) / a16) >> 8) + d[3] = uint8(a16 >> 8) + } + j += 4 + } + } + } +} diff --git a/vendor/github.com/disintegration/imaging/tools.go b/vendor/github.com/disintegration/imaging/tools.go new file mode 100644 index 000000000..0ec19a039 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/tools.go @@ -0,0 +1,249 @@ +package imaging + +import ( + "bytes" + "image" + "image/color" + "math" +) + +// New creates a new image with the specified width and height, and fills it with the specified color. +func New(width, height int, fillColor color.Color) *image.NRGBA { + if width <= 0 || height <= 0 { + return &image.NRGBA{} + } + + c := color.NRGBAModel.Convert(fillColor).(color.NRGBA) + if (c == color.NRGBA{0, 0, 0, 0}) { + return image.NewNRGBA(image.Rect(0, 0, width, height)) + } + + return &image.NRGBA{ + Pix: bytes.Repeat([]byte{c.R, c.G, c.B, c.A}, width*height), + Stride: 4 * width, + Rect: image.Rect(0, 0, width, height), + } +} + +// Clone returns a copy of the given image. +func Clone(img image.Image) *image.NRGBA { + src := newScanner(img) + dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h)) + size := src.w * 4 + parallel(0, src.h, func(ys <-chan int) { + for y := range ys { + i := y * dst.Stride + src.scan(0, y, src.w, y+1, dst.Pix[i:i+size]) + } + }) + return dst +} + +// Anchor is the anchor point for image alignment. +type Anchor int + +// Anchor point positions. +const ( + Center Anchor = iota + TopLeft + Top + TopRight + Left + Right + BottomLeft + Bottom + BottomRight +) + +func anchorPt(b image.Rectangle, w, h int, anchor Anchor) image.Point { + var x, y int + switch anchor { + case TopLeft: + x = b.Min.X + y = b.Min.Y + case Top: + x = b.Min.X + (b.Dx()-w)/2 + y = b.Min.Y + case TopRight: + x = b.Max.X - w + y = b.Min.Y + case Left: + x = b.Min.X + y = b.Min.Y + (b.Dy()-h)/2 + case Right: + x = b.Max.X - w + y = b.Min.Y + (b.Dy()-h)/2 + case BottomLeft: + x = b.Min.X + y = b.Max.Y - h + case Bottom: + x = b.Min.X + (b.Dx()-w)/2 + y = b.Max.Y - h + case BottomRight: + x = b.Max.X - w + y = b.Max.Y - h + default: + x = b.Min.X + (b.Dx()-w)/2 + y = b.Min.Y + (b.Dy()-h)/2 + } + return image.Pt(x, y) +} + +// Crop cuts out a rectangular region with the specified bounds +// from the image and returns the cropped image. +func Crop(img image.Image, rect image.Rectangle) *image.NRGBA { + r := rect.Intersect(img.Bounds()).Sub(img.Bounds().Min) + if r.Empty() { + return &image.NRGBA{} + } + src := newScanner(img) + dst := image.NewNRGBA(image.Rect(0, 0, r.Dx(), r.Dy())) + rowSize := r.Dx() * 4 + parallel(r.Min.Y, r.Max.Y, func(ys <-chan int) { + for y := range ys { + i := (y - r.Min.Y) * dst.Stride + src.scan(r.Min.X, y, r.Max.X, y+1, dst.Pix[i:i+rowSize]) + } + }) + return dst +} + +// CropAnchor cuts out a rectangular region with the specified size +// from the image using the specified anchor point and returns the cropped image. +func CropAnchor(img image.Image, width, height int, anchor Anchor) *image.NRGBA { + srcBounds := img.Bounds() + pt := anchorPt(srcBounds, width, height, anchor) + r := image.Rect(0, 0, width, height).Add(pt) + b := srcBounds.Intersect(r) + return Crop(img, b) +} + +// CropCenter cuts out a rectangular region with the specified size +// from the center of the image and returns the cropped image. +func CropCenter(img image.Image, width, height int) *image.NRGBA { + return CropAnchor(img, width, height, Center) +} + +// Paste pastes the img image to the background image at the specified position and returns the combined image. +func Paste(background, img image.Image, pos image.Point) *image.NRGBA { + dst := Clone(background) + pos = pos.Sub(background.Bounds().Min) + pasteRect := image.Rectangle{Min: pos, Max: pos.Add(img.Bounds().Size())} + interRect := pasteRect.Intersect(dst.Bounds()) + if interRect.Empty() { + return dst + } + src := newScanner(img) + parallel(interRect.Min.Y, interRect.Max.Y, func(ys <-chan int) { + for y := range ys { + x1 := interRect.Min.X - pasteRect.Min.X + x2 := interRect.Max.X - pasteRect.Min.X + y1 := y - pasteRect.Min.Y + y2 := y1 + 1 + i1 := y*dst.Stride + interRect.Min.X*4 + i2 := i1 + interRect.Dx()*4 + src.scan(x1, y1, x2, y2, dst.Pix[i1:i2]) + } + }) + return dst +} + +// PasteCenter pastes the img image to the center of the background image and returns the combined image. +func PasteCenter(background, img image.Image) *image.NRGBA { + bgBounds := background.Bounds() + bgW := bgBounds.Dx() + bgH := bgBounds.Dy() + bgMinX := bgBounds.Min.X + bgMinY := bgBounds.Min.Y + + centerX := bgMinX + bgW/2 + centerY := bgMinY + bgH/2 + + x0 := centerX - img.Bounds().Dx()/2 + y0 := centerY - img.Bounds().Dy()/2 + + return Paste(background, img, image.Pt(x0, y0)) +} + +// Overlay draws the img image over the background image at given position +// and returns the combined image. Opacity parameter is the opacity of the img +// image layer, used to compose the images, it must be from 0.0 to 1.0. +// +// Examples: +// +// // Draw spriteImage over backgroundImage at the given position (x=50, y=50). +// dstImage := imaging.Overlay(backgroundImage, spriteImage, image.Pt(50, 50), 1.0) +// +// // Blend two opaque images of the same size. +// dstImage := imaging.Overlay(imageOne, imageTwo, image.Pt(0, 0), 0.5) +// +func Overlay(background, img image.Image, pos image.Point, opacity float64) *image.NRGBA { + opacity = math.Min(math.Max(opacity, 0.0), 1.0) // Ensure 0.0 <= opacity <= 1.0. + dst := Clone(background) + pos = pos.Sub(background.Bounds().Min) + pasteRect := image.Rectangle{Min: pos, Max: pos.Add(img.Bounds().Size())} + interRect := pasteRect.Intersect(dst.Bounds()) + if interRect.Empty() { + return dst + } + src := newScanner(img) + parallel(interRect.Min.Y, interRect.Max.Y, func(ys <-chan int) { + scanLine := make([]uint8, interRect.Dx()*4) + for y := range ys { + x1 := interRect.Min.X - pasteRect.Min.X + x2 := interRect.Max.X - pasteRect.Min.X + y1 := y - pasteRect.Min.Y + y2 := y1 + 1 + src.scan(x1, y1, x2, y2, scanLine) + i := y*dst.Stride + interRect.Min.X*4 + j := 0 + for x := interRect.Min.X; x < interRect.Max.X; x++ { + d := dst.Pix[i : i+4 : i+4] + r1 := float64(d[0]) + g1 := float64(d[1]) + b1 := float64(d[2]) + a1 := float64(d[3]) + + s := scanLine[j : j+4 : j+4] + r2 := float64(s[0]) + g2 := float64(s[1]) + b2 := float64(s[2]) + a2 := float64(s[3]) + + coef2 := opacity * a2 / 255 + coef1 := (1 - coef2) * a1 / 255 + coefSum := coef1 + coef2 + coef1 /= coefSum + coef2 /= coefSum + + d[0] = uint8(r1*coef1 + r2*coef2) + d[1] = uint8(g1*coef1 + g2*coef2) + d[2] = uint8(b1*coef1 + b2*coef2) + d[3] = uint8(math.Min(a1+a2*opacity*(255-a1)/255, 255)) + + i += 4 + j += 4 + } + } + }) + return dst +} + +// OverlayCenter overlays the img image to the center of the background image and +// returns the combined image. Opacity parameter is the opacity of the img +// image layer, used to compose the images, it must be from 0.0 to 1.0. +func OverlayCenter(background, img image.Image, opacity float64) *image.NRGBA { + bgBounds := background.Bounds() + bgW := bgBounds.Dx() + bgH := bgBounds.Dy() + bgMinX := bgBounds.Min.X + bgMinY := bgBounds.Min.Y + + centerX := bgMinX + bgW/2 + centerY := bgMinY + bgH/2 + + x0 := centerX - img.Bounds().Dx()/2 + y0 := centerY - img.Bounds().Dy()/2 + + return Overlay(background, img, image.Point{x0, y0}, opacity) +} diff --git a/vendor/github.com/disintegration/imaging/transform.go b/vendor/github.com/disintegration/imaging/transform.go new file mode 100644 index 000000000..fe4a92f9d --- /dev/null +++ b/vendor/github.com/disintegration/imaging/transform.go @@ -0,0 +1,268 @@ +package imaging + +import ( + "image" + "image/color" + "math" +) + +// FlipH flips the image horizontally (from left to right) and returns the transformed image. +func FlipH(img image.Image) *image.NRGBA { + src := newScanner(img) + dstW := src.w + dstH := src.h + rowSize := dstW * 4 + dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH)) + parallel(0, dstH, func(ys <-chan int) { + for dstY := range ys { + i := dstY * dst.Stride + srcY := dstY + src.scan(0, srcY, src.w, srcY+1, dst.Pix[i:i+rowSize]) + reverse(dst.Pix[i : i+rowSize]) + } + }) + return dst +} + +// FlipV flips the image vertically (from top to bottom) and returns the transformed image. +func FlipV(img image.Image) *image.NRGBA { + src := newScanner(img) + dstW := src.w + dstH := src.h + rowSize := dstW * 4 + dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH)) + parallel(0, dstH, func(ys <-chan int) { + for dstY := range ys { + i := dstY * dst.Stride + srcY := dstH - dstY - 1 + src.scan(0, srcY, src.w, srcY+1, dst.Pix[i:i+rowSize]) + } + }) + return dst +} + +// Transpose flips the image horizontally and rotates 90 degrees counter-clockwise. +func Transpose(img image.Image) *image.NRGBA { + src := newScanner(img) + dstW := src.h + dstH := src.w + rowSize := dstW * 4 + dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH)) + parallel(0, dstH, func(ys <-chan int) { + for dstY := range ys { + i := dstY * dst.Stride + srcX := dstY + src.scan(srcX, 0, srcX+1, src.h, dst.Pix[i:i+rowSize]) + } + }) + return dst +} + +// Transverse flips the image vertically and rotates 90 degrees counter-clockwise. +func Transverse(img image.Image) *image.NRGBA { + src := newScanner(img) + dstW := src.h + dstH := src.w + rowSize := dstW * 4 + dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH)) + parallel(0, dstH, func(ys <-chan int) { + for dstY := range ys { + i := dstY * dst.Stride + srcX := dstH - dstY - 1 + src.scan(srcX, 0, srcX+1, src.h, dst.Pix[i:i+rowSize]) + reverse(dst.Pix[i : i+rowSize]) + } + }) + return dst +} + +// Rotate90 rotates the image 90 degrees counter-clockwise and returns the transformed image. +func Rotate90(img image.Image) *image.NRGBA { + src := newScanner(img) + dstW := src.h + dstH := src.w + rowSize := dstW * 4 + dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH)) + parallel(0, dstH, func(ys <-chan int) { + for dstY := range ys { + i := dstY * dst.Stride + srcX := dstH - dstY - 1 + src.scan(srcX, 0, srcX+1, src.h, dst.Pix[i:i+rowSize]) + } + }) + return dst +} + +// Rotate180 rotates the image 180 degrees counter-clockwise and returns the transformed image. +func Rotate180(img image.Image) *image.NRGBA { + src := newScanner(img) + dstW := src.w + dstH := src.h + rowSize := dstW * 4 + dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH)) + parallel(0, dstH, func(ys <-chan int) { + for dstY := range ys { + i := dstY * dst.Stride + srcY := dstH - dstY - 1 + src.scan(0, srcY, src.w, srcY+1, dst.Pix[i:i+rowSize]) + reverse(dst.Pix[i : i+rowSize]) + } + }) + return dst +} + +// Rotate270 rotates the image 270 degrees counter-clockwise and returns the transformed image. +func Rotate270(img image.Image) *image.NRGBA { + src := newScanner(img) + dstW := src.h + dstH := src.w + rowSize := dstW * 4 + dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH)) + parallel(0, dstH, func(ys <-chan int) { + for dstY := range ys { + i := dstY * dst.Stride + srcX := dstY + src.scan(srcX, 0, srcX+1, src.h, dst.Pix[i:i+rowSize]) + reverse(dst.Pix[i : i+rowSize]) + } + }) + return dst +} + +// Rotate rotates an image by the given angle counter-clockwise . +// The angle parameter is the rotation angle in degrees. +// The bgColor parameter specifies the color of the uncovered zone after the rotation. +func Rotate(img image.Image, angle float64, bgColor color.Color) *image.NRGBA { + angle = angle - math.Floor(angle/360)*360 + + switch angle { + case 0: + return Clone(img) + case 90: + return Rotate90(img) + case 180: + return Rotate180(img) + case 270: + return Rotate270(img) + } + + src := toNRGBA(img) + srcW := src.Bounds().Max.X + srcH := src.Bounds().Max.Y + dstW, dstH := rotatedSize(srcW, srcH, angle) + dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH)) + + if dstW <= 0 || dstH <= 0 { + return dst + } + + srcXOff := float64(srcW)/2 - 0.5 + srcYOff := float64(srcH)/2 - 0.5 + dstXOff := float64(dstW)/2 - 0.5 + dstYOff := float64(dstH)/2 - 0.5 + + bgColorNRGBA := color.NRGBAModel.Convert(bgColor).(color.NRGBA) + sin, cos := math.Sincos(math.Pi * angle / 180) + + parallel(0, dstH, func(ys <-chan int) { + for dstY := range ys { + for dstX := 0; dstX < dstW; dstX++ { + xf, yf := rotatePoint(float64(dstX)-dstXOff, float64(dstY)-dstYOff, sin, cos) + xf, yf = xf+srcXOff, yf+srcYOff + interpolatePoint(dst, dstX, dstY, src, xf, yf, bgColorNRGBA) + } + } + }) + + return dst +} + +func rotatePoint(x, y, sin, cos float64) (float64, float64) { + return x*cos - y*sin, x*sin + y*cos +} + +func rotatedSize(w, h int, angle float64) (int, int) { + if w <= 0 || h <= 0 { + return 0, 0 + } + + sin, cos := math.Sincos(math.Pi * angle / 180) + x1, y1 := rotatePoint(float64(w-1), 0, sin, cos) + x2, y2 := rotatePoint(float64(w-1), float64(h-1), sin, cos) + x3, y3 := rotatePoint(0, float64(h-1), sin, cos) + + minx := math.Min(x1, math.Min(x2, math.Min(x3, 0))) + maxx := math.Max(x1, math.Max(x2, math.Max(x3, 0))) + miny := math.Min(y1, math.Min(y2, math.Min(y3, 0))) + maxy := math.Max(y1, math.Max(y2, math.Max(y3, 0))) + + neww := maxx - minx + 1 + if neww-math.Floor(neww) > 0.1 { + neww++ + } + newh := maxy - miny + 1 + if newh-math.Floor(newh) > 0.1 { + newh++ + } + + return int(neww), int(newh) +} + +func interpolatePoint(dst *image.NRGBA, dstX, dstY int, src *image.NRGBA, xf, yf float64, bgColor color.NRGBA) { + j := dstY*dst.Stride + dstX*4 + d := dst.Pix[j : j+4 : j+4] + + x0 := int(math.Floor(xf)) + y0 := int(math.Floor(yf)) + bounds := src.Bounds() + if !image.Pt(x0, y0).In(image.Rect(bounds.Min.X-1, bounds.Min.Y-1, bounds.Max.X, bounds.Max.Y)) { + d[0] = bgColor.R + d[1] = bgColor.G + d[2] = bgColor.B + d[3] = bgColor.A + return + } + + xq := xf - float64(x0) + yq := yf - float64(y0) + points := [4]image.Point{ + {x0, y0}, + {x0 + 1, y0}, + {x0, y0 + 1}, + {x0 + 1, y0 + 1}, + } + weights := [4]float64{ + (1 - xq) * (1 - yq), + xq * (1 - yq), + (1 - xq) * yq, + xq * yq, + } + + var r, g, b, a float64 + for i := 0; i < 4; i++ { + p := points[i] + w := weights[i] + if p.In(bounds) { + i := p.Y*src.Stride + p.X*4 + s := src.Pix[i : i+4 : i+4] + wa := float64(s[3]) * w + r += float64(s[0]) * wa + g += float64(s[1]) * wa + b += float64(s[2]) * wa + a += wa + } else { + wa := float64(bgColor.A) * w + r += float64(bgColor.R) * wa + g += float64(bgColor.G) * wa + b += float64(bgColor.B) * wa + a += wa + } + } + if a != 0 { + aInv := 1 / a + d[0] = clamp(r * aInv) + d[1] = clamp(g * aInv) + d[2] = clamp(b * aInv) + d[3] = clamp(a) + } +} diff --git a/vendor/github.com/disintegration/imaging/utils.go b/vendor/github.com/disintegration/imaging/utils.go new file mode 100644 index 000000000..6c7af1a51 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/utils.go @@ -0,0 +1,167 @@ +package imaging + +import ( + "image" + "math" + "runtime" + "sync" +) + +// parallel processes the data in separate goroutines. +func parallel(start, stop int, fn func(<-chan int)) { + count := stop - start + if count < 1 { + return + } + + procs := runtime.GOMAXPROCS(0) + if procs > count { + procs = count + } + + c := make(chan int, count) + for i := start; i < stop; i++ { + c <- i + } + close(c) + + var wg sync.WaitGroup + for i := 0; i < procs; i++ { + wg.Add(1) + go func() { + defer wg.Done() + fn(c) + }() + } + wg.Wait() +} + +// absint returns the absolute value of i. +func absint(i int) int { + if i < 0 { + return -i + } + return i +} + +// clamp rounds and clamps float64 value to fit into uint8. +func clamp(x float64) uint8 { + v := int64(x + 0.5) + if v > 255 { + return 255 + } + if v > 0 { + return uint8(v) + } + return 0 +} + +func reverse(pix []uint8) { + if len(pix) <= 4 { + return + } + i := 0 + j := len(pix) - 4 + for i < j { + pi := pix[i : i+4 : i+4] + pj := pix[j : j+4 : j+4] + pi[0], pj[0] = pj[0], pi[0] + pi[1], pj[1] = pj[1], pi[1] + pi[2], pj[2] = pj[2], pi[2] + pi[3], pj[3] = pj[3], pi[3] + i += 4 + j -= 4 + } +} + +func toNRGBA(img image.Image) *image.NRGBA { + if img, ok := img.(*image.NRGBA); ok { + return &image.NRGBA{ + Pix: img.Pix, + Stride: img.Stride, + Rect: img.Rect.Sub(img.Rect.Min), + } + } + return Clone(img) +} + +// rgbToHSL converts a color from RGB to HSL. +func rgbToHSL(r, g, b uint8) (float64, float64, float64) { + rr := float64(r) / 255 + gg := float64(g) / 255 + bb := float64(b) / 255 + + max := math.Max(rr, math.Max(gg, bb)) + min := math.Min(rr, math.Min(gg, bb)) + + l := (max + min) / 2 + + if max == min { + return 0, 0, l + } + + var h, s float64 + d := max - min + if l > 0.5 { + s = d / (2 - max - min) + } else { + s = d / (max + min) + } + + switch max { + case rr: + h = (gg - bb) / d + if g < b { + h += 6 + } + case gg: + h = (bb-rr)/d + 2 + case bb: + h = (rr-gg)/d + 4 + } + h /= 6 + + return h, s, l +} + +// hslToRGB converts a color from HSL to RGB. +func hslToRGB(h, s, l float64) (uint8, uint8, uint8) { + var r, g, b float64 + if s == 0 { + v := clamp(l * 255) + return v, v, v + } + + var q float64 + if l < 0.5 { + q = l * (1 + s) + } else { + q = l + s - l*s + } + p := 2*l - q + + r = hueToRGB(p, q, h+1/3.0) + g = hueToRGB(p, q, h) + b = hueToRGB(p, q, h-1/3.0) + + return clamp(r * 255), clamp(g * 255), clamp(b * 255) +} + +func hueToRGB(p, q, t float64) float64 { + if t < 0 { + t++ + } + if t > 1 { + t-- + } + if t < 1/6.0 { + return p + (q-p)*6*t + } + if t < 1/2.0 { + return q + } + if t < 2/3.0 { + return p + (q-p)*(2/3.0-t)*6 + } + return p +} diff --git a/vendor/golang.org/x/image/AUTHORS b/vendor/golang.org/x/image/AUTHORS new file mode 100644 index 000000000..15167cd74 --- /dev/null +++ b/vendor/golang.org/x/image/AUTHORS @@ -0,0 +1,3 @@ +# This source code refers to The Go Authors for copyright purposes. +# The master list of authors is in the main Go distribution, +# visible at http://tip.golang.org/AUTHORS. diff --git a/vendor/golang.org/x/image/CONTRIBUTORS b/vendor/golang.org/x/image/CONTRIBUTORS new file mode 100644 index 000000000..1c4577e96 --- /dev/null +++ b/vendor/golang.org/x/image/CONTRIBUTORS @@ -0,0 +1,3 @@ +# This source code was written by the Go contributors. +# The master list of contributors is in the main Go distribution, +# visible at http://tip.golang.org/CONTRIBUTORS. diff --git a/vendor/golang.org/x/image/LICENSE b/vendor/golang.org/x/image/LICENSE new file mode 100644 index 000000000..6a66aea5e --- /dev/null +++ b/vendor/golang.org/x/image/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/image/PATENTS b/vendor/golang.org/x/image/PATENTS new file mode 100644 index 000000000..733099041 --- /dev/null +++ b/vendor/golang.org/x/image/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/image/bmp/reader.go b/vendor/golang.org/x/image/bmp/reader.go new file mode 100644 index 000000000..c10a022f6 --- /dev/null +++ b/vendor/golang.org/x/image/bmp/reader.go @@ -0,0 +1,213 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package bmp implements a BMP image decoder and encoder. +// +// The BMP specification is at http://www.digicamsoft.com/bmp/bmp.html. +package bmp // import "golang.org/x/image/bmp" + +import ( + "errors" + "image" + "image/color" + "io" +) + +// ErrUnsupported means that the input BMP image uses a valid but unsupported +// feature. +var ErrUnsupported = errors.New("bmp: unsupported BMP image") + +func readUint16(b []byte) uint16 { + return uint16(b[0]) | uint16(b[1])<<8 +} + +func readUint32(b []byte) uint32 { + return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 +} + +// decodePaletted reads an 8 bit-per-pixel BMP image from r. +// If topDown is false, the image rows will be read bottom-up. +func decodePaletted(r io.Reader, c image.Config, topDown bool) (image.Image, error) { + paletted := image.NewPaletted(image.Rect(0, 0, c.Width, c.Height), c.ColorModel.(color.Palette)) + if c.Width == 0 || c.Height == 0 { + return paletted, nil + } + var tmp [4]byte + y0, y1, yDelta := c.Height-1, -1, -1 + if topDown { + y0, y1, yDelta = 0, c.Height, +1 + } + for y := y0; y != y1; y += yDelta { + p := paletted.Pix[y*paletted.Stride : y*paletted.Stride+c.Width] + if _, err := io.ReadFull(r, p); err != nil { + return nil, err + } + // Each row is 4-byte aligned. + if c.Width%4 != 0 { + _, err := io.ReadFull(r, tmp[:4-c.Width%4]) + if err != nil { + return nil, err + } + } + } + return paletted, nil +} + +// decodeRGB reads a 24 bit-per-pixel BMP image from r. +// If topDown is false, the image rows will be read bottom-up. +func decodeRGB(r io.Reader, c image.Config, topDown bool) (image.Image, error) { + rgba := image.NewRGBA(image.Rect(0, 0, c.Width, c.Height)) + if c.Width == 0 || c.Height == 0 { + return rgba, nil + } + // There are 3 bytes per pixel, and each row is 4-byte aligned. + b := make([]byte, (3*c.Width+3)&^3) + y0, y1, yDelta := c.Height-1, -1, -1 + if topDown { + y0, y1, yDelta = 0, c.Height, +1 + } + for y := y0; y != y1; y += yDelta { + if _, err := io.ReadFull(r, b); err != nil { + return nil, err + } + p := rgba.Pix[y*rgba.Stride : y*rgba.Stride+c.Width*4] + for i, j := 0, 0; i < len(p); i, j = i+4, j+3 { + // BMP images are stored in BGR order rather than RGB order. + p[i+0] = b[j+2] + p[i+1] = b[j+1] + p[i+2] = b[j+0] + p[i+3] = 0xFF + } + } + return rgba, nil +} + +// decodeNRGBA reads a 32 bit-per-pixel BMP image from r. +// If topDown is false, the image rows will be read bottom-up. +func decodeNRGBA(r io.Reader, c image.Config, topDown bool) (image.Image, error) { + rgba := image.NewNRGBA(image.Rect(0, 0, c.Width, c.Height)) + if c.Width == 0 || c.Height == 0 { + return rgba, nil + } + y0, y1, yDelta := c.Height-1, -1, -1 + if topDown { + y0, y1, yDelta = 0, c.Height, +1 + } + for y := y0; y != y1; y += yDelta { + p := rgba.Pix[y*rgba.Stride : y*rgba.Stride+c.Width*4] + if _, err := io.ReadFull(r, p); err != nil { + return nil, err + } + for i := 0; i < len(p); i += 4 { + // BMP images are stored in BGRA order rather than RGBA order. + p[i+0], p[i+2] = p[i+2], p[i+0] + } + } + return rgba, nil +} + +// Decode reads a BMP image from r and returns it as an image.Image. +// Limitation: The file must be 8, 24 or 32 bits per pixel. +func Decode(r io.Reader) (image.Image, error) { + c, bpp, topDown, err := decodeConfig(r) + if err != nil { + return nil, err + } + switch bpp { + case 8: + return decodePaletted(r, c, topDown) + case 24: + return decodeRGB(r, c, topDown) + case 32: + return decodeNRGBA(r, c, topDown) + } + panic("unreachable") +} + +// DecodeConfig returns the color model and dimensions of a BMP image without +// decoding the entire image. +// Limitation: The file must be 8, 24 or 32 bits per pixel. +func DecodeConfig(r io.Reader) (image.Config, error) { + config, _, _, err := decodeConfig(r) + return config, err +} + +func decodeConfig(r io.Reader) (config image.Config, bitsPerPixel int, topDown bool, err error) { + // We only support those BMP images that are a BITMAPFILEHEADER + // immediately followed by a BITMAPINFOHEADER. + const ( + fileHeaderLen = 14 + infoHeaderLen = 40 + v4InfoHeaderLen = 108 + v5InfoHeaderLen = 124 + ) + var b [1024]byte + if _, err := io.ReadFull(r, b[:fileHeaderLen+4]); err != nil { + return image.Config{}, 0, false, err + } + if string(b[:2]) != "BM" { + return image.Config{}, 0, false, errors.New("bmp: invalid format") + } + offset := readUint32(b[10:14]) + infoLen := readUint32(b[14:18]) + if infoLen != infoHeaderLen && infoLen != v4InfoHeaderLen && infoLen != v5InfoHeaderLen { + return image.Config{}, 0, false, ErrUnsupported + } + if _, err := io.ReadFull(r, b[fileHeaderLen+4:fileHeaderLen+infoLen]); err != nil { + return image.Config{}, 0, false, err + } + width := int(int32(readUint32(b[18:22]))) + height := int(int32(readUint32(b[22:26]))) + if height < 0 { + height, topDown = -height, true + } + if width < 0 || height < 0 { + return image.Config{}, 0, false, ErrUnsupported + } + // We only support 1 plane and 8, 24 or 32 bits per pixel and no + // compression. + planes, bpp, compression := readUint16(b[26:28]), readUint16(b[28:30]), readUint32(b[30:34]) + // if compression is set to BITFIELDS, but the bitmask is set to the default bitmask + // that would be used if compression was set to 0, we can continue as if compression was 0 + if compression == 3 && infoLen > infoHeaderLen && + readUint32(b[54:58]) == 0xff0000 && readUint32(b[58:62]) == 0xff00 && + readUint32(b[62:66]) == 0xff && readUint32(b[66:70]) == 0xff000000 { + compression = 0 + } + if planes != 1 || compression != 0 { + return image.Config{}, 0, false, ErrUnsupported + } + switch bpp { + case 8: + if offset != fileHeaderLen+infoLen+256*4 { + return image.Config{}, 0, false, ErrUnsupported + } + _, err = io.ReadFull(r, b[:256*4]) + if err != nil { + return image.Config{}, 0, false, err + } + pcm := make(color.Palette, 256) + for i := range pcm { + // BMP images are stored in BGR order rather than RGB order. + // Every 4th byte is padding. + pcm[i] = color.RGBA{b[4*i+2], b[4*i+1], b[4*i+0], 0xFF} + } + return image.Config{ColorModel: pcm, Width: width, Height: height}, 8, topDown, nil + case 24: + if offset != fileHeaderLen+infoLen { + return image.Config{}, 0, false, ErrUnsupported + } + return image.Config{ColorModel: color.RGBAModel, Width: width, Height: height}, 24, topDown, nil + case 32: + if offset != fileHeaderLen+infoLen { + return image.Config{}, 0, false, ErrUnsupported + } + return image.Config{ColorModel: color.RGBAModel, Width: width, Height: height}, 32, topDown, nil + } + return image.Config{}, 0, false, ErrUnsupported +} + +func init() { + image.RegisterFormat("bmp", "BM????\x00\x00\x00\x00", Decode, DecodeConfig) +} diff --git a/vendor/golang.org/x/image/bmp/writer.go b/vendor/golang.org/x/image/bmp/writer.go new file mode 100644 index 000000000..f07b39dba --- /dev/null +++ b/vendor/golang.org/x/image/bmp/writer.go @@ -0,0 +1,262 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bmp + +import ( + "encoding/binary" + "errors" + "image" + "io" +) + +type header struct { + sigBM [2]byte + fileSize uint32 + resverved [2]uint16 + pixOffset uint32 + dibHeaderSize uint32 + width uint32 + height uint32 + colorPlane uint16 + bpp uint16 + compression uint32 + imageSize uint32 + xPixelsPerMeter uint32 + yPixelsPerMeter uint32 + colorUse uint32 + colorImportant uint32 +} + +func encodePaletted(w io.Writer, pix []uint8, dx, dy, stride, step int) error { + var padding []byte + if dx < step { + padding = make([]byte, step-dx) + } + for y := dy - 1; y >= 0; y-- { + min := y*stride + 0 + max := y*stride + dx + if _, err := w.Write(pix[min:max]); err != nil { + return err + } + if padding != nil { + if _, err := w.Write(padding); err != nil { + return err + } + } + } + return nil +} + +func encodeRGBA(w io.Writer, pix []uint8, dx, dy, stride, step int, opaque bool) error { + buf := make([]byte, step) + if opaque { + for y := dy - 1; y >= 0; y-- { + min := y*stride + 0 + max := y*stride + dx*4 + off := 0 + for i := min; i < max; i += 4 { + buf[off+2] = pix[i+0] + buf[off+1] = pix[i+1] + buf[off+0] = pix[i+2] + off += 3 + } + if _, err := w.Write(buf); err != nil { + return err + } + } + } else { + for y := dy - 1; y >= 0; y-- { + min := y*stride + 0 + max := y*stride + dx*4 + off := 0 + for i := min; i < max; i += 4 { + a := uint32(pix[i+3]) + if a == 0 { + buf[off+2] = 0 + buf[off+1] = 0 + buf[off+0] = 0 + buf[off+3] = 0 + off += 4 + continue + } else if a == 0xff { + buf[off+2] = pix[i+0] + buf[off+1] = pix[i+1] + buf[off+0] = pix[i+2] + buf[off+3] = 0xff + off += 4 + continue + } + buf[off+2] = uint8(((uint32(pix[i+0]) * 0xffff) / a) >> 8) + buf[off+1] = uint8(((uint32(pix[i+1]) * 0xffff) / a) >> 8) + buf[off+0] = uint8(((uint32(pix[i+2]) * 0xffff) / a) >> 8) + buf[off+3] = uint8(a) + off += 4 + } + if _, err := w.Write(buf); err != nil { + return err + } + } + } + return nil +} + +func encodeNRGBA(w io.Writer, pix []uint8, dx, dy, stride, step int, opaque bool) error { + buf := make([]byte, step) + if opaque { + for y := dy - 1; y >= 0; y-- { + min := y*stride + 0 + max := y*stride + dx*4 + off := 0 + for i := min; i < max; i += 4 { + buf[off+2] = pix[i+0] + buf[off+1] = pix[i+1] + buf[off+0] = pix[i+2] + off += 3 + } + if _, err := w.Write(buf); err != nil { + return err + } + } + } else { + for y := dy - 1; y >= 0; y-- { + min := y*stride + 0 + max := y*stride + dx*4 + off := 0 + for i := min; i < max; i += 4 { + buf[off+2] = pix[i+0] + buf[off+1] = pix[i+1] + buf[off+0] = pix[i+2] + buf[off+3] = pix[i+3] + off += 4 + } + if _, err := w.Write(buf); err != nil { + return err + } + } + } + return nil +} + +func encode(w io.Writer, m image.Image, step int) error { + b := m.Bounds() + buf := make([]byte, step) + for y := b.Max.Y - 1; y >= b.Min.Y; y-- { + off := 0 + for x := b.Min.X; x < b.Max.X; x++ { + r, g, b, _ := m.At(x, y).RGBA() + buf[off+2] = byte(r >> 8) + buf[off+1] = byte(g >> 8) + buf[off+0] = byte(b >> 8) + off += 3 + } + if _, err := w.Write(buf); err != nil { + return err + } + } + return nil +} + +// Encode writes the image m to w in BMP format. +func Encode(w io.Writer, m image.Image) error { + d := m.Bounds().Size() + if d.X < 0 || d.Y < 0 { + return errors.New("bmp: negative bounds") + } + h := &header{ + sigBM: [2]byte{'B', 'M'}, + fileSize: 14 + 40, + pixOffset: 14 + 40, + dibHeaderSize: 40, + width: uint32(d.X), + height: uint32(d.Y), + colorPlane: 1, + } + + var step int + var palette []byte + var opaque bool + switch m := m.(type) { + case *image.Gray: + step = (d.X + 3) &^ 3 + palette = make([]byte, 1024) + for i := 0; i < 256; i++ { + palette[i*4+0] = uint8(i) + palette[i*4+1] = uint8(i) + palette[i*4+2] = uint8(i) + palette[i*4+3] = 0xFF + } + h.imageSize = uint32(d.Y * step) + h.fileSize += uint32(len(palette)) + h.imageSize + h.pixOffset += uint32(len(palette)) + h.bpp = 8 + + case *image.Paletted: + step = (d.X + 3) &^ 3 + palette = make([]byte, 1024) + for i := 0; i < len(m.Palette) && i < 256; i++ { + r, g, b, _ := m.Palette[i].RGBA() + palette[i*4+0] = uint8(b >> 8) + palette[i*4+1] = uint8(g >> 8) + palette[i*4+2] = uint8(r >> 8) + palette[i*4+3] = 0xFF + } + h.imageSize = uint32(d.Y * step) + h.fileSize += uint32(len(palette)) + h.imageSize + h.pixOffset += uint32(len(palette)) + h.bpp = 8 + case *image.RGBA: + opaque = m.Opaque() + if opaque { + step = (3*d.X + 3) &^ 3 + h.bpp = 24 + } else { + step = 4 * d.X + h.bpp = 32 + } + h.imageSize = uint32(d.Y * step) + h.fileSize += h.imageSize + case *image.NRGBA: + opaque = m.Opaque() + if opaque { + step = (3*d.X + 3) &^ 3 + h.bpp = 24 + } else { + step = 4 * d.X + h.bpp = 32 + } + h.imageSize = uint32(d.Y * step) + h.fileSize += h.imageSize + default: + step = (3*d.X + 3) &^ 3 + h.imageSize = uint32(d.Y * step) + h.fileSize += h.imageSize + h.bpp = 24 + } + + if err := binary.Write(w, binary.LittleEndian, h); err != nil { + return err + } + if palette != nil { + if err := binary.Write(w, binary.LittleEndian, palette); err != nil { + return err + } + } + + if d.X == 0 || d.Y == 0 { + return nil + } + + switch m := m.(type) { + case *image.Gray: + return encodePaletted(w, m.Pix, d.X, d.Y, m.Stride, step) + case *image.Paletted: + return encodePaletted(w, m.Pix, d.X, d.Y, m.Stride, step) + case *image.RGBA: + return encodeRGBA(w, m.Pix, d.X, d.Y, m.Stride, step, opaque) + case *image.NRGBA: + return encodeNRGBA(w, m.Pix, d.X, d.Y, m.Stride, step, opaque) + } + return encode(w, m, step) +} diff --git a/vendor/golang.org/x/image/ccitt/reader.go b/vendor/golang.org/x/image/ccitt/reader.go new file mode 100644 index 000000000..16bd495d5 --- /dev/null +++ b/vendor/golang.org/x/image/ccitt/reader.go @@ -0,0 +1,697 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:generate go run gen.go + +// Package ccitt implements a CCITT (fax) image decoder. +package ccitt + +import ( + "encoding/binary" + "errors" + "image" + "io" + "math/bits" +) + +var ( + errInvalidBounds = errors.New("ccitt: invalid bounds") + errInvalidCode = errors.New("ccitt: invalid code") + errInvalidMode = errors.New("ccitt: invalid mode") + errInvalidOffset = errors.New("ccitt: invalid offset") + errMissingEOL = errors.New("ccitt: missing End-of-Line") + errRunLengthOverflowsWidth = errors.New("ccitt: run length overflows width") + errRunLengthTooLong = errors.New("ccitt: run length too long") + errUnsupportedMode = errors.New("ccitt: unsupported mode") + errUnsupportedSubFormat = errors.New("ccitt: unsupported sub-format") + errUnsupportedWidth = errors.New("ccitt: unsupported width") +) + +// Order specifies the bit ordering in a CCITT data stream. +type Order uint32 + +const ( + // LSB means Least Significant Bits first. + LSB Order = iota + // MSB means Most Significant Bits first. + MSB +) + +// SubFormat represents that the CCITT format consists of a number of +// sub-formats. Decoding or encoding a CCITT data stream requires knowing the +// sub-format context. It is not represented in the data stream per se. +type SubFormat uint32 + +const ( + Group3 SubFormat = iota + Group4 +) + +// Options are optional parameters. +type Options struct { + // Align means that some variable-bit-width codes are byte-aligned. + Align bool + // Invert means that black is the 1 bit or 0xFF byte, and white is 0. + Invert bool +} + +// maxWidth is the maximum (inclusive) supported width. This is a limitation of +// this implementation, to guard against integer overflow, and not anything +// inherent to the CCITT format. +const maxWidth = 1 << 20 + +func invertBytes(b []byte) { + for i, c := range b { + b[i] = ^c + } +} + +func reverseBitsWithinBytes(b []byte) { + for i, c := range b { + b[i] = bits.Reverse8(c) + } +} + +// highBits writes to dst (1 bit per pixel, most significant bit first) the +// high (0x80) bits from src (1 byte per pixel). It returns the number of bytes +// written and read such that dst[:d] is the packed form of src[:s]. +// +// For example, if src starts with the 8 bytes [0x7D, 0x7E, 0x7F, 0x80, 0x81, +// 0x82, 0x00, 0xFF] then 0x1D will be written to dst[0]. +// +// If src has (8 * len(dst)) or more bytes then only len(dst) bytes are +// written, (8 * len(dst)) bytes are read, and invert is ignored. +// +// Otherwise, if len(src) is not a multiple of 8 then the final byte written to +// dst is padded with 1 bits (if invert is true) or 0 bits. If inverted, the 1s +// are typically temporary, e.g. they will be flipped back to 0s by an +// invertBytes call in the highBits caller, reader.Read. +func highBits(dst []byte, src []byte, invert bool) (d int, s int) { + // Pack as many complete groups of 8 src bytes as we can. + n := len(src) / 8 + if n > len(dst) { + n = len(dst) + } + dstN := dst[:n] + for i := range dstN { + src8 := src[i*8 : i*8+8] + dstN[i] = ((src8[0] & 0x80) >> 0) | + ((src8[1] & 0x80) >> 1) | + ((src8[2] & 0x80) >> 2) | + ((src8[3] & 0x80) >> 3) | + ((src8[4] & 0x80) >> 4) | + ((src8[5] & 0x80) >> 5) | + ((src8[6] & 0x80) >> 6) | + ((src8[7] & 0x80) >> 7) + } + d, s = n, 8*n + dst, src = dst[d:], src[s:] + + // Pack up to 7 remaining src bytes, if there's room in dst. + if (len(dst) > 0) && (len(src) > 0) { + dstByte := byte(0) + if invert { + dstByte = 0xFF >> uint(len(src)) + } + for n, srcByte := range src { + dstByte |= (srcByte & 0x80) >> uint(n) + } + dst[0] = dstByte + d, s = d+1, s+len(src) + } + return d, s +} + +type bitReader struct { + r io.Reader + + // readErr is the error returned from the most recent r.Read call. As the + // io.Reader documentation says, when r.Read returns (n, err), "always + // process the n > 0 bytes returned before considering the error err". + readErr error + + // order is whether to process r's bytes LSB first or MSB first. + order Order + + // The high nBits bits of the bits field hold upcoming bits in MSB order. + bits uint64 + nBits uint32 + + // bytes[br:bw] holds bytes read from r but not yet loaded into bits. + br uint32 + bw uint32 + bytes [1024]uint8 +} + +func (b *bitReader) alignToByteBoundary() { + n := b.nBits & 7 + b.bits <<= n + b.nBits -= n +} + +// nextBitMaxNBits is the maximum possible value of bitReader.nBits after a +// bitReader.nextBit call, provided that bitReader.nBits was not more than this +// value before that call. +// +// Note that the decode function can unread bits, which can temporarily set the +// bitReader.nBits value above nextBitMaxNBits. +const nextBitMaxNBits = 31 + +func (b *bitReader) nextBit() (uint64, error) { + for { + if b.nBits > 0 { + bit := b.bits >> 63 + b.bits <<= 1 + b.nBits-- + return bit, nil + } + + if available := b.bw - b.br; available >= 4 { + // Read 32 bits, even though b.bits is a uint64, since the decode + // function may need to unread up to maxCodeLength bits, putting + // them back in the remaining (64 - 32) bits. TestMaxCodeLength + // checks that the generated maxCodeLength constant fits. + // + // If changing the Uint32 call, also change nextBitMaxNBits. + b.bits = uint64(binary.BigEndian.Uint32(b.bytes[b.br:])) << 32 + b.br += 4 + b.nBits = 32 + continue + } else if available > 0 { + b.bits = uint64(b.bytes[b.br]) << (7 * 8) + b.br++ + b.nBits = 8 + continue + } + + if b.readErr != nil { + return 0, b.readErr + } + + n, err := b.r.Read(b.bytes[:]) + b.br = 0 + b.bw = uint32(n) + b.readErr = err + + if b.order != MSB { + reverseBitsWithinBytes(b.bytes[:b.bw]) + } + } +} + +func decode(b *bitReader, decodeTable [][2]int16) (uint32, error) { + nBitsRead, bitsRead, state := uint32(0), uint64(0), int32(1) + for { + bit, err := b.nextBit() + if err != nil { + return 0, err + } + bitsRead |= bit << (63 - nBitsRead) + nBitsRead++ + // The "&1" is redundant, but can eliminate a bounds check. + state = int32(decodeTable[state][bit&1]) + if state < 0 { + return uint32(^state), nil + } else if state == 0 { + // Unread the bits we've read, then return errInvalidCode. + b.bits = (b.bits >> nBitsRead) | bitsRead + b.nBits += nBitsRead + return 0, errInvalidCode + } + } +} + +type reader struct { + br bitReader + subFormat SubFormat + + // width is the image width in pixels. + width int + + // rowsRemaining starts at the image height in pixels, when the reader is + // driven through the io.Reader interface, and decrements to zero as rows + // are decoded. When driven through DecodeIntoGray, this field is unused. + rowsRemaining int + + // curr and prev hold the current and previous rows. Each element is either + // 0x00 (black) or 0xFF (white). + // + // prev may be nil, when processing the first row. + curr []byte + prev []byte + + // ri is the read index. curr[:ri] are those bytes of curr that have been + // passed along via the Read method. + // + // When the reader is driven through DecodeIntoGray, instead of through the + // io.Reader interface, this field is unused. + ri int + + // wi is the write index. curr[:wi] are those bytes of curr that have + // already been decoded via the decodeRow method. + // + // What this implementation calls wi is roughly equivalent to what the spec + // calls the a0 index. + wi int + + // These fields are copied from the *Options (which may be nil). + align bool + invert bool + + // atStartOfRow is whether we have just started the row. Some parts of the + // spec say to treat this situation as if "wi = -1". + atStartOfRow bool + + // penColorIsWhite is whether the next run is black or white. + penColorIsWhite bool + + // seenStartOfImage is whether we've called the startDecode method. + seenStartOfImage bool + + // readErr is a sticky error for the Read method. + readErr error +} + +func (z *reader) Read(p []byte) (int, error) { + if z.readErr != nil { + return 0, z.readErr + } + originalP := p + + for len(p) > 0 { + // Allocate buffers (and decode any start-of-image codes), if + // processing the first or second row. + if z.curr == nil { + if !z.seenStartOfImage { + if z.readErr = z.startDecode(); z.readErr != nil { + break + } + z.atStartOfRow = true + } + z.curr = make([]byte, z.width) + } + + // Decode the next row, if necessary. + if z.atStartOfRow { + if z.rowsRemaining <= 0 { + if z.readErr = z.finishDecode(); z.readErr != nil { + break + } + z.readErr = io.EOF + break + } + if z.readErr = z.decodeRow(); z.readErr != nil { + break + } + z.rowsRemaining-- + } + + // Pack from z.curr (1 byte per pixel) to p (1 bit per pixel). + packD, packS := highBits(p, z.curr[z.ri:], z.invert) + p = p[packD:] + z.ri += packS + + // Prepare to decode the next row, if necessary. + if z.ri == len(z.curr) { + z.ri, z.curr, z.prev = 0, z.prev, z.curr + z.atStartOfRow = true + } + } + + n := len(originalP) - len(p) + if z.invert { + invertBytes(originalP[:n]) + } + return n, z.readErr +} + +func (z *reader) penColor() byte { + if z.penColorIsWhite { + return 0xFF + } + return 0x00 +} + +func (z *reader) startDecode() error { + switch z.subFormat { + case Group3: + if err := z.decodeEOL(); err != nil { + return err + } + + case Group4: + // No-op. + + default: + return errUnsupportedSubFormat + } + + z.seenStartOfImage = true + return nil +} + +func (z *reader) finishDecode() error { + numberOfEOLs := 0 + switch z.subFormat { + case Group3: + // The stream ends with a RTC (Return To Control) of 6 consecutive + // EOL's, but we should have already just seen an EOL, either in + // z.startDecode (for a zero-height image) or in z.decodeRow. + numberOfEOLs = 5 + + case Group4: + // The stream ends with two EOL's, the first of which is possibly + // byte-aligned. + numberOfEOLs = 2 + if err := z.decodeEOL(); err == nil { + numberOfEOLs-- + } else if err == errInvalidCode { + // Try again, this time starting from a byte boundary. + z.br.alignToByteBoundary() + } else { + return err + } + + default: + return errUnsupportedSubFormat + } + + for ; numberOfEOLs > 0; numberOfEOLs-- { + if err := z.decodeEOL(); err != nil { + return err + } + } + return nil +} + +func (z *reader) decodeEOL() error { + // TODO: EOL doesn't have to be in the modeDecodeTable. It could be in its + // own table, or we could just hard-code it, especially if we might need to + // cater for optional byte-alignment, or an arbitrary number (potentially + // more than 8) of 0-valued padding bits. + if mode, err := decode(&z.br, modeDecodeTable[:]); err != nil { + return err + } else if mode != modeEOL { + return errMissingEOL + } + return nil +} + +func (z *reader) decodeRow() error { + z.wi = 0 + z.atStartOfRow = true + z.penColorIsWhite = true + + if z.align { + z.br.alignToByteBoundary() + } + + switch z.subFormat { + case Group3: + for ; z.wi < len(z.curr); z.atStartOfRow = false { + if err := z.decodeRun(); err != nil { + return err + } + } + return z.decodeEOL() + + case Group4: + for ; z.wi < len(z.curr); z.atStartOfRow = false { + mode, err := decode(&z.br, modeDecodeTable[:]) + if err != nil { + return err + } + rm := readerMode{} + if mode < uint32(len(readerModes)) { + rm = readerModes[mode] + } + if rm.function == nil { + return errInvalidMode + } + if err := rm.function(z, rm.arg); err != nil { + return err + } + } + return nil + } + + return errUnsupportedSubFormat +} + +func (z *reader) decodeRun() error { + table := blackDecodeTable[:] + if z.penColorIsWhite { + table = whiteDecodeTable[:] + } + + total := 0 + for { + n, err := decode(&z.br, table) + if err != nil { + return err + } + if n > maxWidth { + panic("unreachable") + } + total += int(n) + if total > maxWidth { + return errRunLengthTooLong + } + // Anything 0x3F or below is a terminal code. + if n <= 0x3F { + break + } + } + + if total > (len(z.curr) - z.wi) { + return errRunLengthOverflowsWidth + } + dst := z.curr[z.wi : z.wi+total] + penColor := z.penColor() + for i := range dst { + dst[i] = penColor + } + z.wi += total + z.penColorIsWhite = !z.penColorIsWhite + + return nil +} + +// The various modes' semantics are based on determining a row of pixels' +// "changing elements": those pixels whose color differs from the one on its +// immediate left. +// +// The row above the first row is implicitly all white. Similarly, the column +// to the left of the first column is implicitly all white. +// +// For example, here's Figure 1 in "ITU-T Recommendation T.6", where the +// current and previous rows contain black (B) and white (w) pixels. The a? +// indexes point into curr, the b? indexes point into prev. +// +// b1 b2 +// v v +// prev: BBBBBwwwwwBBBwwwww +// curr: BBBwwwwwBBBBBBwwww +// ^ ^ ^ +// a0 a1 a2 +// +// a0 is the "reference element" or current decoder position, roughly +// equivalent to what this implementation calls reader.wi. +// +// a1 is the next changing element to the right of a0, on the "coding line" +// (the current row). +// +// a2 is the next changing element to the right of a1, again on curr. +// +// b1 is the first changing element on the "reference line" (the previous row) +// to the right of a0 and of opposite color to a0. +// +// b2 is the next changing element to the right of b1, again on prev. +// +// The various modes calculate a1 (and a2, for modeH): +// - modePass calculates that a1 is at or to the right of b2. +// - modeH calculates a1 and a2 without considering b1 or b2. +// - modeV* calculates a1 to be b1 plus an adjustment (between -3 and +3). + +const ( + findB1 = false + findB2 = true +) + +// findB finds either the b1 or b2 value. +func (z *reader) findB(whichB bool) int { + // The initial row is a special case. The previous row is implicitly all + // white, so that there are no changing pixel elements. We return b1 or b2 + // to be at the end of the row. + if len(z.prev) != len(z.curr) { + return len(z.curr) + } + + i := z.wi + + if z.atStartOfRow { + // a0 is implicitly at -1, on a white pixel. b1 is the first black + // pixel in the previous row. b2 is the first white pixel after that. + for ; (i < len(z.prev)) && (z.prev[i] == 0xFF); i++ { + } + if whichB == findB2 { + for ; (i < len(z.prev)) && (z.prev[i] == 0x00); i++ { + } + } + return i + } + + // As per figure 1 above, assume that the current pen color is white. + // First, walk past every contiguous black pixel in prev, starting at a0. + oppositeColor := ^z.penColor() + for ; (i < len(z.prev)) && (z.prev[i] == oppositeColor); i++ { + } + + // Then walk past every contiguous white pixel. + penColor := ^oppositeColor + for ; (i < len(z.prev)) && (z.prev[i] == penColor); i++ { + } + + // We're now at a black pixel (or at the end of the row). That's b1. + if whichB == findB2 { + // If we're looking for b2, walk past every contiguous black pixel + // again. + oppositeColor := ^penColor + for ; (i < len(z.prev)) && (z.prev[i] == oppositeColor); i++ { + } + } + + return i +} + +type readerMode struct { + function func(z *reader, arg int) error + arg int +} + +var readerModes = [...]readerMode{ + modePass: {function: readerModePass}, + modeH: {function: readerModeH}, + modeV0: {function: readerModeV, arg: +0}, + modeVR1: {function: readerModeV, arg: +1}, + modeVR2: {function: readerModeV, arg: +2}, + modeVR3: {function: readerModeV, arg: +3}, + modeVL1: {function: readerModeV, arg: -1}, + modeVL2: {function: readerModeV, arg: -2}, + modeVL3: {function: readerModeV, arg: -3}, + modeExt: {function: readerModeExt}, +} + +func readerModePass(z *reader, arg int) error { + b2 := z.findB(findB2) + if (b2 < z.wi) || (len(z.curr) < b2) { + return errInvalidOffset + } + dst := z.curr[z.wi:b2] + penColor := z.penColor() + for i := range dst { + dst[i] = penColor + } + z.wi = b2 + return nil +} + +func readerModeH(z *reader, arg int) error { + // The first iteration finds a1. The second finds a2. + for i := 0; i < 2; i++ { + if err := z.decodeRun(); err != nil { + return err + } + } + return nil +} + +func readerModeV(z *reader, arg int) error { + a1 := z.findB(findB1) + arg + if (a1 < z.wi) || (len(z.curr) < a1) { + return errInvalidOffset + } + dst := z.curr[z.wi:a1] + penColor := z.penColor() + for i := range dst { + dst[i] = penColor + } + z.wi = a1 + z.penColorIsWhite = !z.penColorIsWhite + return nil +} + +func readerModeExt(z *reader, arg int) error { + return errUnsupportedMode +} + +// DecodeIntoGray decodes the CCITT-formatted data in r into dst. +// +// It returns an error if dst's width and height don't match the implied width +// and height of CCITT-formatted data. +func DecodeIntoGray(dst *image.Gray, r io.Reader, order Order, sf SubFormat, opts *Options) error { + bounds := dst.Bounds() + if (bounds.Dx() < 0) || (bounds.Dy() < 0) { + return errInvalidBounds + } + if bounds.Dx() > maxWidth { + return errUnsupportedWidth + } + + z := reader{ + br: bitReader{r: r, order: order}, + subFormat: sf, + align: (opts != nil) && opts.Align, + invert: (opts != nil) && opts.Invert, + width: bounds.Dx(), + } + if err := z.startDecode(); err != nil { + return err + } + + width := bounds.Dx() + for y := bounds.Min.Y; y < bounds.Max.Y; y++ { + p := (y - bounds.Min.Y) * dst.Stride + z.curr = dst.Pix[p : p+width] + if err := z.decodeRow(); err != nil { + return err + } + z.curr, z.prev = nil, z.curr + } + + if err := z.finishDecode(); err != nil { + return err + } + + if z.invert { + for y := bounds.Min.Y; y < bounds.Max.Y; y++ { + p := (y - bounds.Min.Y) * dst.Stride + invertBytes(dst.Pix[p : p+width]) + } + } + + return nil +} + +// NewReader returns an io.Reader that decodes the CCITT-formatted data in r. +// The resultant byte stream is one bit per pixel (MSB first), with 1 meaning +// white and 0 meaning black. Each row in the result is byte-aligned. +func NewReader(r io.Reader, order Order, sf SubFormat, width int, height int, opts *Options) io.Reader { + readErr := error(nil) + if (width < 0) || (height < 0) { + readErr = errInvalidBounds + } else if width > maxWidth { + readErr = errUnsupportedWidth + } + + return &reader{ + br: bitReader{r: r, order: order}, + subFormat: sf, + align: (opts != nil) && opts.Align, + invert: (opts != nil) && opts.Invert, + width: width, + rowsRemaining: height, + readErr: readErr, + } +} diff --git a/vendor/golang.org/x/image/ccitt/table.go b/vendor/golang.org/x/image/ccitt/table.go new file mode 100644 index 000000000..f01cc12b5 --- /dev/null +++ b/vendor/golang.org/x/image/ccitt/table.go @@ -0,0 +1,989 @@ +// generated by "go run gen.go". DO NOT EDIT. + +package ccitt + +// Each decodeTable is represented by an array of [2]int16's: a binary tree. +// Each array element (other than element 0, which means invalid) is a branch +// node in that tree. The root node is always element 1 (the second element). +// +// To walk the tree, look at the next bit in the bit stream, using it to select +// the first or second element of the [2]int16. If that int16 is 0, we have an +// invalid code. If it is positive, go to that branch node. If it is negative, +// then we have a leaf node, whose value is the bitwise complement (the ^ +// operator) of that int16. +// +// Comments above each decodeTable also show the same structure visually. The +// "b123" lines show the 123'rd branch node. The "=XXXXX" lines show an invalid +// code. The "=v1234" lines show a leaf node with value 1234. When reading the +// bit stream, a 0 or 1 bit means to go up or down, as you move left to right. +// +// For example, in modeDecodeTable, branch node b005 is three steps up from the +// root node, meaning that we have already seen "000". If the next bit is "0" +// then we move to branch node b006. Otherwise, the next bit is "1", and we +// move to the leaf node v0000 (also known as the modePass constant). Indeed, +// the bits that encode modePass are "0001". +// +// Tables 1, 2 and 3 come from the "ITU-T Recommendation T.6: FACSIMILE CODING +// SCHEMES AND CODING CONTROL FUNCTIONS FOR GROUP 4 FACSIMILE APPARATUS" +// specification: +// +// https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-T.6-198811-I!!PDF-E&type=items + +// modeDecodeTable represents Table 1 and the End-of-Line code. +// +// +=XXXXX +// b015 +-+ +// | +=v0010 +// b014 +-+ +// | +=XXXXX +// b013 +-+ +// | +=XXXXX +// b012 +-+ +// | +=XXXXX +// b011 +-+ +// | +=XXXXX +// b009 +-+ +// | +=v0009 +// b007 +-+ +// | | +=v0008 +// b010 | +-+ +// | +=v0005 +// b006 +-+ +// | | +=v0007 +// b008 | +-+ +// | +=v0004 +// b005 +-+ +// | +=v0000 +// b003 +-+ +// | +=v0001 +// b002 +-+ +// | | +=v0006 +// b004 | +-+ +// | +=v0003 +// b001 +-+ +// +=v0002 +var modeDecodeTable = [...][2]int16{ + 0: {0, 0}, + 1: {2, ^2}, + 2: {3, 4}, + 3: {5, ^1}, + 4: {^6, ^3}, + 5: {6, ^0}, + 6: {7, 8}, + 7: {9, 10}, + 8: {^7, ^4}, + 9: {11, ^9}, + 10: {^8, ^5}, + 11: {12, 0}, + 12: {13, 0}, + 13: {14, 0}, + 14: {15, 0}, + 15: {0, ^10}, +} + +// whiteDecodeTable represents Tables 2 and 3 for a white run. +// +// +=XXXXX +// b059 +-+ +// | | +=v1792 +// b096 | | +-+ +// | | | | +=v1984 +// b100 | | | +-+ +// | | | +=v2048 +// b094 | | +-+ +// | | | | +=v2112 +// b101 | | | | +-+ +// | | | | | +=v2176 +// b097 | | | +-+ +// | | | | +=v2240 +// b102 | | | +-+ +// | | | +=v2304 +// b085 | +-+ +// | | +=v1856 +// b098 | | +-+ +// | | | +=v1920 +// b095 | +-+ +// | | +=v2368 +// b103 | | +-+ +// | | | +=v2432 +// b099 | +-+ +// | | +=v2496 +// b104 | +-+ +// | +=v2560 +// b040 +-+ +// | | +=v0029 +// b060 | +-+ +// | +=v0030 +// b026 +-+ +// | | +=v0045 +// b061 | | +-+ +// | | | +=v0046 +// b041 | +-+ +// | +=v0022 +// b016 +-+ +// | | +=v0023 +// b042 | | +-+ +// | | | | +=v0047 +// b062 | | | +-+ +// | | | +=v0048 +// b027 | +-+ +// | +=v0013 +// b008 +-+ +// | | +=v0020 +// b043 | | +-+ +// | | | | +=v0033 +// b063 | | | +-+ +// | | | +=v0034 +// b028 | | +-+ +// | | | | +=v0035 +// b064 | | | | +-+ +// | | | | | +=v0036 +// b044 | | | +-+ +// | | | | +=v0037 +// b065 | | | +-+ +// | | | +=v0038 +// b017 | +-+ +// | | +=v0019 +// b045 | | +-+ +// | | | | +=v0031 +// b066 | | | +-+ +// | | | +=v0032 +// b029 | +-+ +// | +=v0001 +// b004 +-+ +// | | +=v0012 +// b030 | | +-+ +// | | | | +=v0053 +// b067 | | | | +-+ +// | | | | | +=v0054 +// b046 | | | +-+ +// | | | +=v0026 +// b018 | | +-+ +// | | | | +=v0039 +// b068 | | | | +-+ +// | | | | | +=v0040 +// b047 | | | | +-+ +// | | | | | | +=v0041 +// b069 | | | | | +-+ +// | | | | | +=v0042 +// b031 | | | +-+ +// | | | | +=v0043 +// b070 | | | | +-+ +// | | | | | +=v0044 +// b048 | | | +-+ +// | | | +=v0021 +// b009 | +-+ +// | | +=v0028 +// b049 | | +-+ +// | | | | +=v0061 +// b071 | | | +-+ +// | | | +=v0062 +// b032 | | +-+ +// | | | | +=v0063 +// b072 | | | | +-+ +// | | | | | +=v0000 +// b050 | | | +-+ +// | | | | +=v0320 +// b073 | | | +-+ +// | | | +=v0384 +// b019 | +-+ +// | +=v0010 +// b002 +-+ +// | | +=v0011 +// b020 | | +-+ +// | | | | +=v0027 +// b051 | | | | +-+ +// | | | | | | +=v0059 +// b074 | | | | | +-+ +// | | | | | +=v0060 +// b033 | | | +-+ +// | | | | +=v1472 +// b086 | | | | +-+ +// | | | | | +=v1536 +// b075 | | | | +-+ +// | | | | | | +=v1600 +// b087 | | | | | +-+ +// | | | | | +=v1728 +// b052 | | | +-+ +// | | | +=v0018 +// b010 | | +-+ +// | | | | +=v0024 +// b053 | | | | +-+ +// | | | | | | +=v0049 +// b076 | | | | | +-+ +// | | | | | +=v0050 +// b034 | | | | +-+ +// | | | | | | +=v0051 +// b077 | | | | | | +-+ +// | | | | | | | +=v0052 +// b054 | | | | | +-+ +// | | | | | +=v0025 +// b021 | | | +-+ +// | | | | +=v0055 +// b078 | | | | +-+ +// | | | | | +=v0056 +// b055 | | | | +-+ +// | | | | | | +=v0057 +// b079 | | | | | +-+ +// | | | | | +=v0058 +// b035 | | | +-+ +// | | | +=v0192 +// b005 | +-+ +// | | +=v1664 +// b036 | | +-+ +// | | | | +=v0448 +// b080 | | | | +-+ +// | | | | | +=v0512 +// b056 | | | +-+ +// | | | | +=v0704 +// b088 | | | | +-+ +// | | | | | +=v0768 +// b081 | | | +-+ +// | | | +=v0640 +// b022 | | +-+ +// | | | | +=v0576 +// b082 | | | | +-+ +// | | | | | | +=v0832 +// b089 | | | | | +-+ +// | | | | | +=v0896 +// b057 | | | | +-+ +// | | | | | | +=v0960 +// b090 | | | | | | +-+ +// | | | | | | | +=v1024 +// b083 | | | | | +-+ +// | | | | | | +=v1088 +// b091 | | | | | +-+ +// | | | | | +=v1152 +// b037 | | | +-+ +// | | | | +=v1216 +// b092 | | | | +-+ +// | | | | | +=v1280 +// b084 | | | | +-+ +// | | | | | | +=v1344 +// b093 | | | | | +-+ +// | | | | | +=v1408 +// b058 | | | +-+ +// | | | +=v0256 +// b011 | +-+ +// | +=v0002 +// b001 +-+ +// | +=v0003 +// b012 | +-+ +// | | | +=v0128 +// b023 | | +-+ +// | | +=v0008 +// b006 | +-+ +// | | | +=v0009 +// b024 | | | +-+ +// | | | | | +=v0016 +// b038 | | | | +-+ +// | | | | +=v0017 +// b013 | | +-+ +// | | +=v0004 +// b003 +-+ +// | +=v0005 +// b014 | +-+ +// | | | +=v0014 +// b039 | | | +-+ +// | | | | +=v0015 +// b025 | | +-+ +// | | +=v0064 +// b007 +-+ +// | +=v0006 +// b015 +-+ +// +=v0007 +var whiteDecodeTable = [...][2]int16{ + 0: {0, 0}, + 1: {2, 3}, + 2: {4, 5}, + 3: {6, 7}, + 4: {8, 9}, + 5: {10, 11}, + 6: {12, 13}, + 7: {14, 15}, + 8: {16, 17}, + 9: {18, 19}, + 10: {20, 21}, + 11: {22, ^2}, + 12: {^3, 23}, + 13: {24, ^4}, + 14: {^5, 25}, + 15: {^6, ^7}, + 16: {26, 27}, + 17: {28, 29}, + 18: {30, 31}, + 19: {32, ^10}, + 20: {^11, 33}, + 21: {34, 35}, + 22: {36, 37}, + 23: {^128, ^8}, + 24: {^9, 38}, + 25: {39, ^64}, + 26: {40, 41}, + 27: {42, ^13}, + 28: {43, 44}, + 29: {45, ^1}, + 30: {^12, 46}, + 31: {47, 48}, + 32: {49, 50}, + 33: {51, 52}, + 34: {53, 54}, + 35: {55, ^192}, + 36: {^1664, 56}, + 37: {57, 58}, + 38: {^16, ^17}, + 39: {^14, ^15}, + 40: {59, 60}, + 41: {61, ^22}, + 42: {^23, 62}, + 43: {^20, 63}, + 44: {64, 65}, + 45: {^19, 66}, + 46: {67, ^26}, + 47: {68, 69}, + 48: {70, ^21}, + 49: {^28, 71}, + 50: {72, 73}, + 51: {^27, 74}, + 52: {75, ^18}, + 53: {^24, 76}, + 54: {77, ^25}, + 55: {78, 79}, + 56: {80, 81}, + 57: {82, 83}, + 58: {84, ^256}, + 59: {0, 85}, + 60: {^29, ^30}, + 61: {^45, ^46}, + 62: {^47, ^48}, + 63: {^33, ^34}, + 64: {^35, ^36}, + 65: {^37, ^38}, + 66: {^31, ^32}, + 67: {^53, ^54}, + 68: {^39, ^40}, + 69: {^41, ^42}, + 70: {^43, ^44}, + 71: {^61, ^62}, + 72: {^63, ^0}, + 73: {^320, ^384}, + 74: {^59, ^60}, + 75: {86, 87}, + 76: {^49, ^50}, + 77: {^51, ^52}, + 78: {^55, ^56}, + 79: {^57, ^58}, + 80: {^448, ^512}, + 81: {88, ^640}, + 82: {^576, 89}, + 83: {90, 91}, + 84: {92, 93}, + 85: {94, 95}, + 86: {^1472, ^1536}, + 87: {^1600, ^1728}, + 88: {^704, ^768}, + 89: {^832, ^896}, + 90: {^960, ^1024}, + 91: {^1088, ^1152}, + 92: {^1216, ^1280}, + 93: {^1344, ^1408}, + 94: {96, 97}, + 95: {98, 99}, + 96: {^1792, 100}, + 97: {101, 102}, + 98: {^1856, ^1920}, + 99: {103, 104}, + 100: {^1984, ^2048}, + 101: {^2112, ^2176}, + 102: {^2240, ^2304}, + 103: {^2368, ^2432}, + 104: {^2496, ^2560}, +} + +// blackDecodeTable represents Tables 2 and 3 for a black run. +// +// +=XXXXX +// b017 +-+ +// | | +=v1792 +// b042 | | +-+ +// | | | | +=v1984 +// b063 | | | +-+ +// | | | +=v2048 +// b029 | | +-+ +// | | | | +=v2112 +// b064 | | | | +-+ +// | | | | | +=v2176 +// b043 | | | +-+ +// | | | | +=v2240 +// b065 | | | +-+ +// | | | +=v2304 +// b022 | +-+ +// | | +=v1856 +// b044 | | +-+ +// | | | +=v1920 +// b030 | +-+ +// | | +=v2368 +// b066 | | +-+ +// | | | +=v2432 +// b045 | +-+ +// | | +=v2496 +// b067 | +-+ +// | +=v2560 +// b013 +-+ +// | | +=v0018 +// b031 | | +-+ +// | | | | +=v0052 +// b068 | | | | +-+ +// | | | | | | +=v0640 +// b095 | | | | | +-+ +// | | | | | +=v0704 +// b046 | | | +-+ +// | | | | +=v0768 +// b096 | | | | +-+ +// | | | | | +=v0832 +// b069 | | | +-+ +// | | | +=v0055 +// b023 | | +-+ +// | | | | +=v0056 +// b070 | | | | +-+ +// | | | | | | +=v1280 +// b097 | | | | | +-+ +// | | | | | +=v1344 +// b047 | | | | +-+ +// | | | | | | +=v1408 +// b098 | | | | | | +-+ +// | | | | | | | +=v1472 +// b071 | | | | | +-+ +// | | | | | +=v0059 +// b032 | | | +-+ +// | | | | +=v0060 +// b072 | | | | +-+ +// | | | | | | +=v1536 +// b099 | | | | | +-+ +// | | | | | +=v1600 +// b048 | | | +-+ +// | | | +=v0024 +// b018 | +-+ +// | | +=v0025 +// b049 | | +-+ +// | | | | +=v1664 +// b100 | | | | +-+ +// | | | | | +=v1728 +// b073 | | | +-+ +// | | | +=v0320 +// b033 | | +-+ +// | | | | +=v0384 +// b074 | | | | +-+ +// | | | | | +=v0448 +// b050 | | | +-+ +// | | | | +=v0512 +// b101 | | | | +-+ +// | | | | | +=v0576 +// b075 | | | +-+ +// | | | +=v0053 +// b024 | +-+ +// | | +=v0054 +// b076 | | +-+ +// | | | | +=v0896 +// b102 | | | +-+ +// | | | +=v0960 +// b051 | | +-+ +// | | | | +=v1024 +// b103 | | | | +-+ +// | | | | | +=v1088 +// b077 | | | +-+ +// | | | | +=v1152 +// b104 | | | +-+ +// | | | +=v1216 +// b034 | +-+ +// | +=v0064 +// b010 +-+ +// | | +=v0013 +// b019 | | +-+ +// | | | | +=v0023 +// b052 | | | | +-+ +// | | | | | | +=v0050 +// b078 | | | | | +-+ +// | | | | | +=v0051 +// b035 | | | | +-+ +// | | | | | | +=v0044 +// b079 | | | | | | +-+ +// | | | | | | | +=v0045 +// b053 | | | | | +-+ +// | | | | | | +=v0046 +// b080 | | | | | +-+ +// | | | | | +=v0047 +// b025 | | | +-+ +// | | | | +=v0057 +// b081 | | | | +-+ +// | | | | | +=v0058 +// b054 | | | | +-+ +// | | | | | | +=v0061 +// b082 | | | | | +-+ +// | | | | | +=v0256 +// b036 | | | +-+ +// | | | +=v0016 +// b014 | +-+ +// | | +=v0017 +// b037 | | +-+ +// | | | | +=v0048 +// b083 | | | | +-+ +// | | | | | +=v0049 +// b055 | | | +-+ +// | | | | +=v0062 +// b084 | | | +-+ +// | | | +=v0063 +// b026 | | +-+ +// | | | | +=v0030 +// b085 | | | | +-+ +// | | | | | +=v0031 +// b056 | | | | +-+ +// | | | | | | +=v0032 +// b086 | | | | | +-+ +// | | | | | +=v0033 +// b038 | | | +-+ +// | | | | +=v0040 +// b087 | | | | +-+ +// | | | | | +=v0041 +// b057 | | | +-+ +// | | | +=v0022 +// b020 | +-+ +// | +=v0014 +// b008 +-+ +// | | +=v0010 +// b015 | | +-+ +// | | | +=v0011 +// b011 | +-+ +// | | +=v0015 +// b027 | | +-+ +// | | | | +=v0128 +// b088 | | | | +-+ +// | | | | | +=v0192 +// b058 | | | | +-+ +// | | | | | | +=v0026 +// b089 | | | | | +-+ +// | | | | | +=v0027 +// b039 | | | +-+ +// | | | | +=v0028 +// b090 | | | | +-+ +// | | | | | +=v0029 +// b059 | | | +-+ +// | | | +=v0019 +// b021 | | +-+ +// | | | | +=v0020 +// b060 | | | | +-+ +// | | | | | | +=v0034 +// b091 | | | | | +-+ +// | | | | | +=v0035 +// b040 | | | | +-+ +// | | | | | | +=v0036 +// b092 | | | | | | +-+ +// | | | | | | | +=v0037 +// b061 | | | | | +-+ +// | | | | | | +=v0038 +// b093 | | | | | +-+ +// | | | | | +=v0039 +// b028 | | | +-+ +// | | | | +=v0021 +// b062 | | | | +-+ +// | | | | | | +=v0042 +// b094 | | | | | +-+ +// | | | | | +=v0043 +// b041 | | | +-+ +// | | | +=v0000 +// b016 | +-+ +// | +=v0012 +// b006 +-+ +// | | +=v0009 +// b012 | | +-+ +// | | | +=v0008 +// b009 | +-+ +// | +=v0007 +// b004 +-+ +// | | +=v0006 +// b007 | +-+ +// | +=v0005 +// b002 +-+ +// | | +=v0001 +// b005 | +-+ +// | +=v0004 +// b001 +-+ +// | +=v0003 +// b003 +-+ +// +=v0002 +var blackDecodeTable = [...][2]int16{ + 0: {0, 0}, + 1: {2, 3}, + 2: {4, 5}, + 3: {^3, ^2}, + 4: {6, 7}, + 5: {^1, ^4}, + 6: {8, 9}, + 7: {^6, ^5}, + 8: {10, 11}, + 9: {12, ^7}, + 10: {13, 14}, + 11: {15, 16}, + 12: {^9, ^8}, + 13: {17, 18}, + 14: {19, 20}, + 15: {^10, ^11}, + 16: {21, ^12}, + 17: {0, 22}, + 18: {23, 24}, + 19: {^13, 25}, + 20: {26, ^14}, + 21: {27, 28}, + 22: {29, 30}, + 23: {31, 32}, + 24: {33, 34}, + 25: {35, 36}, + 26: {37, 38}, + 27: {^15, 39}, + 28: {40, 41}, + 29: {42, 43}, + 30: {44, 45}, + 31: {^18, 46}, + 32: {47, 48}, + 33: {49, 50}, + 34: {51, ^64}, + 35: {52, 53}, + 36: {54, ^16}, + 37: {^17, 55}, + 38: {56, 57}, + 39: {58, 59}, + 40: {60, 61}, + 41: {62, ^0}, + 42: {^1792, 63}, + 43: {64, 65}, + 44: {^1856, ^1920}, + 45: {66, 67}, + 46: {68, 69}, + 47: {70, 71}, + 48: {72, ^24}, + 49: {^25, 73}, + 50: {74, 75}, + 51: {76, 77}, + 52: {^23, 78}, + 53: {79, 80}, + 54: {81, 82}, + 55: {83, 84}, + 56: {85, 86}, + 57: {87, ^22}, + 58: {88, 89}, + 59: {90, ^19}, + 60: {^20, 91}, + 61: {92, 93}, + 62: {^21, 94}, + 63: {^1984, ^2048}, + 64: {^2112, ^2176}, + 65: {^2240, ^2304}, + 66: {^2368, ^2432}, + 67: {^2496, ^2560}, + 68: {^52, 95}, + 69: {96, ^55}, + 70: {^56, 97}, + 71: {98, ^59}, + 72: {^60, 99}, + 73: {100, ^320}, + 74: {^384, ^448}, + 75: {101, ^53}, + 76: {^54, 102}, + 77: {103, 104}, + 78: {^50, ^51}, + 79: {^44, ^45}, + 80: {^46, ^47}, + 81: {^57, ^58}, + 82: {^61, ^256}, + 83: {^48, ^49}, + 84: {^62, ^63}, + 85: {^30, ^31}, + 86: {^32, ^33}, + 87: {^40, ^41}, + 88: {^128, ^192}, + 89: {^26, ^27}, + 90: {^28, ^29}, + 91: {^34, ^35}, + 92: {^36, ^37}, + 93: {^38, ^39}, + 94: {^42, ^43}, + 95: {^640, ^704}, + 96: {^768, ^832}, + 97: {^1280, ^1344}, + 98: {^1408, ^1472}, + 99: {^1536, ^1600}, + 100: {^1664, ^1728}, + 101: {^512, ^576}, + 102: {^896, ^960}, + 103: {^1024, ^1088}, + 104: {^1152, ^1216}, +} + +const maxCodeLength = 13 + +// Each encodeTable is represented by an array of bitStrings. + +// bitString is a pair of uint32 values representing a bit code. +// The nBits low bits of bits make up the actual bit code. +// Eg. bitString{0x0004, 8} represents the bitcode "00000100". +type bitString struct { + bits uint32 + nBits uint32 +} + +// modeEncodeTable represents Table 1 and the End-of-Line code. +var modeEncodeTable = [...]bitString{ + 0: {0x0001, 4}, // "0001" + 1: {0x0001, 3}, // "001" + 2: {0x0001, 1}, // "1" + 3: {0x0003, 3}, // "011" + 4: {0x0003, 6}, // "000011" + 5: {0x0003, 7}, // "0000011" + 6: {0x0002, 3}, // "010" + 7: {0x0002, 6}, // "000010" + 8: {0x0002, 7}, // "0000010" + 9: {0x0001, 7}, // "0000001" + 10: {0x0001, 12}, // "000000000001" +} + +// whiteEncodeTable2 represents Table 2 for a white run. +var whiteEncodeTable2 = [...]bitString{ + 0: {0x0035, 8}, // "00110101" + 1: {0x0007, 6}, // "000111" + 2: {0x0007, 4}, // "0111" + 3: {0x0008, 4}, // "1000" + 4: {0x000b, 4}, // "1011" + 5: {0x000c, 4}, // "1100" + 6: {0x000e, 4}, // "1110" + 7: {0x000f, 4}, // "1111" + 8: {0x0013, 5}, // "10011" + 9: {0x0014, 5}, // "10100" + 10: {0x0007, 5}, // "00111" + 11: {0x0008, 5}, // "01000" + 12: {0x0008, 6}, // "001000" + 13: {0x0003, 6}, // "000011" + 14: {0x0034, 6}, // "110100" + 15: {0x0035, 6}, // "110101" + 16: {0x002a, 6}, // "101010" + 17: {0x002b, 6}, // "101011" + 18: {0x0027, 7}, // "0100111" + 19: {0x000c, 7}, // "0001100" + 20: {0x0008, 7}, // "0001000" + 21: {0x0017, 7}, // "0010111" + 22: {0x0003, 7}, // "0000011" + 23: {0x0004, 7}, // "0000100" + 24: {0x0028, 7}, // "0101000" + 25: {0x002b, 7}, // "0101011" + 26: {0x0013, 7}, // "0010011" + 27: {0x0024, 7}, // "0100100" + 28: {0x0018, 7}, // "0011000" + 29: {0x0002, 8}, // "00000010" + 30: {0x0003, 8}, // "00000011" + 31: {0x001a, 8}, // "00011010" + 32: {0x001b, 8}, // "00011011" + 33: {0x0012, 8}, // "00010010" + 34: {0x0013, 8}, // "00010011" + 35: {0x0014, 8}, // "00010100" + 36: {0x0015, 8}, // "00010101" + 37: {0x0016, 8}, // "00010110" + 38: {0x0017, 8}, // "00010111" + 39: {0x0028, 8}, // "00101000" + 40: {0x0029, 8}, // "00101001" + 41: {0x002a, 8}, // "00101010" + 42: {0x002b, 8}, // "00101011" + 43: {0x002c, 8}, // "00101100" + 44: {0x002d, 8}, // "00101101" + 45: {0x0004, 8}, // "00000100" + 46: {0x0005, 8}, // "00000101" + 47: {0x000a, 8}, // "00001010" + 48: {0x000b, 8}, // "00001011" + 49: {0x0052, 8}, // "01010010" + 50: {0x0053, 8}, // "01010011" + 51: {0x0054, 8}, // "01010100" + 52: {0x0055, 8}, // "01010101" + 53: {0x0024, 8}, // "00100100" + 54: {0x0025, 8}, // "00100101" + 55: {0x0058, 8}, // "01011000" + 56: {0x0059, 8}, // "01011001" + 57: {0x005a, 8}, // "01011010" + 58: {0x005b, 8}, // "01011011" + 59: {0x004a, 8}, // "01001010" + 60: {0x004b, 8}, // "01001011" + 61: {0x0032, 8}, // "00110010" + 62: {0x0033, 8}, // "00110011" + 63: {0x0034, 8}, // "00110100" +} + +// whiteEncodeTable3 represents Table 3 for a white run. +var whiteEncodeTable3 = [...]bitString{ + 0: {0x001b, 5}, // "11011" + 1: {0x0012, 5}, // "10010" + 2: {0x0017, 6}, // "010111" + 3: {0x0037, 7}, // "0110111" + 4: {0x0036, 8}, // "00110110" + 5: {0x0037, 8}, // "00110111" + 6: {0x0064, 8}, // "01100100" + 7: {0x0065, 8}, // "01100101" + 8: {0x0068, 8}, // "01101000" + 9: {0x0067, 8}, // "01100111" + 10: {0x00cc, 9}, // "011001100" + 11: {0x00cd, 9}, // "011001101" + 12: {0x00d2, 9}, // "011010010" + 13: {0x00d3, 9}, // "011010011" + 14: {0x00d4, 9}, // "011010100" + 15: {0x00d5, 9}, // "011010101" + 16: {0x00d6, 9}, // "011010110" + 17: {0x00d7, 9}, // "011010111" + 18: {0x00d8, 9}, // "011011000" + 19: {0x00d9, 9}, // "011011001" + 20: {0x00da, 9}, // "011011010" + 21: {0x00db, 9}, // "011011011" + 22: {0x0098, 9}, // "010011000" + 23: {0x0099, 9}, // "010011001" + 24: {0x009a, 9}, // "010011010" + 25: {0x0018, 6}, // "011000" + 26: {0x009b, 9}, // "010011011" + 27: {0x0008, 11}, // "00000001000" + 28: {0x000c, 11}, // "00000001100" + 29: {0x000d, 11}, // "00000001101" + 30: {0x0012, 12}, // "000000010010" + 31: {0x0013, 12}, // "000000010011" + 32: {0x0014, 12}, // "000000010100" + 33: {0x0015, 12}, // "000000010101" + 34: {0x0016, 12}, // "000000010110" + 35: {0x0017, 12}, // "000000010111" + 36: {0x001c, 12}, // "000000011100" + 37: {0x001d, 12}, // "000000011101" + 38: {0x001e, 12}, // "000000011110" + 39: {0x001f, 12}, // "000000011111" +} + +// blackEncodeTable2 represents Table 2 for a black run. +var blackEncodeTable2 = [...]bitString{ + 0: {0x0037, 10}, // "0000110111" + 1: {0x0002, 3}, // "010" + 2: {0x0003, 2}, // "11" + 3: {0x0002, 2}, // "10" + 4: {0x0003, 3}, // "011" + 5: {0x0003, 4}, // "0011" + 6: {0x0002, 4}, // "0010" + 7: {0x0003, 5}, // "00011" + 8: {0x0005, 6}, // "000101" + 9: {0x0004, 6}, // "000100" + 10: {0x0004, 7}, // "0000100" + 11: {0x0005, 7}, // "0000101" + 12: {0x0007, 7}, // "0000111" + 13: {0x0004, 8}, // "00000100" + 14: {0x0007, 8}, // "00000111" + 15: {0x0018, 9}, // "000011000" + 16: {0x0017, 10}, // "0000010111" + 17: {0x0018, 10}, // "0000011000" + 18: {0x0008, 10}, // "0000001000" + 19: {0x0067, 11}, // "00001100111" + 20: {0x0068, 11}, // "00001101000" + 21: {0x006c, 11}, // "00001101100" + 22: {0x0037, 11}, // "00000110111" + 23: {0x0028, 11}, // "00000101000" + 24: {0x0017, 11}, // "00000010111" + 25: {0x0018, 11}, // "00000011000" + 26: {0x00ca, 12}, // "000011001010" + 27: {0x00cb, 12}, // "000011001011" + 28: {0x00cc, 12}, // "000011001100" + 29: {0x00cd, 12}, // "000011001101" + 30: {0x0068, 12}, // "000001101000" + 31: {0x0069, 12}, // "000001101001" + 32: {0x006a, 12}, // "000001101010" + 33: {0x006b, 12}, // "000001101011" + 34: {0x00d2, 12}, // "000011010010" + 35: {0x00d3, 12}, // "000011010011" + 36: {0x00d4, 12}, // "000011010100" + 37: {0x00d5, 12}, // "000011010101" + 38: {0x00d6, 12}, // "000011010110" + 39: {0x00d7, 12}, // "000011010111" + 40: {0x006c, 12}, // "000001101100" + 41: {0x006d, 12}, // "000001101101" + 42: {0x00da, 12}, // "000011011010" + 43: {0x00db, 12}, // "000011011011" + 44: {0x0054, 12}, // "000001010100" + 45: {0x0055, 12}, // "000001010101" + 46: {0x0056, 12}, // "000001010110" + 47: {0x0057, 12}, // "000001010111" + 48: {0x0064, 12}, // "000001100100" + 49: {0x0065, 12}, // "000001100101" + 50: {0x0052, 12}, // "000001010010" + 51: {0x0053, 12}, // "000001010011" + 52: {0x0024, 12}, // "000000100100" + 53: {0x0037, 12}, // "000000110111" + 54: {0x0038, 12}, // "000000111000" + 55: {0x0027, 12}, // "000000100111" + 56: {0x0028, 12}, // "000000101000" + 57: {0x0058, 12}, // "000001011000" + 58: {0x0059, 12}, // "000001011001" + 59: {0x002b, 12}, // "000000101011" + 60: {0x002c, 12}, // "000000101100" + 61: {0x005a, 12}, // "000001011010" + 62: {0x0066, 12}, // "000001100110" + 63: {0x0067, 12}, // "000001100111" +} + +// blackEncodeTable3 represents Table 3 for a black run. +var blackEncodeTable3 = [...]bitString{ + 0: {0x000f, 10}, // "0000001111" + 1: {0x00c8, 12}, // "000011001000" + 2: {0x00c9, 12}, // "000011001001" + 3: {0x005b, 12}, // "000001011011" + 4: {0x0033, 12}, // "000000110011" + 5: {0x0034, 12}, // "000000110100" + 6: {0x0035, 12}, // "000000110101" + 7: {0x006c, 13}, // "0000001101100" + 8: {0x006d, 13}, // "0000001101101" + 9: {0x004a, 13}, // "0000001001010" + 10: {0x004b, 13}, // "0000001001011" + 11: {0x004c, 13}, // "0000001001100" + 12: {0x004d, 13}, // "0000001001101" + 13: {0x0072, 13}, // "0000001110010" + 14: {0x0073, 13}, // "0000001110011" + 15: {0x0074, 13}, // "0000001110100" + 16: {0x0075, 13}, // "0000001110101" + 17: {0x0076, 13}, // "0000001110110" + 18: {0x0077, 13}, // "0000001110111" + 19: {0x0052, 13}, // "0000001010010" + 20: {0x0053, 13}, // "0000001010011" + 21: {0x0054, 13}, // "0000001010100" + 22: {0x0055, 13}, // "0000001010101" + 23: {0x005a, 13}, // "0000001011010" + 24: {0x005b, 13}, // "0000001011011" + 25: {0x0064, 13}, // "0000001100100" + 26: {0x0065, 13}, // "0000001100101" + 27: {0x0008, 11}, // "00000001000" + 28: {0x000c, 11}, // "00000001100" + 29: {0x000d, 11}, // "00000001101" + 30: {0x0012, 12}, // "000000010010" + 31: {0x0013, 12}, // "000000010011" + 32: {0x0014, 12}, // "000000010100" + 33: {0x0015, 12}, // "000000010101" + 34: {0x0016, 12}, // "000000010110" + 35: {0x0017, 12}, // "000000010111" + 36: {0x001c, 12}, // "000000011100" + 37: {0x001d, 12}, // "000000011101" + 38: {0x001e, 12}, // "000000011110" + 39: {0x001f, 12}, // "000000011111" +} + +// COPY PASTE table.go BEGIN + +const ( + modePass = iota // Pass + modeH // Horizontal + modeV0 // Vertical-0 + modeVR1 // Vertical-Right-1 + modeVR2 // Vertical-Right-2 + modeVR3 // Vertical-Right-3 + modeVL1 // Vertical-Left-1 + modeVL2 // Vertical-Left-2 + modeVL3 // Vertical-Left-3 + modeExt // Extension + modeEOL // End-of-Line +) + +// COPY PASTE table.go END diff --git a/vendor/golang.org/x/image/ccitt/writer.go b/vendor/golang.org/x/image/ccitt/writer.go new file mode 100644 index 000000000..87130ab04 --- /dev/null +++ b/vendor/golang.org/x/image/ccitt/writer.go @@ -0,0 +1,102 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ccitt + +import ( + "encoding/binary" + "io" +) + +type bitWriter struct { + w io.Writer + + // order is whether to process w's bytes LSB first or MSB first. + order Order + + // The high nBits bits of the bits field hold encoded bits to be written to w. + bits uint64 + nBits uint32 + + // bytes[:bw] holds encoded bytes not yet written to w. + // Overflow protection is ensured by using a multiple of 8 as bytes length. + bw uint32 + bytes [1024]uint8 +} + +// flushBits copies 64 bits from b.bits to b.bytes. If b.bytes is then full, it +// is written to b.w. +func (b *bitWriter) flushBits() error { + binary.BigEndian.PutUint64(b.bytes[b.bw:], b.bits) + b.bits = 0 + b.nBits = 0 + b.bw += 8 + if b.bw < uint32(len(b.bytes)) { + return nil + } + b.bw = 0 + if b.order != MSB { + reverseBitsWithinBytes(b.bytes[:]) + } + _, err := b.w.Write(b.bytes[:]) + return err +} + +// close finalizes a bitcode stream by writing any +// pending bits to bitWriter's underlying io.Writer. +func (b *bitWriter) close() error { + // Write any encoded bits to bytes. + if b.nBits > 0 { + binary.BigEndian.PutUint64(b.bytes[b.bw:], b.bits) + b.bw += (b.nBits + 7) >> 3 + } + + if b.order != MSB { + reverseBitsWithinBytes(b.bytes[:b.bw]) + } + + // Write b.bw bytes to b.w. + _, err := b.w.Write(b.bytes[:b.bw]) + return err +} + +// alignToByteBoundary rounds b.nBits up to a multiple of 8. +// If all 64 bits are used, flush them to bitWriter's bytes. +func (b *bitWriter) alignToByteBoundary() error { + if b.nBits = (b.nBits + 7) &^ 7; b.nBits == 64 { + return b.flushBits() + } + return nil +} + +// writeCode writes a variable length bitcode to b's underlying io.Writer. +func (b *bitWriter) writeCode(bs bitString) error { + bits := bs.bits + nBits := bs.nBits + if 64-b.nBits >= nBits { + // b.bits has sufficient room for storing nBits bits. + b.bits |= uint64(bits) << (64 - nBits - b.nBits) + b.nBits += nBits + if b.nBits == 64 { + return b.flushBits() + } + return nil + } + + // Number of leading bits that fill b.bits. + i := 64 - b.nBits + + // Fill b.bits then flush and write remaining bits. + b.bits |= uint64(bits) >> (nBits - i) + b.nBits = 64 + + if err := b.flushBits(); err != nil { + return err + } + + nBits -= i + b.bits = uint64(bits) << (64 - nBits) + b.nBits = nBits + return nil +} diff --git a/vendor/golang.org/x/image/tiff/buffer.go b/vendor/golang.org/x/image/tiff/buffer.go new file mode 100644 index 000000000..d1801be48 --- /dev/null +++ b/vendor/golang.org/x/image/tiff/buffer.go @@ -0,0 +1,69 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tiff + +import "io" + +// buffer buffers an io.Reader to satisfy io.ReaderAt. +type buffer struct { + r io.Reader + buf []byte +} + +// fill reads data from b.r until the buffer contains at least end bytes. +func (b *buffer) fill(end int) error { + m := len(b.buf) + if end > m { + if end > cap(b.buf) { + newcap := 1024 + for newcap < end { + newcap *= 2 + } + newbuf := make([]byte, end, newcap) + copy(newbuf, b.buf) + b.buf = newbuf + } else { + b.buf = b.buf[:end] + } + if n, err := io.ReadFull(b.r, b.buf[m:end]); err != nil { + end = m + n + b.buf = b.buf[:end] + return err + } + } + return nil +} + +func (b *buffer) ReadAt(p []byte, off int64) (int, error) { + o := int(off) + end := o + len(p) + if int64(end) != off+int64(len(p)) { + return 0, io.ErrUnexpectedEOF + } + + err := b.fill(end) + return copy(p, b.buf[o:end]), err +} + +// Slice returns a slice of the underlying buffer. The slice contains +// n bytes starting at offset off. +func (b *buffer) Slice(off, n int) ([]byte, error) { + end := off + n + if err := b.fill(end); err != nil { + return nil, err + } + return b.buf[off:end], nil +} + +// newReaderAt converts an io.Reader into an io.ReaderAt. +func newReaderAt(r io.Reader) io.ReaderAt { + if ra, ok := r.(io.ReaderAt); ok { + return ra + } + return &buffer{ + r: r, + buf: make([]byte, 0, 1024), + } +} diff --git a/vendor/golang.org/x/image/tiff/compress.go b/vendor/golang.org/x/image/tiff/compress.go new file mode 100644 index 000000000..3f176f00a --- /dev/null +++ b/vendor/golang.org/x/image/tiff/compress.go @@ -0,0 +1,58 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tiff + +import ( + "bufio" + "io" +) + +type byteReader interface { + io.Reader + io.ByteReader +} + +// unpackBits decodes the PackBits-compressed data in src and returns the +// uncompressed data. +// +// The PackBits compression format is described in section 9 (p. 42) +// of the TIFF spec. +func unpackBits(r io.Reader) ([]byte, error) { + buf := make([]byte, 128) + dst := make([]byte, 0, 1024) + br, ok := r.(byteReader) + if !ok { + br = bufio.NewReader(r) + } + + for { + b, err := br.ReadByte() + if err != nil { + if err == io.EOF { + return dst, nil + } + return nil, err + } + code := int(int8(b)) + switch { + case code >= 0: + n, err := io.ReadFull(br, buf[:code+1]) + if err != nil { + return nil, err + } + dst = append(dst, buf[:n]...) + case code == -128: + // No-op. + default: + if b, err = br.ReadByte(); err != nil { + return nil, err + } + for j := 0; j < 1-code; j++ { + buf[j] = b + } + dst = append(dst, buf[:1-code]...) + } + } +} diff --git a/vendor/golang.org/x/image/tiff/consts.go b/vendor/golang.org/x/image/tiff/consts.go new file mode 100644 index 000000000..3e5f7f14d --- /dev/null +++ b/vendor/golang.org/x/image/tiff/consts.go @@ -0,0 +1,149 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tiff + +// A tiff image file contains one or more images. The metadata +// of each image is contained in an Image File Directory (IFD), +// which contains entries of 12 bytes each and is described +// on page 14-16 of the specification. An IFD entry consists of +// +// - a tag, which describes the signification of the entry, +// - the data type and length of the entry, +// - the data itself or a pointer to it if it is more than 4 bytes. +// +// The presence of a length means that each IFD is effectively an array. + +const ( + leHeader = "II\x2A\x00" // Header for little-endian files. + beHeader = "MM\x00\x2A" // Header for big-endian files. + + ifdLen = 12 // Length of an IFD entry in bytes. +) + +// Data types (p. 14-16 of the spec). +const ( + dtByte = 1 + dtASCII = 2 + dtShort = 3 + dtLong = 4 + dtRational = 5 +) + +// The length of one instance of each data type in bytes. +var lengths = [...]uint32{0, 1, 1, 2, 4, 8} + +// Tags (see p. 28-41 of the spec). +const ( + tImageWidth = 256 + tImageLength = 257 + tBitsPerSample = 258 + tCompression = 259 + tPhotometricInterpretation = 262 + + tFillOrder = 266 + + tStripOffsets = 273 + tSamplesPerPixel = 277 + tRowsPerStrip = 278 + tStripByteCounts = 279 + + tT4Options = 292 // CCITT Group 3 options, a set of 32 flag bits. + tT6Options = 293 // CCITT Group 4 options, a set of 32 flag bits. + + tTileWidth = 322 + tTileLength = 323 + tTileOffsets = 324 + tTileByteCounts = 325 + + tXResolution = 282 + tYResolution = 283 + tResolutionUnit = 296 + + tPredictor = 317 + tColorMap = 320 + tExtraSamples = 338 + tSampleFormat = 339 +) + +// Compression types (defined in various places in the spec and supplements). +const ( + cNone = 1 + cCCITT = 2 + cG3 = 3 // Group 3 Fax. + cG4 = 4 // Group 4 Fax. + cLZW = 5 + cJPEGOld = 6 // Superseded by cJPEG. + cJPEG = 7 + cDeflate = 8 // zlib compression. + cPackBits = 32773 + cDeflateOld = 32946 // Superseded by cDeflate. +) + +// Photometric interpretation values (see p. 37 of the spec). +const ( + pWhiteIsZero = 0 + pBlackIsZero = 1 + pRGB = 2 + pPaletted = 3 + pTransMask = 4 // transparency mask + pCMYK = 5 + pYCbCr = 6 + pCIELab = 8 +) + +// Values for the tPredictor tag (page 64-65 of the spec). +const ( + prNone = 1 + prHorizontal = 2 +) + +// Values for the tResolutionUnit tag (page 18). +const ( + resNone = 1 + resPerInch = 2 // Dots per inch. + resPerCM = 3 // Dots per centimeter. +) + +// imageMode represents the mode of the image. +type imageMode int + +const ( + mBilevel imageMode = iota + mPaletted + mGray + mGrayInvert + mRGB + mRGBA + mNRGBA + mCMYK +) + +// CompressionType describes the type of compression used in Options. +type CompressionType int + +// Constants for supported compression types. +const ( + Uncompressed CompressionType = iota + Deflate + LZW + CCITTGroup3 + CCITTGroup4 +) + +// specValue returns the compression type constant from the TIFF spec that +// is equivalent to c. +func (c CompressionType) specValue() uint32 { + switch c { + case LZW: + return cLZW + case Deflate: + return cDeflate + case CCITTGroup3: + return cG3 + case CCITTGroup4: + return cG4 + } + return cNone +} diff --git a/vendor/golang.org/x/image/tiff/fuzz.go b/vendor/golang.org/x/image/tiff/fuzz.go new file mode 100644 index 000000000..ec52c7882 --- /dev/null +++ b/vendor/golang.org/x/image/tiff/fuzz.go @@ -0,0 +1,29 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build gofuzz + +package tiff + +import "bytes" + +func Fuzz(data []byte) int { + cfg, err := DecodeConfig(bytes.NewReader(data)) + if err != nil { + return 0 + } + if cfg.Width*cfg.Height > 1e6 { + return 0 + } + img, err := Decode(bytes.NewReader(data)) + if err != nil { + return 0 + } + var w bytes.Buffer + err = Encode(&w, img, nil) + if err != nil { + panic(err) + } + return 1 +} diff --git a/vendor/golang.org/x/image/tiff/lzw/reader.go b/vendor/golang.org/x/image/tiff/lzw/reader.go new file mode 100644 index 000000000..78204ba92 --- /dev/null +++ b/vendor/golang.org/x/image/tiff/lzw/reader.go @@ -0,0 +1,272 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package lzw implements the Lempel-Ziv-Welch compressed data format, +// described in T. A. Welch, ``A Technique for High-Performance Data +// Compression'', Computer, 17(6) (June 1984), pp 8-19. +// +// In particular, it implements LZW as used by the TIFF file format, including +// an "off by one" algorithmic difference when compared to standard LZW. +package lzw // import "golang.org/x/image/tiff/lzw" + +/* +This file was branched from src/pkg/compress/lzw/reader.go in the +standard library. Differences from the original are marked with "NOTE". + +The tif_lzw.c file in the libtiff C library has this comment: + +---- +The 5.0 spec describes a different algorithm than Aldus +implements. Specifically, Aldus does code length transitions +one code earlier than should be done (for real LZW). +Earlier versions of this library implemented the correct +LZW algorithm, but emitted codes in a bit order opposite +to the TIFF spec. Thus, to maintain compatibility w/ Aldus +we interpret MSB-LSB ordered codes to be images written w/ +old versions of this library, but otherwise adhere to the +Aldus "off by one" algorithm. +---- + +The Go code doesn't read (invalid) TIFF files written by old versions of +libtiff, but the LZW algorithm in this package still differs from the one in +Go's standard package library to accomodate this "off by one" in valid TIFFs. +*/ + +import ( + "bufio" + "errors" + "fmt" + "io" +) + +// Order specifies the bit ordering in an LZW data stream. +type Order int + +const ( + // LSB means Least Significant Bits first, as used in the GIF file format. + LSB Order = iota + // MSB means Most Significant Bits first, as used in the TIFF and PDF + // file formats. + MSB +) + +const ( + maxWidth = 12 + decoderInvalidCode = 0xffff + flushBuffer = 1 << maxWidth +) + +// decoder is the state from which the readXxx method converts a byte +// stream into a code stream. +type decoder struct { + r io.ByteReader + bits uint32 + nBits uint + width uint + read func(*decoder) (uint16, error) // readLSB or readMSB + litWidth int // width in bits of literal codes + err error + + // The first 1<= 1<>= d.width + d.nBits -= d.width + return code, nil +} + +// readMSB returns the next code for "Most Significant Bits first" data. +func (d *decoder) readMSB() (uint16, error) { + for d.nBits < d.width { + x, err := d.r.ReadByte() + if err != nil { + return 0, err + } + d.bits |= uint32(x) << (24 - d.nBits) + d.nBits += 8 + } + code := uint16(d.bits >> (32 - d.width)) + d.bits <<= d.width + d.nBits -= d.width + return code, nil +} + +func (d *decoder) Read(b []byte) (int, error) { + for { + if len(d.toRead) > 0 { + n := copy(b, d.toRead) + d.toRead = d.toRead[n:] + return n, nil + } + if d.err != nil { + return 0, d.err + } + d.decode() + } +} + +// decode decompresses bytes from r and leaves them in d.toRead. +// read specifies how to decode bytes into codes. +// litWidth is the width in bits of literal codes. +func (d *decoder) decode() { + // Loop over the code stream, converting codes into decompressed bytes. +loop: + for { + code, err := d.read(d) + if err != nil { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + d.err = err + break + } + switch { + case code < d.clear: + // We have a literal code. + d.output[d.o] = uint8(code) + d.o++ + if d.last != decoderInvalidCode { + // Save what the hi code expands to. + d.suffix[d.hi] = uint8(code) + d.prefix[d.hi] = d.last + } + case code == d.clear: + d.width = 1 + uint(d.litWidth) + d.hi = d.eof + d.overflow = 1 << d.width + d.last = decoderInvalidCode + continue + case code == d.eof: + d.err = io.EOF + break loop + case code <= d.hi: + c, i := code, len(d.output)-1 + if code == d.hi && d.last != decoderInvalidCode { + // code == hi is a special case which expands to the last expansion + // followed by the head of the last expansion. To find the head, we walk + // the prefix chain until we find a literal code. + c = d.last + for c >= d.clear { + c = d.prefix[c] + } + d.output[i] = uint8(c) + i-- + c = d.last + } + // Copy the suffix chain into output and then write that to w. + for c >= d.clear { + d.output[i] = d.suffix[c] + i-- + c = d.prefix[c] + } + d.output[i] = uint8(c) + d.o += copy(d.output[d.o:], d.output[i:]) + if d.last != decoderInvalidCode { + // Save what the hi code expands to. + d.suffix[d.hi] = uint8(c) + d.prefix[d.hi] = d.last + } + default: + d.err = errors.New("lzw: invalid code") + break loop + } + d.last, d.hi = code, d.hi+1 + if d.hi+1 >= d.overflow { // NOTE: the "+1" is where TIFF's LZW differs from the standard algorithm. + if d.width == maxWidth { + d.last = decoderInvalidCode + } else { + d.width++ + d.overflow <<= 1 + } + } + if d.o >= flushBuffer { + break + } + } + // Flush pending output. + d.toRead = d.output[:d.o] + d.o = 0 +} + +var errClosed = errors.New("lzw: reader/writer is closed") + +func (d *decoder) Close() error { + d.err = errClosed // in case any Reads come along + return nil +} + +// NewReader creates a new io.ReadCloser. +// Reads from the returned io.ReadCloser read and decompress data from r. +// If r does not also implement io.ByteReader, +// the decompressor may read more data than necessary from r. +// It is the caller's responsibility to call Close on the ReadCloser when +// finished reading. +// The number of bits to use for literal codes, litWidth, must be in the +// range [2,8] and is typically 8. It must equal the litWidth +// used during compression. +func NewReader(r io.Reader, order Order, litWidth int) io.ReadCloser { + d := new(decoder) + switch order { + case LSB: + d.read = (*decoder).readLSB + case MSB: + d.read = (*decoder).readMSB + default: + d.err = errors.New("lzw: unknown order") + return d + } + if litWidth < 2 || 8 < litWidth { + d.err = fmt.Errorf("lzw: litWidth %d out of range", litWidth) + return d + } + if br, ok := r.(io.ByteReader); ok { + d.r = br + } else { + d.r = bufio.NewReader(r) + } + d.litWidth = litWidth + d.width = 1 + uint(litWidth) + d.clear = uint16(1) << uint(litWidth) + d.eof, d.hi = d.clear+1, d.clear+1 + d.overflow = uint16(1) << d.width + d.last = decoderInvalidCode + + return d +} diff --git a/vendor/golang.org/x/image/tiff/reader.go b/vendor/golang.org/x/image/tiff/reader.go new file mode 100644 index 000000000..c26ec36bb --- /dev/null +++ b/vendor/golang.org/x/image/tiff/reader.go @@ -0,0 +1,706 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package tiff implements a TIFF image decoder and encoder. +// +// The TIFF specification is at http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf +package tiff // import "golang.org/x/image/tiff" + +import ( + "compress/zlib" + "encoding/binary" + "fmt" + "image" + "image/color" + "io" + "io/ioutil" + "math" + + "golang.org/x/image/ccitt" + "golang.org/x/image/tiff/lzw" +) + +// A FormatError reports that the input is not a valid TIFF image. +type FormatError string + +func (e FormatError) Error() string { + return "tiff: invalid format: " + string(e) +} + +// An UnsupportedError reports that the input uses a valid but +// unimplemented feature. +type UnsupportedError string + +func (e UnsupportedError) Error() string { + return "tiff: unsupported feature: " + string(e) +} + +var errNoPixels = FormatError("not enough pixel data") + +type decoder struct { + r io.ReaderAt + byteOrder binary.ByteOrder + config image.Config + mode imageMode + bpp uint + features map[int][]uint + palette []color.Color + + buf []byte + off int // Current offset in buf. + v uint32 // Buffer value for reading with arbitrary bit depths. + nbits uint // Remaining number of bits in v. +} + +// firstVal returns the first uint of the features entry with the given tag, +// or 0 if the tag does not exist. +func (d *decoder) firstVal(tag int) uint { + f := d.features[tag] + if len(f) == 0 { + return 0 + } + return f[0] +} + +// ifdUint decodes the IFD entry in p, which must be of the Byte, Short +// or Long type, and returns the decoded uint values. +func (d *decoder) ifdUint(p []byte) (u []uint, err error) { + var raw []byte + if len(p) < ifdLen { + return nil, FormatError("bad IFD entry") + } + + datatype := d.byteOrder.Uint16(p[2:4]) + if dt := int(datatype); dt <= 0 || dt >= len(lengths) { + return nil, UnsupportedError("IFD entry datatype") + } + + count := d.byteOrder.Uint32(p[4:8]) + if count > math.MaxInt32/lengths[datatype] { + return nil, FormatError("IFD data too large") + } + if datalen := lengths[datatype] * count; datalen > 4 { + // The IFD contains a pointer to the real value. + raw = make([]byte, datalen) + _, err = d.r.ReadAt(raw, int64(d.byteOrder.Uint32(p[8:12]))) + } else { + raw = p[8 : 8+datalen] + } + if err != nil { + return nil, err + } + + u = make([]uint, count) + switch datatype { + case dtByte: + for i := uint32(0); i < count; i++ { + u[i] = uint(raw[i]) + } + case dtShort: + for i := uint32(0); i < count; i++ { + u[i] = uint(d.byteOrder.Uint16(raw[2*i : 2*(i+1)])) + } + case dtLong: + for i := uint32(0); i < count; i++ { + u[i] = uint(d.byteOrder.Uint32(raw[4*i : 4*(i+1)])) + } + default: + return nil, UnsupportedError("data type") + } + return u, nil +} + +// parseIFD decides whether the IFD entry in p is "interesting" and +// stows away the data in the decoder. It returns the tag number of the +// entry and an error, if any. +func (d *decoder) parseIFD(p []byte) (int, error) { + tag := d.byteOrder.Uint16(p[0:2]) + switch tag { + case tBitsPerSample, + tExtraSamples, + tPhotometricInterpretation, + tCompression, + tPredictor, + tStripOffsets, + tStripByteCounts, + tRowsPerStrip, + tTileWidth, + tTileLength, + tTileOffsets, + tTileByteCounts, + tImageLength, + tImageWidth, + tFillOrder, + tT4Options, + tT6Options: + val, err := d.ifdUint(p) + if err != nil { + return 0, err + } + d.features[int(tag)] = val + case tColorMap: + val, err := d.ifdUint(p) + if err != nil { + return 0, err + } + numcolors := len(val) / 3 + if len(val)%3 != 0 || numcolors <= 0 || numcolors > 256 { + return 0, FormatError("bad ColorMap length") + } + d.palette = make([]color.Color, numcolors) + for i := 0; i < numcolors; i++ { + d.palette[i] = color.RGBA64{ + uint16(val[i]), + uint16(val[i+numcolors]), + uint16(val[i+2*numcolors]), + 0xffff, + } + } + case tSampleFormat: + // Page 27 of the spec: If the SampleFormat is present and + // the value is not 1 [= unsigned integer data], a Baseline + // TIFF reader that cannot handle the SampleFormat value + // must terminate the import process gracefully. + val, err := d.ifdUint(p) + if err != nil { + return 0, err + } + for _, v := range val { + if v != 1 { + return 0, UnsupportedError("sample format") + } + } + } + return int(tag), nil +} + +// readBits reads n bits from the internal buffer starting at the current offset. +func (d *decoder) readBits(n uint) (v uint32, ok bool) { + for d.nbits < n { + d.v <<= 8 + if d.off >= len(d.buf) { + return 0, false + } + d.v |= uint32(d.buf[d.off]) + d.off++ + d.nbits += 8 + } + d.nbits -= n + rv := d.v >> d.nbits + d.v &^= rv << d.nbits + return rv, true +} + +// flushBits discards the unread bits in the buffer used by readBits. +// It is used at the end of a line. +func (d *decoder) flushBits() { + d.v = 0 + d.nbits = 0 +} + +// minInt returns the smaller of x or y. +func minInt(a, b int) int { + if a <= b { + return a + } + return b +} + +// decode decodes the raw data of an image. +// It reads from d.buf and writes the strip or tile into dst. +func (d *decoder) decode(dst image.Image, xmin, ymin, xmax, ymax int) error { + d.off = 0 + + // Apply horizontal predictor if necessary. + // In this case, p contains the color difference to the preceding pixel. + // See page 64-65 of the spec. + if d.firstVal(tPredictor) == prHorizontal { + switch d.bpp { + case 16: + var off int + n := 2 * len(d.features[tBitsPerSample]) // bytes per sample times samples per pixel + for y := ymin; y < ymax; y++ { + off += n + for x := 0; x < (xmax-xmin-1)*n; x += 2 { + if off+2 > len(d.buf) { + return errNoPixels + } + v0 := d.byteOrder.Uint16(d.buf[off-n : off-n+2]) + v1 := d.byteOrder.Uint16(d.buf[off : off+2]) + d.byteOrder.PutUint16(d.buf[off:off+2], v1+v0) + off += 2 + } + } + case 8: + var off int + n := 1 * len(d.features[tBitsPerSample]) // bytes per sample times samples per pixel + for y := ymin; y < ymax; y++ { + off += n + for x := 0; x < (xmax-xmin-1)*n; x++ { + if off >= len(d.buf) { + return errNoPixels + } + d.buf[off] += d.buf[off-n] + off++ + } + } + case 1: + return UnsupportedError("horizontal predictor with 1 BitsPerSample") + } + } + + rMaxX := minInt(xmax, dst.Bounds().Max.X) + rMaxY := minInt(ymax, dst.Bounds().Max.Y) + switch d.mode { + case mGray, mGrayInvert: + if d.bpp == 16 { + img := dst.(*image.Gray16) + for y := ymin; y < rMaxY; y++ { + for x := xmin; x < rMaxX; x++ { + if d.off+2 > len(d.buf) { + return errNoPixels + } + v := d.byteOrder.Uint16(d.buf[d.off : d.off+2]) + d.off += 2 + if d.mode == mGrayInvert { + v = 0xffff - v + } + img.SetGray16(x, y, color.Gray16{v}) + } + if rMaxX == img.Bounds().Max.X { + d.off += 2 * (xmax - img.Bounds().Max.X) + } + } + } else { + img := dst.(*image.Gray) + max := uint32((1 << d.bpp) - 1) + for y := ymin; y < rMaxY; y++ { + for x := xmin; x < rMaxX; x++ { + v, ok := d.readBits(d.bpp) + if !ok { + return errNoPixels + } + v = v * 0xff / max + if d.mode == mGrayInvert { + v = 0xff - v + } + img.SetGray(x, y, color.Gray{uint8(v)}) + } + d.flushBits() + } + } + case mPaletted: + img := dst.(*image.Paletted) + for y := ymin; y < rMaxY; y++ { + for x := xmin; x < rMaxX; x++ { + v, ok := d.readBits(d.bpp) + if !ok { + return errNoPixels + } + img.SetColorIndex(x, y, uint8(v)) + } + d.flushBits() + } + case mRGB: + if d.bpp == 16 { + img := dst.(*image.RGBA64) + for y := ymin; y < rMaxY; y++ { + for x := xmin; x < rMaxX; x++ { + if d.off+6 > len(d.buf) { + return errNoPixels + } + r := d.byteOrder.Uint16(d.buf[d.off+0 : d.off+2]) + g := d.byteOrder.Uint16(d.buf[d.off+2 : d.off+4]) + b := d.byteOrder.Uint16(d.buf[d.off+4 : d.off+6]) + d.off += 6 + img.SetRGBA64(x, y, color.RGBA64{r, g, b, 0xffff}) + } + } + } else { + img := dst.(*image.RGBA) + for y := ymin; y < rMaxY; y++ { + min := img.PixOffset(xmin, y) + max := img.PixOffset(rMaxX, y) + off := (y - ymin) * (xmax - xmin) * 3 + for i := min; i < max; i += 4 { + if off+3 > len(d.buf) { + return errNoPixels + } + img.Pix[i+0] = d.buf[off+0] + img.Pix[i+1] = d.buf[off+1] + img.Pix[i+2] = d.buf[off+2] + img.Pix[i+3] = 0xff + off += 3 + } + } + } + case mNRGBA: + if d.bpp == 16 { + img := dst.(*image.NRGBA64) + for y := ymin; y < rMaxY; y++ { + for x := xmin; x < rMaxX; x++ { + if d.off+8 > len(d.buf) { + return errNoPixels + } + r := d.byteOrder.Uint16(d.buf[d.off+0 : d.off+2]) + g := d.byteOrder.Uint16(d.buf[d.off+2 : d.off+4]) + b := d.byteOrder.Uint16(d.buf[d.off+4 : d.off+6]) + a := d.byteOrder.Uint16(d.buf[d.off+6 : d.off+8]) + d.off += 8 + img.SetNRGBA64(x, y, color.NRGBA64{r, g, b, a}) + } + } + } else { + img := dst.(*image.NRGBA) + for y := ymin; y < rMaxY; y++ { + min := img.PixOffset(xmin, y) + max := img.PixOffset(rMaxX, y) + i0, i1 := (y-ymin)*(xmax-xmin)*4, (y-ymin+1)*(xmax-xmin)*4 + if i1 > len(d.buf) { + return errNoPixels + } + copy(img.Pix[min:max], d.buf[i0:i1]) + } + } + case mRGBA: + if d.bpp == 16 { + img := dst.(*image.RGBA64) + for y := ymin; y < rMaxY; y++ { + for x := xmin; x < rMaxX; x++ { + if d.off+8 > len(d.buf) { + return errNoPixels + } + r := d.byteOrder.Uint16(d.buf[d.off+0 : d.off+2]) + g := d.byteOrder.Uint16(d.buf[d.off+2 : d.off+4]) + b := d.byteOrder.Uint16(d.buf[d.off+4 : d.off+6]) + a := d.byteOrder.Uint16(d.buf[d.off+6 : d.off+8]) + d.off += 8 + img.SetRGBA64(x, y, color.RGBA64{r, g, b, a}) + } + } + } else { + img := dst.(*image.RGBA) + for y := ymin; y < rMaxY; y++ { + min := img.PixOffset(xmin, y) + max := img.PixOffset(rMaxX, y) + i0, i1 := (y-ymin)*(xmax-xmin)*4, (y-ymin+1)*(xmax-xmin)*4 + if i1 > len(d.buf) { + return errNoPixels + } + copy(img.Pix[min:max], d.buf[i0:i1]) + } + } + } + + return nil +} + +func newDecoder(r io.Reader) (*decoder, error) { + d := &decoder{ + r: newReaderAt(r), + features: make(map[int][]uint), + } + + p := make([]byte, 8) + if _, err := d.r.ReadAt(p, 0); err != nil { + return nil, err + } + switch string(p[0:4]) { + case leHeader: + d.byteOrder = binary.LittleEndian + case beHeader: + d.byteOrder = binary.BigEndian + default: + return nil, FormatError("malformed header") + } + + ifdOffset := int64(d.byteOrder.Uint32(p[4:8])) + + // The first two bytes contain the number of entries (12 bytes each). + if _, err := d.r.ReadAt(p[0:2], ifdOffset); err != nil { + return nil, err + } + numItems := int(d.byteOrder.Uint16(p[0:2])) + + // All IFD entries are read in one chunk. + p = make([]byte, ifdLen*numItems) + if _, err := d.r.ReadAt(p, ifdOffset+2); err != nil { + return nil, err + } + + prevTag := -1 + for i := 0; i < len(p); i += ifdLen { + tag, err := d.parseIFD(p[i : i+ifdLen]) + if err != nil { + return nil, err + } + if tag <= prevTag { + return nil, FormatError("tags are not sorted in ascending order") + } + prevTag = tag + } + + d.config.Width = int(d.firstVal(tImageWidth)) + d.config.Height = int(d.firstVal(tImageLength)) + + if _, ok := d.features[tBitsPerSample]; !ok { + // Default is 1 per specification. + d.features[tBitsPerSample] = []uint{1} + } + d.bpp = d.firstVal(tBitsPerSample) + switch d.bpp { + case 0: + return nil, FormatError("BitsPerSample must not be 0") + case 1, 8, 16: + // Nothing to do, these are accepted by this implementation. + default: + return nil, UnsupportedError(fmt.Sprintf("BitsPerSample of %v", d.bpp)) + } + + // Determine the image mode. + switch d.firstVal(tPhotometricInterpretation) { + case pRGB: + if d.bpp == 16 { + for _, b := range d.features[tBitsPerSample] { + if b != 16 { + return nil, FormatError("wrong number of samples for 16bit RGB") + } + } + } else { + for _, b := range d.features[tBitsPerSample] { + if b != 8 { + return nil, FormatError("wrong number of samples for 8bit RGB") + } + } + } + // RGB images normally have 3 samples per pixel. + // If there are more, ExtraSamples (p. 31-32 of the spec) + // gives their meaning (usually an alpha channel). + // + // This implementation does not support extra samples + // of an unspecified type. + switch len(d.features[tBitsPerSample]) { + case 3: + d.mode = mRGB + if d.bpp == 16 { + d.config.ColorModel = color.RGBA64Model + } else { + d.config.ColorModel = color.RGBAModel + } + case 4: + switch d.firstVal(tExtraSamples) { + case 1: + d.mode = mRGBA + if d.bpp == 16 { + d.config.ColorModel = color.RGBA64Model + } else { + d.config.ColorModel = color.RGBAModel + } + case 2: + d.mode = mNRGBA + if d.bpp == 16 { + d.config.ColorModel = color.NRGBA64Model + } else { + d.config.ColorModel = color.NRGBAModel + } + default: + return nil, FormatError("wrong number of samples for RGB") + } + default: + return nil, FormatError("wrong number of samples for RGB") + } + case pPaletted: + d.mode = mPaletted + d.config.ColorModel = color.Palette(d.palette) + case pWhiteIsZero: + d.mode = mGrayInvert + if d.bpp == 16 { + d.config.ColorModel = color.Gray16Model + } else { + d.config.ColorModel = color.GrayModel + } + case pBlackIsZero: + d.mode = mGray + if d.bpp == 16 { + d.config.ColorModel = color.Gray16Model + } else { + d.config.ColorModel = color.GrayModel + } + default: + return nil, UnsupportedError("color model") + } + + return d, nil +} + +// DecodeConfig returns the color model and dimensions of a TIFF image without +// decoding the entire image. +func DecodeConfig(r io.Reader) (image.Config, error) { + d, err := newDecoder(r) + if err != nil { + return image.Config{}, err + } + return d.config, nil +} + +func ccittFillOrder(tiffFillOrder uint) ccitt.Order { + if tiffFillOrder == 2 { + return ccitt.LSB + } + return ccitt.MSB +} + +// Decode reads a TIFF image from r and returns it as an image.Image. +// The type of Image returned depends on the contents of the TIFF. +func Decode(r io.Reader) (img image.Image, err error) { + d, err := newDecoder(r) + if err != nil { + return + } + + blockPadding := false + blockWidth := d.config.Width + blockHeight := d.config.Height + blocksAcross := 1 + blocksDown := 1 + + if d.config.Width == 0 { + blocksAcross = 0 + } + if d.config.Height == 0 { + blocksDown = 0 + } + + var blockOffsets, blockCounts []uint + + if int(d.firstVal(tTileWidth)) != 0 { + blockPadding = true + + blockWidth = int(d.firstVal(tTileWidth)) + blockHeight = int(d.firstVal(tTileLength)) + + if blockWidth != 0 { + blocksAcross = (d.config.Width + blockWidth - 1) / blockWidth + } + if blockHeight != 0 { + blocksDown = (d.config.Height + blockHeight - 1) / blockHeight + } + + blockCounts = d.features[tTileByteCounts] + blockOffsets = d.features[tTileOffsets] + + } else { + if int(d.firstVal(tRowsPerStrip)) != 0 { + blockHeight = int(d.firstVal(tRowsPerStrip)) + } + + if blockHeight != 0 { + blocksDown = (d.config.Height + blockHeight - 1) / blockHeight + } + + blockOffsets = d.features[tStripOffsets] + blockCounts = d.features[tStripByteCounts] + } + + // Check if we have the right number of strips/tiles, offsets and counts. + if n := blocksAcross * blocksDown; len(blockOffsets) < n || len(blockCounts) < n { + return nil, FormatError("inconsistent header") + } + + imgRect := image.Rect(0, 0, d.config.Width, d.config.Height) + switch d.mode { + case mGray, mGrayInvert: + if d.bpp == 16 { + img = image.NewGray16(imgRect) + } else { + img = image.NewGray(imgRect) + } + case mPaletted: + img = image.NewPaletted(imgRect, d.palette) + case mNRGBA: + if d.bpp == 16 { + img = image.NewNRGBA64(imgRect) + } else { + img = image.NewNRGBA(imgRect) + } + case mRGB, mRGBA: + if d.bpp == 16 { + img = image.NewRGBA64(imgRect) + } else { + img = image.NewRGBA(imgRect) + } + } + + for i := 0; i < blocksAcross; i++ { + blkW := blockWidth + if !blockPadding && i == blocksAcross-1 && d.config.Width%blockWidth != 0 { + blkW = d.config.Width % blockWidth + } + for j := 0; j < blocksDown; j++ { + blkH := blockHeight + if !blockPadding && j == blocksDown-1 && d.config.Height%blockHeight != 0 { + blkH = d.config.Height % blockHeight + } + offset := int64(blockOffsets[j*blocksAcross+i]) + n := int64(blockCounts[j*blocksAcross+i]) + switch d.firstVal(tCompression) { + + // According to the spec, Compression does not have a default value, + // but some tools interpret a missing Compression value as none so we do + // the same. + case cNone, 0: + if b, ok := d.r.(*buffer); ok { + d.buf, err = b.Slice(int(offset), int(n)) + } else { + d.buf = make([]byte, n) + _, err = d.r.ReadAt(d.buf, offset) + } + case cG3: + inv := d.firstVal(tPhotometricInterpretation) == pWhiteIsZero + order := ccittFillOrder(d.firstVal(tFillOrder)) + r := ccitt.NewReader(io.NewSectionReader(d.r, offset, n), order, ccitt.Group3, blkW, blkH, &ccitt.Options{Invert: inv, Align: false}) + d.buf, err = ioutil.ReadAll(r) + case cG4: + inv := d.firstVal(tPhotometricInterpretation) == pWhiteIsZero + order := ccittFillOrder(d.firstVal(tFillOrder)) + r := ccitt.NewReader(io.NewSectionReader(d.r, offset, n), order, ccitt.Group4, blkW, blkH, &ccitt.Options{Invert: inv, Align: false}) + d.buf, err = ioutil.ReadAll(r) + case cLZW: + r := lzw.NewReader(io.NewSectionReader(d.r, offset, n), lzw.MSB, 8) + d.buf, err = ioutil.ReadAll(r) + r.Close() + case cDeflate, cDeflateOld: + var r io.ReadCloser + r, err = zlib.NewReader(io.NewSectionReader(d.r, offset, n)) + if err != nil { + return nil, err + } + d.buf, err = ioutil.ReadAll(r) + r.Close() + case cPackBits: + d.buf, err = unpackBits(io.NewSectionReader(d.r, offset, n)) + default: + err = UnsupportedError(fmt.Sprintf("compression value %d", d.firstVal(tCompression))) + } + if err != nil { + return nil, err + } + + xmin := i * blockWidth + ymin := j * blockHeight + xmax := xmin + blkW + ymax := ymin + blkH + err = d.decode(img, xmin, ymin, xmax, ymax) + if err != nil { + return nil, err + } + } + } + return +} + +func init() { + image.RegisterFormat("tiff", leHeader, Decode, DecodeConfig) + image.RegisterFormat("tiff", beHeader, Decode, DecodeConfig) +} diff --git a/vendor/golang.org/x/image/tiff/writer.go b/vendor/golang.org/x/image/tiff/writer.go new file mode 100644 index 000000000..c8a01cea7 --- /dev/null +++ b/vendor/golang.org/x/image/tiff/writer.go @@ -0,0 +1,438 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tiff + +import ( + "bytes" + "compress/zlib" + "encoding/binary" + "image" + "io" + "sort" +) + +// The TIFF format allows to choose the order of the different elements freely. +// The basic structure of a TIFF file written by this package is: +// +// 1. Header (8 bytes). +// 2. Image data. +// 3. Image File Directory (IFD). +// 4. "Pointer area" for larger entries in the IFD. + +// We only write little-endian TIFF files. +var enc = binary.LittleEndian + +// An ifdEntry is a single entry in an Image File Directory. +// A value of type dtRational is composed of two 32-bit values, +// thus data contains two uints (numerator and denominator) for a single number. +type ifdEntry struct { + tag int + datatype int + data []uint32 +} + +func (e ifdEntry) putData(p []byte) { + for _, d := range e.data { + switch e.datatype { + case dtByte, dtASCII: + p[0] = byte(d) + p = p[1:] + case dtShort: + enc.PutUint16(p, uint16(d)) + p = p[2:] + case dtLong, dtRational: + enc.PutUint32(p, uint32(d)) + p = p[4:] + } + } +} + +type byTag []ifdEntry + +func (d byTag) Len() int { return len(d) } +func (d byTag) Less(i, j int) bool { return d[i].tag < d[j].tag } +func (d byTag) Swap(i, j int) { d[i], d[j] = d[j], d[i] } + +func encodeGray(w io.Writer, pix []uint8, dx, dy, stride int, predictor bool) error { + if !predictor { + return writePix(w, pix, dy, dx, stride) + } + buf := make([]byte, dx) + for y := 0; y < dy; y++ { + min := y*stride + 0 + max := y*stride + dx + off := 0 + var v0 uint8 + for i := min; i < max; i++ { + v1 := pix[i] + buf[off] = v1 - v0 + v0 = v1 + off++ + } + if _, err := w.Write(buf); err != nil { + return err + } + } + return nil +} + +func encodeGray16(w io.Writer, pix []uint8, dx, dy, stride int, predictor bool) error { + buf := make([]byte, dx*2) + for y := 0; y < dy; y++ { + min := y*stride + 0 + max := y*stride + dx*2 + off := 0 + var v0 uint16 + for i := min; i < max; i += 2 { + // An image.Gray16's Pix is in big-endian order. + v1 := uint16(pix[i])<<8 | uint16(pix[i+1]) + if predictor { + v0, v1 = v1, v1-v0 + } + // We only write little-endian TIFF files. + buf[off+0] = byte(v1) + buf[off+1] = byte(v1 >> 8) + off += 2 + } + if _, err := w.Write(buf); err != nil { + return err + } + } + return nil +} + +func encodeRGBA(w io.Writer, pix []uint8, dx, dy, stride int, predictor bool) error { + if !predictor { + return writePix(w, pix, dy, dx*4, stride) + } + buf := make([]byte, dx*4) + for y := 0; y < dy; y++ { + min := y*stride + 0 + max := y*stride + dx*4 + off := 0 + var r0, g0, b0, a0 uint8 + for i := min; i < max; i += 4 { + r1, g1, b1, a1 := pix[i+0], pix[i+1], pix[i+2], pix[i+3] + buf[off+0] = r1 - r0 + buf[off+1] = g1 - g0 + buf[off+2] = b1 - b0 + buf[off+3] = a1 - a0 + off += 4 + r0, g0, b0, a0 = r1, g1, b1, a1 + } + if _, err := w.Write(buf); err != nil { + return err + } + } + return nil +} + +func encodeRGBA64(w io.Writer, pix []uint8, dx, dy, stride int, predictor bool) error { + buf := make([]byte, dx*8) + for y := 0; y < dy; y++ { + min := y*stride + 0 + max := y*stride + dx*8 + off := 0 + var r0, g0, b0, a0 uint16 + for i := min; i < max; i += 8 { + // An image.RGBA64's Pix is in big-endian order. + r1 := uint16(pix[i+0])<<8 | uint16(pix[i+1]) + g1 := uint16(pix[i+2])<<8 | uint16(pix[i+3]) + b1 := uint16(pix[i+4])<<8 | uint16(pix[i+5]) + a1 := uint16(pix[i+6])<<8 | uint16(pix[i+7]) + if predictor { + r0, r1 = r1, r1-r0 + g0, g1 = g1, g1-g0 + b0, b1 = b1, b1-b0 + a0, a1 = a1, a1-a0 + } + // We only write little-endian TIFF files. + buf[off+0] = byte(r1) + buf[off+1] = byte(r1 >> 8) + buf[off+2] = byte(g1) + buf[off+3] = byte(g1 >> 8) + buf[off+4] = byte(b1) + buf[off+5] = byte(b1 >> 8) + buf[off+6] = byte(a1) + buf[off+7] = byte(a1 >> 8) + off += 8 + } + if _, err := w.Write(buf); err != nil { + return err + } + } + return nil +} + +func encode(w io.Writer, m image.Image, predictor bool) error { + bounds := m.Bounds() + buf := make([]byte, 4*bounds.Dx()) + for y := bounds.Min.Y; y < bounds.Max.Y; y++ { + off := 0 + if predictor { + var r0, g0, b0, a0 uint8 + for x := bounds.Min.X; x < bounds.Max.X; x++ { + r, g, b, a := m.At(x, y).RGBA() + r1 := uint8(r >> 8) + g1 := uint8(g >> 8) + b1 := uint8(b >> 8) + a1 := uint8(a >> 8) + buf[off+0] = r1 - r0 + buf[off+1] = g1 - g0 + buf[off+2] = b1 - b0 + buf[off+3] = a1 - a0 + off += 4 + r0, g0, b0, a0 = r1, g1, b1, a1 + } + } else { + for x := bounds.Min.X; x < bounds.Max.X; x++ { + r, g, b, a := m.At(x, y).RGBA() + buf[off+0] = uint8(r >> 8) + buf[off+1] = uint8(g >> 8) + buf[off+2] = uint8(b >> 8) + buf[off+3] = uint8(a >> 8) + off += 4 + } + } + if _, err := w.Write(buf); err != nil { + return err + } + } + return nil +} + +// writePix writes the internal byte array of an image to w. It is less general +// but much faster then encode. writePix is used when pix directly +// corresponds to one of the TIFF image types. +func writePix(w io.Writer, pix []byte, nrows, length, stride int) error { + if length == stride { + _, err := w.Write(pix[:nrows*length]) + return err + } + for ; nrows > 0; nrows-- { + if _, err := w.Write(pix[:length]); err != nil { + return err + } + pix = pix[stride:] + } + return nil +} + +func writeIFD(w io.Writer, ifdOffset int, d []ifdEntry) error { + var buf [ifdLen]byte + // Make space for "pointer area" containing IFD entry data + // longer than 4 bytes. + parea := make([]byte, 1024) + pstart := ifdOffset + ifdLen*len(d) + 6 + var o int // Current offset in parea. + + // The IFD has to be written with the tags in ascending order. + sort.Sort(byTag(d)) + + // Write the number of entries in this IFD. + if err := binary.Write(w, enc, uint16(len(d))); err != nil { + return err + } + for _, ent := range d { + enc.PutUint16(buf[0:2], uint16(ent.tag)) + enc.PutUint16(buf[2:4], uint16(ent.datatype)) + count := uint32(len(ent.data)) + if ent.datatype == dtRational { + count /= 2 + } + enc.PutUint32(buf[4:8], count) + datalen := int(count * lengths[ent.datatype]) + if datalen <= 4 { + ent.putData(buf[8:12]) + } else { + if (o + datalen) > len(parea) { + newlen := len(parea) + 1024 + for (o + datalen) > newlen { + newlen += 1024 + } + newarea := make([]byte, newlen) + copy(newarea, parea) + parea = newarea + } + ent.putData(parea[o : o+datalen]) + enc.PutUint32(buf[8:12], uint32(pstart+o)) + o += datalen + } + if _, err := w.Write(buf[:]); err != nil { + return err + } + } + // The IFD ends with the offset of the next IFD in the file, + // or zero if it is the last one (page 14). + if err := binary.Write(w, enc, uint32(0)); err != nil { + return err + } + _, err := w.Write(parea[:o]) + return err +} + +// Options are the encoding parameters. +type Options struct { + // Compression is the type of compression used. + Compression CompressionType + // Predictor determines whether a differencing predictor is used; + // if true, instead of each pixel's color, the color difference to the + // preceding one is saved. This improves the compression for certain + // types of images and compressors. For example, it works well for + // photos with Deflate compression. + Predictor bool +} + +// Encode writes the image m to w. opt determines the options used for +// encoding, such as the compression type. If opt is nil, an uncompressed +// image is written. +func Encode(w io.Writer, m image.Image, opt *Options) error { + d := m.Bounds().Size() + + compression := uint32(cNone) + predictor := false + if opt != nil { + compression = opt.Compression.specValue() + // The predictor field is only used with LZW. See page 64 of the spec. + predictor = opt.Predictor && compression == cLZW + } + + _, err := io.WriteString(w, leHeader) + if err != nil { + return err + } + + // Compressed data is written into a buffer first, so that we + // know the compressed size. + var buf bytes.Buffer + // dst holds the destination for the pixel data of the image -- + // either w or a writer to buf. + var dst io.Writer + // imageLen is the length of the pixel data in bytes. + // The offset of the IFD is imageLen + 8 header bytes. + var imageLen int + + switch compression { + case cNone: + dst = w + // Write IFD offset before outputting pixel data. + switch m.(type) { + case *image.Paletted: + imageLen = d.X * d.Y * 1 + case *image.Gray: + imageLen = d.X * d.Y * 1 + case *image.Gray16: + imageLen = d.X * d.Y * 2 + case *image.RGBA64: + imageLen = d.X * d.Y * 8 + case *image.NRGBA64: + imageLen = d.X * d.Y * 8 + default: + imageLen = d.X * d.Y * 4 + } + err = binary.Write(w, enc, uint32(imageLen+8)) + if err != nil { + return err + } + case cDeflate: + dst = zlib.NewWriter(&buf) + } + + pr := uint32(prNone) + photometricInterpretation := uint32(pRGB) + samplesPerPixel := uint32(4) + bitsPerSample := []uint32{8, 8, 8, 8} + extraSamples := uint32(0) + colorMap := []uint32{} + + if predictor { + pr = prHorizontal + } + switch m := m.(type) { + case *image.Paletted: + photometricInterpretation = pPaletted + samplesPerPixel = 1 + bitsPerSample = []uint32{8} + colorMap = make([]uint32, 256*3) + for i := 0; i < 256 && i < len(m.Palette); i++ { + r, g, b, _ := m.Palette[i].RGBA() + colorMap[i+0*256] = uint32(r) + colorMap[i+1*256] = uint32(g) + colorMap[i+2*256] = uint32(b) + } + err = encodeGray(dst, m.Pix, d.X, d.Y, m.Stride, predictor) + case *image.Gray: + photometricInterpretation = pBlackIsZero + samplesPerPixel = 1 + bitsPerSample = []uint32{8} + err = encodeGray(dst, m.Pix, d.X, d.Y, m.Stride, predictor) + case *image.Gray16: + photometricInterpretation = pBlackIsZero + samplesPerPixel = 1 + bitsPerSample = []uint32{16} + err = encodeGray16(dst, m.Pix, d.X, d.Y, m.Stride, predictor) + case *image.NRGBA: + extraSamples = 2 // Unassociated alpha. + err = encodeRGBA(dst, m.Pix, d.X, d.Y, m.Stride, predictor) + case *image.NRGBA64: + extraSamples = 2 // Unassociated alpha. + bitsPerSample = []uint32{16, 16, 16, 16} + err = encodeRGBA64(dst, m.Pix, d.X, d.Y, m.Stride, predictor) + case *image.RGBA: + extraSamples = 1 // Associated alpha. + err = encodeRGBA(dst, m.Pix, d.X, d.Y, m.Stride, predictor) + case *image.RGBA64: + extraSamples = 1 // Associated alpha. + bitsPerSample = []uint32{16, 16, 16, 16} + err = encodeRGBA64(dst, m.Pix, d.X, d.Y, m.Stride, predictor) + default: + extraSamples = 1 // Associated alpha. + err = encode(dst, m, predictor) + } + if err != nil { + return err + } + + if compression != cNone { + if err = dst.(io.Closer).Close(); err != nil { + return err + } + imageLen = buf.Len() + if err = binary.Write(w, enc, uint32(imageLen+8)); err != nil { + return err + } + if _, err = buf.WriteTo(w); err != nil { + return err + } + } + + ifd := []ifdEntry{ + {tImageWidth, dtShort, []uint32{uint32(d.X)}}, + {tImageLength, dtShort, []uint32{uint32(d.Y)}}, + {tBitsPerSample, dtShort, bitsPerSample}, + {tCompression, dtShort, []uint32{compression}}, + {tPhotometricInterpretation, dtShort, []uint32{photometricInterpretation}}, + {tStripOffsets, dtLong, []uint32{8}}, + {tSamplesPerPixel, dtShort, []uint32{samplesPerPixel}}, + {tRowsPerStrip, dtShort, []uint32{uint32(d.Y)}}, + {tStripByteCounts, dtLong, []uint32{uint32(imageLen)}}, + // There is currently no support for storing the image + // resolution, so give a bogus value of 72x72 dpi. + {tXResolution, dtRational, []uint32{72, 1}}, + {tYResolution, dtRational, []uint32{72, 1}}, + {tResolutionUnit, dtShort, []uint32{resPerInch}}, + } + if pr != prNone { + ifd = append(ifd, ifdEntry{tPredictor, dtShort, []uint32{pr}}) + } + if len(colorMap) != 0 { + ifd = append(ifd, ifdEntry{tColorMap, dtShort, colorMap}) + } + if extraSamples > 0 { + ifd = append(ifd, ifdEntry{tExtraSamples, dtShort, []uint32{extraSamples}}) + } + + return writeIFD(w, imageLen+8, ifd) +} diff --git a/vendor/gopkg.in/ini.v1/.travis.yml b/vendor/gopkg.in/ini.v1/.travis.yml deleted file mode 100644 index 149b7249f..000000000 --- a/vendor/gopkg.in/ini.v1/.travis.yml +++ /dev/null @@ -1,20 +0,0 @@ -sudo: false -language: go -go: - - 1.6.x - - 1.7.x - - 1.8.x - - 1.9.x - - 1.10.x - - 1.11.x - - 1.12.x - - 1.13.x - -install: skip -script: - - go get golang.org/x/tools/cmd/cover - - go get github.com/smartystreets/goconvey - - mkdir -p $HOME/gopath/src/gopkg.in - - ln -s $HOME/gopath/src/github.com/go-ini/ini $HOME/gopath/src/gopkg.in/ini.v1 - - cd $HOME/gopath/src/gopkg.in/ini.v1 - - go test -v -cover -race diff --git a/vendor/modules.txt b/vendor/modules.txt index 383109df4..2f4348f65 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -119,6 +119,7 @@ github.com/alibabacloud-go/openapi-util/service github.com/alibabacloud-go/tea/tea github.com/alibabacloud-go/tea/utils # github.com/alibabacloud-go/tea-utils v1.4.3 +## explicit github.com/alibabacloud-go/tea-utils/service # github.com/alibabacloud-go/tea-xml v1.1.2 ## explicit @@ -278,6 +279,9 @@ github.com/denisenkom/go-mssqldb/internal/querytext # github.com/dgrijalva/jwt-go v3.2.0+incompatible ## explicit github.com/dgrijalva/jwt-go +# github.com/disintegration/imaging v1.6.2 +## explicit +github.com/disintegration/imaging # github.com/dustin/go-humanize v1.0.0 ## explicit github.com/dustin/go-humanize @@ -490,6 +494,7 @@ github.com/google/go-github/v24/github # github.com/google/go-querystring v1.0.0 github.com/google/go-querystring/query # github.com/google/uuid v1.1.1 +## explicit github.com/google/uuid # github.com/googleapis/gax-go/v2 v2.0.5 github.com/googleapis/gax-go/v2 @@ -939,6 +944,11 @@ golang.org/x/crypto/ssh golang.org/x/crypto/ssh/agent golang.org/x/crypto/ssh/internal/bcrypt_pbkdf golang.org/x/crypto/ssh/knownhosts +# golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 +golang.org/x/image/bmp +golang.org/x/image/ccitt +golang.org/x/image/tiff +golang.org/x/image/tiff/lzw # golang.org/x/mod v0.3.0 ## explicit golang.org/x/mod/module From b0a70369ca79aed5c25130147097afff6e092d2d Mon Sep 17 00:00:00 2001 From: ychao_1983 Date: Thu, 2 Jun 2022 17:37:42 +0800 Subject: [PATCH 03/45] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/img/slide/bg/1.png | Bin 0 -> 172330 bytes public/img/slide/bg/2.png | Bin 0 -> 116123 bytes public/img/slide/bg/3.png | Bin 0 -> 163560 bytes public/img/slide/bg/4.png | Bin 0 -> 163210 bytes public/img/slide/mask/mask.png | Bin 0 -> 199 bytes 5 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 public/img/slide/bg/1.png create mode 100644 public/img/slide/bg/2.png create mode 100644 public/img/slide/bg/3.png create mode 100644 public/img/slide/bg/4.png create mode 100644 public/img/slide/mask/mask.png diff --git a/public/img/slide/bg/1.png b/public/img/slide/bg/1.png new file mode 100644 index 0000000000000000000000000000000000000000..d76aa372574a4fd0716572b6333bee01b3faea7d GIT binary patch literal 172330 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�F*2dc1z1@{gFUxYXHy2Wd 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 KMNUMnLSTZ8EQb>S literal 0 HcmV?d00001 diff --git a/public/img/slide/bg/2.png b/public/img/slide/bg/2.png new file mode 100644 index 0000000000000000000000000000000000000000..73536b138dae339095c9ee015c22c4476e94f1fa GIT binary patch literal 116123 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 literal 0 HcmV?d00001 diff --git a/public/img/slide/bg/3.png b/public/img/slide/bg/3.png new file mode 100644 index 0000000000000000000000000000000000000000..530e5b5c0ec50c4958b637196b36bf1d21b32173 GIT binary patch literal 163560 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%0D)$J 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%0AXCW4qck6&|@H9i6T#D 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�XHMC=@6FK{g;F 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 o=0OoWp>a*92UUd+-hcG}0kg#c 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�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 + diff --git a/templates/user/auth/signup_inner.tmpl b/templates/user/auth/signup_inner.tmpl index bb3c343b7..0f0279b92 100644 --- a/templates/user/auth/signup_inner.tmpl +++ b/templates/user/auth/signup_inner.tmpl @@ -64,6 +64,10 @@ {{end}} + {{if .EnablePhone }} + {{template "user/auth/phone_verify" .}} + {{end}} +
From c0f2f671ff369beac99b4ce178ce18699673779f Mon Sep 17 00:00:00 2001 From: chenshihai Date: Mon, 20 Jun 2022 16:04:01 +0800 Subject: [PATCH 09/45] =?UTF-8?q?=E6=89=8B=E6=9C=BA=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E7=A0=81=20Signed-off-by:=20chenshihai=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- templates/user/auth/phone_verify.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/user/auth/phone_verify.tmpl b/templates/user/auth/phone_verify.tmpl index 6898b4d36..8f8e2ea87 100644 --- a/templates/user/auth/phone_verify.tmpl +++ b/templates/user/auth/phone_verify.tmpl @@ -291,8 +291,8 @@ type: 'post', dataType: 'json', data: { - slideID: self.imgID, - x: self.dom.find('.slide-image-small').position().left + slide_id: self.imgID, + x: parseInt(self.dom.find('.slide-image-small').position().left) }, success: function(res) { if (res && res.code === 0) { From da58267eeb849e10b531ef06d4f510d9fc24cf17 Mon Sep 17 00:00:00 2001 From: ychao_1983 Date: Mon, 20 Jun 2022 16:09:24 +0800 Subject: [PATCH 10/45] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/slideimage/slideimage.go | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/slideimage/slideimage.go b/modules/slideimage/slideimage.go index 302729649..748cc2d6c 100644 --- a/modules/slideimage/slideimage.go +++ b/modules/slideimage/slideimage.go @@ -173,6 +173,7 @@ func (s *SlideImage) Verify(id string, x int) bool { imageRandX, _ := strconv.Atoi(values[1]) if int(math.Abs(float64(imageRandX-x))) <= s.Tolerance { redis_client.SETNX(redisConn, s.mkey(id), "1", s.Expiration) + return true } return false From c990a850cbd7c9c61ee1d720913d13abc7e85e26 Mon Sep 17 00:00:00 2001 From: chenshihai Date: Mon, 20 Jun 2022 16:53:26 +0800 Subject: [PATCH 11/45] =?UTF-8?q?=E6=89=8B=E6=9C=BA=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E7=A0=81=20Signed-off-by:=20chenshihai=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- templates/user/auth/phone_verify.tmpl | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/templates/user/auth/phone_verify.tmpl b/templates/user/auth/phone_verify.tmpl index 8f8e2ea87..1942835df 100644 --- a/templates/user/auth/phone_verify.tmpl +++ b/templates/user/auth/phone_verify.tmpl @@ -195,7 +195,7 @@
- +
@@ -210,7 +210,7 @@
- +
获取验证码
@@ -295,7 +295,7 @@ x: parseInt(self.dom.find('.slide-image-small').position().left) }, success: function(res) { - if (res && res.code === 0) { + if (res && res.Code === 0) { self.verifySucess = true; self.canSendCode = true; self.dom.find('.slide-bar').addClass('sucess'); @@ -338,9 +338,28 @@ var phoneNumber = self.dom.find('.phone-num-c input').val(); if (!/^1[3578]\d{9}$/.test(phoneNumber)) { self.dom.find('.phone-num-c').addClass('error'); + return; } else { self.dom.find('.phone-num-c').removeClass('error'); self.countDown(); + $.ajax({ + url: '/sendVerifyCode', + type: 'post', + dataType: 'json', + data: { + phone_number: phoneNumber, + mode: 0, // 0注册,1登录 ,2修改手机号,3找回密码 + slide_id: self.imgID, + }, + success: function(res) { + if (res && res.code === 0) { + console.log(res); + } + }, + error: function(err) { + console.log(err); + } + }); } }); }; From 8dd77eec2ffed9a9044f6fce5f702e933007c7df Mon Sep 17 00:00:00 2001 From: ychao_1983 Date: Mon, 20 Jun 2022 17:00:03 +0800 Subject: [PATCH 12/45] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/auth/user_form.go | 4 ++-- routers/user/auth.go | 39 ++++++++++++++++++++------------------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/modules/auth/user_form.go b/modules/auth/user_form.go index b7c2c5ff3..8575e7344 100755 --- a/modules/auth/user_form.go +++ b/modules/auth/user_form.go @@ -107,7 +107,7 @@ func (f RegisterForm) IsEmailDomainWhitelisted() bool { } domain := strings.ToLower(f.Email[n+1:]) - + //support edu.cn if strings.HasSuffix(domain, "edu.cn") { return true @@ -371,7 +371,7 @@ func (f *U2FDeleteForm) Validate(ctx *macaron.Context, errs binding.Errors) bind type PhoneNumberForm struct { PhoneNumber string `binding:"Required;MaxSize(20)"` - IsSignUp bool `binding:"Required"` + Mode int `binding:"Required"` SlideID string `binding:"Required;MaxSize(100)"` } diff --git a/routers/user/auth.go b/routers/user/auth.go index c0c715c31..6d77468e1 100755 --- a/routers/user/auth.go +++ b/routers/user/auth.go @@ -1796,7 +1796,7 @@ func SendVerifyCode(ctx *context.Context, slideImage *slideimage.SlideImage, for return } - if !ctx.IsSigned { + if form.Mode != 2 { has, err := models.IsUserByPhoneNumberExist(phoneNumber) if err != nil { log.Warn("sql err", err) @@ -1804,13 +1804,13 @@ func SendVerifyCode(ctx *context.Context, slideImage *slideimage.SlideImage, for return } - if form.IsSignUp { //注册 + if form.Mode==0 || form.Mode==3{ //注册或绑定 if has { ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.already_register"))) return } - } else { //手机号验证码登录 + } else { //手机号验证码登录 mode=1 if !has { ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.not_register"))) return @@ -1818,27 +1818,28 @@ func SendVerifyCode(ctx *context.Context, slideImage *slideimage.SlideImage, for } - } else { //修改手机号 - u, err := models.GetUserByPhoneNumber(phoneNumber) - if err != nil && !models.IsErrUserNotExist(err) { - log.Warn("sql err", err) - ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.query_err"))) - return - } - - if u != nil { - - if u.ID == ctx.User.ID { //没有修改手机号 - ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.not_modify"))) - return - } else { //修改的手机已经被别的用户注册 - ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.already_register"))) + } else { + //修改手机号 mode=2 + u, err := models.GetUserByPhoneNumber(phoneNumber) + if err != nil && !models.IsErrUserNotExist(err) { + log.Warn("sql err", err) + ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.query_err"))) return } + if u != nil { + + if u.ID == ctx.User.ID { //没有修改手机号 + ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.not_modify"))) + return + } else { //修改的手机已经被别的用户注册 + ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.already_register"))) + return + } + + } } - } redisConn := labelmsg.Get() defer redisConn.Close() From 49157ddb5b9387348493eb04c287e675e7a2f2ac Mon Sep 17 00:00:00 2001 From: ychao_1983 Date: Mon, 20 Jun 2022 17:26:33 +0800 Subject: [PATCH 13/45] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- routers/user/auth.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/routers/user/auth.go b/routers/user/auth.go index 6d77468e1..8e6ca9f42 100755 --- a/routers/user/auth.go +++ b/routers/user/auth.go @@ -8,6 +8,7 @@ package user import ( "errors" "fmt" + "github.com/gomodule/redigo/redis" "net/http" "strings" @@ -1845,7 +1846,7 @@ func SendVerifyCode(ctx *context.Context, slideImage *slideimage.SlideImage, for defer redisConn.Close() sendTimes, err := phoneService.GetPhoneNumberSendTimes(redisConn, phoneNumber) - if err != nil { + if err != nil && err!=redis.ErrNil { log.Warn("redis err", err) ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.query_err"))) return From 17e4a6418098e467296ac4ff4676877ff99223de Mon Sep 17 00:00:00 2001 From: chenshihai Date: Tue, 21 Jun 2022 10:54:29 +0800 Subject: [PATCH 14/45] =?UTF-8?q?=E6=89=8B=E6=9C=BA=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- templates/user/auth/bind_phone.tmpl | 1 + templates/user/auth/phone_verify.tmpl | 12 +++--- templates/user/auth/signin_navbar.tmpl | 10 ++++- templates/user/auth/signin_phone.tmpl | 58 ++++++++++++++++++++++++++ templates/user/auth/signup_inner.tmpl | 4 +- templates/user/settings/profile.tmpl | 10 ++++- 6 files changed, 86 insertions(+), 9 deletions(-) create mode 100644 templates/user/auth/bind_phone.tmpl create mode 100644 templates/user/auth/signin_phone.tmpl diff --git a/templates/user/auth/bind_phone.tmpl b/templates/user/auth/bind_phone.tmpl new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/templates/user/auth/bind_phone.tmpl @@ -0,0 +1 @@ + diff --git a/templates/user/auth/phone_verify.tmpl b/templates/user/auth/phone_verify.tmpl index 1942835df..84d053a1e 100644 --- a/templates/user/auth/phone_verify.tmpl +++ b/templates/user/auth/phone_verify.tmpl @@ -33,7 +33,7 @@ .phone-c .phone-num-c input { height: 100%; - width: 100%; + width: 100% !important; padding: 9.5px 14px; box-sizing: border-box; border: none; @@ -116,6 +116,7 @@ top: 36px; left: 0; border-radius: 2px; + z-index: 100; display: none; } @@ -151,7 +152,7 @@ .verify-code-c .verify-code-num-c input { height: 100%; - width: 100%; + width: 100% !important; padding: 9.5px 14px; box-sizing: border-box; border: none; @@ -195,7 +196,7 @@
- +
@@ -210,7 +211,7 @@
- +
获取验证码
@@ -341,6 +342,7 @@ return; } else { self.dom.find('.phone-num-c').removeClass('error'); + var useType = self.dom.closest('div.use-type').attr('usetype') || 0; self.countDown(); $.ajax({ url: '/sendVerifyCode', @@ -348,7 +350,7 @@ dataType: 'json', data: { phone_number: phoneNumber, - mode: 0, // 0注册,1登录 ,2修改手机号,3找回密码 + mode: useType, // 0注册,1登录 ,2修改手机号,3找回密码 slide_id: self.imgID, }, success: function(res) { diff --git a/templates/user/auth/signin_navbar.tmpl b/templates/user/auth/signin_navbar.tmpl index e4ad2bd9c..f26bcb338 100755 --- a/templates/user/auth/signin_navbar.tmpl +++ b/templates/user/auth/signin_navbar.tmpl @@ -1,10 +1,16 @@ {{if or .EnableOpenIDSignIn .EnableSSPI .EnableCloudBrain}}
- +
+ {{if .EnablePhone }} +
+ +
+ {{template "user/auth/phone_verify" .}} +
+
+ {{end}}
From 5d05a6fe7654931f32241b674eb3c68ce99d5b87 Mon Sep 17 00:00:00 2001 From: ychao_1983 Date: Tue, 21 Jun 2022 11:50:27 +0800 Subject: [PATCH 15/45] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- routers/home.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/routers/home.go b/routers/home.go index 38acffb2f..3d804667e 100755 --- a/routers/home.go +++ b/routers/home.go @@ -117,6 +117,10 @@ func Dashboard(ctx *context.Context) { ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/change_password" ctx.SetCookie("redirect_to", setting.AppSubURL+ctx.Req.URL.RequestURI(), 0, setting.AppSubURL) ctx.Redirect(setting.AppSubURL + "/user/settings/change_password") + }else if ctx.User.IsActive && ctx.User.PhoneNumber == "" { + ctx.Data["Title"] = ctx.Tr("phone.bind_phone") + ctx.HTML(200, "user/auth/bind_phone") + return } else { user.Dashboard(ctx) } From dee2c4b17e18cba67315a62bc12ac24097767675 Mon Sep 17 00:00:00 2001 From: chenshihai Date: Tue, 21 Jun 2022 16:16:51 +0800 Subject: [PATCH 16/45] =?UTF-8?q?=E6=89=8B=E6=9C=BA=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- templates/user/auth/bind_phone.tmpl | 80 +++++++++++- templates/user/auth/forgot_passwd.tmpl | 10 ++ templates/user/auth/forgot_passwd_phone.tmpl | 47 +++++++ templates/user/auth/phone_verify.tmpl | 130 +++++++++++++++++-- templates/user/auth/signin_phone.tmpl | 4 +- templates/user/settings/profile.tmpl | 12 +- 6 files changed, 264 insertions(+), 19 deletions(-) create mode 100644 templates/user/auth/forgot_passwd_phone.tmpl diff --git a/templates/user/auth/bind_phone.tmpl b/templates/user/auth/bind_phone.tmpl index 8b1378917..1e1493442 100644 --- a/templates/user/auth/bind_phone.tmpl +++ b/templates/user/auth/bind_phone.tmpl @@ -1 +1,79 @@ - +{{template "base/head" .}} +
+
+
+
+ {{.CsrfTokenHtml}} +

+ {{/*.i18n.Tr "auth.forgot_password_title"*/}} + 请绑定手机号 +

+
+ {{template "base/alert" .}} + + {{if .EnablePhone }} +
+
+ {{template "user/auth/phone_verify" .}} +
+
+ + {{end}} +
+
+ + +
+
+
+ +
+
+
+{{template "base/footer" .}} diff --git a/templates/user/auth/forgot_passwd.tmpl b/templates/user/auth/forgot_passwd.tmpl index 1e061763a..773ec94c7 100644 --- a/templates/user/auth/forgot_passwd.tmpl +++ b/templates/user/auth/forgot_passwd.tmpl @@ -1,4 +1,14 @@ {{template "base/head" .}} +
diff --git a/templates/user/auth/forgot_passwd_phone.tmpl b/templates/user/auth/forgot_passwd_phone.tmpl new file mode 100644 index 000000000..ca98b59e4 --- /dev/null +++ b/templates/user/auth/forgot_passwd_phone.tmpl @@ -0,0 +1,47 @@ +{{template "base/head" .}} + +
+
+
+
+ {{.CsrfTokenHtml}} +

+ {{.i18n.Tr "auth.forgot_password_title"}} +

+
+ {{template "base/alert" .}} + + {{if .EnablePhone }} +
+
+ {{template "user/auth/phone_verify" .}} +
+
+ + {{end}} + +
+
+ + +
+ +
+
+
+
+
+{{template "base/footer" .}} diff --git a/templates/user/auth/phone_verify.tmpl b/templates/user/auth/phone_verify.tmpl index 84d053a1e..935940961 100644 --- a/templates/user/auth/phone_verify.tmpl +++ b/templates/user/auth/phone_verify.tmpl @@ -1,7 +1,30 @@
+
+ 手机号码 +
-
-
-
向右拖动滑块填充拼图
-
-
-
-
-
+
+
+
+
+
向右拖动滑块填充拼图
+
+
+
+
+
+
+
+
+ 手机验证码 +
@@ -217,9 +286,28 @@
获取验证码
+
+
+ 新的登录密码 +
+
+ +
+
+
+
+
+
+ + +
+
+
+ diff --git a/templates/user/auth/signin_phone.tmpl b/templates/user/auth/signin_phone.tmpl index 35c00c6f9..6821c9049 100644 --- a/templates/user/auth/signin_phone.tmpl +++ b/templates/user/auth/signin_phone.tmpl @@ -23,9 +23,9 @@
{{.CsrfTokenHtml}} - + {{if .EnablePhone }} -
+
{{template "user/auth/phone_verify" .}}
{{end}} diff --git a/templates/user/settings/profile.tmpl b/templates/user/settings/profile.tmpl index eb0d271ce..f159ca1ca 100644 --- a/templates/user/settings/profile.tmpl +++ b/templates/user/settings/profile.tmpl @@ -33,13 +33,13 @@
- {{if .EnablePhone }} -
- -
- {{template "user/auth/phone_verify" .}} -
+ {{if .EnablePhone }} +
+ +
+ {{template "user/auth/phone_verify" .}}
+
{{end}}
From 2f6f2dcf5fdec77f5442d9c4f252eebef915274e Mon Sep 17 00:00:00 2001 From: ychao_1983 Date: Tue, 21 Jun 2022 16:32:16 +0800 Subject: [PATCH 17/45] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/context/auth.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/context/auth.go b/modules/context/auth.go index e296535d4..9e84bc35b 100755 --- a/modules/context/auth.go +++ b/modules/context/auth.go @@ -53,7 +53,7 @@ func Toggle(options *ToggleOptions) macaron.Handler { ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") ctx.HTML(200, "user/auth/prohibit_login") return - } else if ctx.User.IsActive && ctx.User.PhoneNumber == "" { + } else if ctx.User.IsActive && ctx.User.PhoneNumber == "" && ctx.Req.URL.Path != "/bindPhone" { ctx.Data["Title"] = ctx.Tr("phone.bind_phone") ctx.HTML(200, "user/auth/bind_phone") return From a5acac468da1d29a90af6bd82e8502b8bfa7ed69 Mon Sep 17 00:00:00 2001 From: ychao_1983 Date: Tue, 21 Jun 2022 17:10:01 +0800 Subject: [PATCH 18/45] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- routers/user/auth.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/routers/user/auth.go b/routers/user/auth.go index 8e6ca9f42..adb78dfb4 100755 --- a/routers/user/auth.go +++ b/routers/user/auth.go @@ -1805,13 +1805,13 @@ func SendVerifyCode(ctx *context.Context, slideImage *slideimage.SlideImage, for return } - if form.Mode==0 || form.Mode==3{ //注册或绑定 + if form.Mode==0 { //注册 if has { ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.already_register"))) return } - } else { //手机号验证码登录 mode=1 + } else { //手机号验证码登录 mode=1 忘记密码 mode=3 if !has { ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.not_register"))) return @@ -1820,7 +1820,7 @@ func SendVerifyCode(ctx *context.Context, slideImage *slideimage.SlideImage, for } } else { - //修改手机号 mode=2 + //修改手机号 mode=2 绑定手机 u, err := models.GetUserByPhoneNumber(phoneNumber) if err != nil && !models.IsErrUserNotExist(err) { log.Warn("sql err", err) From 3fab05c1f440aac303f8fba2e674ba864719e57d Mon Sep 17 00:00:00 2001 From: chenshihai Date: Tue, 21 Jun 2022 19:24:21 +0800 Subject: [PATCH 19/45] =?UTF-8?q?=E6=89=8B=E6=9C=BA=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- options/locale/locale_en-US.ini | 17 ++++++++ options/locale/locale_zh-CN.ini | 20 +++++++-- templates/user/auth/bind_phone.tmpl | 10 ++--- templates/user/auth/forgot_passwd_phone.tmpl | 12 ++--- templates/user/auth/phone_verify.tmpl | 46 ++++++++++++-------- templates/user/auth/signin_navbar.tmpl | 9 ++-- templates/user/settings/profile.tmpl | 4 +- 7 files changed, 77 insertions(+), 41 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index c0dfd456d..b1e6852db 100755 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -389,6 +389,23 @@ manual_first=Please slide to finish the jigsaw first. verify_code_fail=Please input right verify code. bind_phone=Please Bind Your Phone. bind_phone_fail=Fail to bind phone number, please try again later. +phone_number=Phone number +drag_the_slider_to_fill_the_puzzle=Drag the slider to the right to fill the puzzle +mobile_phone_verification_code=Phone verification code +please_enter_SMS_verification_code=Please enter SMS verification code +get_verification_code=Get verification code +new_login_password=New login password +please_enter_new_password=Please enter new password +second_resend=S resend +please_bind_your_mobile_number=Please Bind Your Phone Number +submit=Submit +please_enter_the_correct_mobile_number=Please enter the correct phone number +please_enter_the_correct_mobile_phone_verification_code=Please enter the correct phone verification code +email_retrieve_password=Email retrieve password +mobile_number_retrieve_password=Phone number retrieve password +mobile_login=Mobile login +account_password_login=Account password login +cloud_brain_user_login=Cloud brain user login [mail] diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index ed513ca65..7de657ea9 100755 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -393,9 +393,23 @@ manual_first=请先拖动滑块填充拼图。 verify_code_fail=请输入正确的短信验证码。 bind_phone=请绑定手机号。 bind_phone_fail=绑定手机号失败,请稍后再试。 - - - +phone_number=手机号码 +drag_the_slider_to_fill_the_puzzle=向右拖动滑块填充拼图 +mobile_phone_verification_code=手机验证码 +please_enter_SMS_verification_code=请输入短信验证码 +get_verification_code=获取验证码 +new_login_password=新的登录密码 +please_enter_new_password=请输入新的密码 +second_resend=S后重发 +please_bind_your_mobile_number=请绑定手机号 +submit=提交 +please_enter_the_correct_mobile_number=请输入正确的手机号 +please_enter_the_correct_mobile_phone_verification_code=请输入正确格式的手机验证码 +email_retrieve_password=邮箱找回密码 +mobile_number_retrieve_password=手机号找回密码 +mobile_login=手机登录 +account_password_login=账号密码登录 +cloud_brain_user_login=云脑1用户登录 [mail] diff --git a/templates/user/auth/bind_phone.tmpl b/templates/user/auth/bind_phone.tmpl index 1e1493442..4ddaa97b3 100644 --- a/templates/user/auth/bind_phone.tmpl +++ b/templates/user/auth/bind_phone.tmpl @@ -5,8 +5,7 @@ {{.CsrfTokenHtml}}

- {{/*.i18n.Tr "auth.forgot_password_title"*/}} - 请绑定手机号 + {{.i18n.Tr "phone.please_bind_your_mobile_number"}}

{{template "base/alert" .}} @@ -42,11 +41,11 @@ if (phoneNumber && verifyCode) { e.preventDefault(); if (!/^1[3578]\d{9}$/.test(phoneNumber)) { - bindPhoneEl.find('.ui.negative.message').show().find('p').text('请输入正确的手机号'); + bindPhoneEl.find('.ui.negative.message').show().find('p').text({{.i18n.Tr "phone.please_enter_the_correct_mobile_number"}}); return; } if (!/^\d{6}$/.test(verifyCode)) { - bindPhoneEl.find('.ui.negative.message').show().find('p').text('请输入正确格式的手机验证码'); + bindPhoneEl.find('.ui.negative.message').show().find('p').text({{.i18n.Tr "phone.please_enter_the_correct_mobile_phone_verification_code"}}); return; } $.ajax({ @@ -54,8 +53,9 @@ type: 'post', dataType: 'json', data: { + _csrf: bindPhoneEl.find('input[name="_csrf"]').val(), phone_number: phoneNumber, - verify_code: phoneNumber + verify_code: verifyCode }, success: function(res) { if (res && res.Code === 0) { diff --git a/templates/user/auth/forgot_passwd_phone.tmpl b/templates/user/auth/forgot_passwd_phone.tmpl index ca98b59e4..3dc4fba74 100644 --- a/templates/user/auth/forgot_passwd_phone.tmpl +++ b/templates/user/auth/forgot_passwd_phone.tmpl @@ -1,12 +1,10 @@ {{template "base/head" .}}
@@ -19,13 +17,12 @@
{{template "base/alert" .}} - {{if .EnablePhone }}
{{template "user/auth/phone_verify" .}}
-
+
+
- 手机号码 + {{.i18n.Tr "phone.phone_number"}}
- +
-
向右拖动滑块填充拼图
+
{{.i18n.Tr "phone.drag_the_slider_to_fill_the_puzzle"}}
@@ -274,24 +275,23 @@
-
- 手机验证码 + {{.i18n.Tr "phone.mobile_phone_verification_code"}}
- +
-
获取验证码
+
{{.i18n.Tr "phone.get_verification_code"}}
- 新的登录密码 + {{.i18n.Tr "phone.new_login_password"}}
- +
@@ -310,7 +310,7 @@ window.addEventListener('load', function () { function PhoneVerifyCode(dom) { if (!dom) return; - this.countDownNumber = 20; + this.countDownNumber = 120; this.init(dom); } @@ -345,6 +345,10 @@ if (autofocus) { this.dom.find('input.phoneNumber').focus(); } + var verifyCodeNoRequired = wrap.attr('verifycodenorequired'); + if (verifyCodeNoRequired) { + this.dom.find('input.verifyCode').removeAttr('required'); + } }; PhoneVerifyCode.prototype.eventInit = function () { @@ -452,7 +456,6 @@ } else { self.dom.find('.phone-num-c').removeClass('error'); var useType = self.dom.closest('div.use-type').attr('usetype') || 0; - self.countDown(); $.ajax({ url: '/sendVerifyCode', type: 'post', @@ -463,8 +466,17 @@ slide_id: self.imgID, }, success: function(res) { - if (res && res.code === 0) { - console.log(res); + if (res && res.Code === 0) { + self.countDown(); + } else { + $('body').toast({ + message: res.Message, + showProgress: 'bottom', + showIcon:'warning circle', + class: 'warning', + position: 'top right', + }); + self.refreshImages(); } }, error: function(err) { @@ -507,14 +519,14 @@ var self = this; var sendBtnEl = this.dom.find('.verify-code-send-btn'); var count = this.countDownNumber; - sendBtnEl.addClass('__disabled').text(`${count}S后重发`); + sendBtnEl.addClass('__disabled').text(`${count}{{.i18n.Tr "phone.second_resend"}}`); this.canSendCode = false; this.countDownEnd = false; var timer = setInterval(function () { count--; - sendBtnEl.addClass('__disabled').text(`${count}S后重发`); + sendBtnEl.addClass('__disabled').text(`${count}{{.i18n.Tr "phone.second_resend"}}`); if (count <= 0) { - sendBtnEl.removeClass('__disabled').text(`获取验证码`); + sendBtnEl.removeClass('__disabled').text({{.i18n.Tr "phone.get_verification_code"}}); clearInterval(timer); self.canSendCode = true; self.countDownEnd = true; diff --git a/templates/user/auth/signin_navbar.tmpl b/templates/user/auth/signin_navbar.tmpl index f26bcb338..45475e46f 100755 --- a/templates/user/auth/signin_navbar.tmpl +++ b/templates/user/auth/signin_navbar.tmpl @@ -1,16 +1,13 @@ {{if or .EnableOpenIDSignIn .EnableSSPI .EnableCloudBrain}} {{if .EnablePhone }}
- -
+ +
{{template "user/auth/phone_verify" .}}
From 70bf85df61a08c26fdefe901fcff4b67040d5f37 Mon Sep 17 00:00:00 2001 From: ychao_1983 Date: Wed, 22 Jun 2022 09:56:00 +0800 Subject: [PATCH 20/45] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- routers/user/auth.go | 1 + 1 file changed, 1 insertion(+) diff --git a/routers/user/auth.go b/routers/user/auth.go index adb78dfb4..8390d8e41 100755 --- a/routers/user/auth.go +++ b/routers/user/auth.go @@ -205,6 +205,7 @@ func SignInPhonePost(ctx *context.Context, form auth.PhoneNumberCodeForm) { ctx.Data["Title"] = ctx.Tr("sign_in") ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login" ctx.Data["PageIsSignIn"] = true + ctx.Data["PageIsPhoneLogin"] = true ctx.Data["PageIsLogin"] = true ctx.Data["EnablePhone"] = setting.PhoneService.Enabled ctx.Data["Title"] = ctx.Tr("sign_in") From 0a190ca543150411f49967da6edab4eea7c737c0 Mon Sep 17 00:00:00 2001 From: ychao_1983 Date: Wed, 22 Jun 2022 10:03:41 +0800 Subject: [PATCH 21/45] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- routers/user/auth.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/routers/user/auth.go b/routers/user/auth.go index 8390d8e41..a6d95507c 100755 --- a/routers/user/auth.go +++ b/routers/user/auth.go @@ -194,8 +194,6 @@ func SignInPhone(ctx *context.Context) { return } - ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login" - ctx.Data["PageIsSignIn"] = true ctx.Data["PageIsPhoneLogin"] = true ctx.HTML(200, tplSignInPhone) @@ -203,12 +201,7 @@ func SignInPhone(ctx *context.Context) { func SignInPhonePost(ctx *context.Context, form auth.PhoneNumberCodeForm) { ctx.Data["Title"] = ctx.Tr("sign_in") - ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login" - ctx.Data["PageIsSignIn"] = true ctx.Data["PageIsPhoneLogin"] = true - ctx.Data["PageIsLogin"] = true - ctx.Data["EnablePhone"] = setting.PhoneService.Enabled - ctx.Data["Title"] = ctx.Tr("sign_in") ctx.Data["IsCourse"] = ctx.QueryBool("course") if ctx.HasError() { From 3d1c188614cb3a2b967d811eb0d4b49a89da0314 Mon Sep 17 00:00:00 2001 From: chenshihai Date: Wed, 22 Jun 2022 10:08:59 +0800 Subject: [PATCH 22/45] =?UTF-8?q?=E6=89=8B=E6=9C=BA=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- options/locale/locale_en-US.ini | 1 + options/locale/locale_zh-CN.ini | 1 + templates/user/auth/phone_verify.tmpl | 98 ++++++++++++++++----------- templates/user/auth/signin_inner.tmpl | 4 +- 4 files changed, 63 insertions(+), 41 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index b1e6852db..46c5d1453 100755 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -197,6 +197,7 @@ no_reply_address_helper = Domain name for users with a hidden email address. For [home] uname_holder = Username or Email Address +login_uname_holder=Username/Email/Phone number uname_holder_cloud_brain = cloudbrain username password_holder = Password switch_dashboard_context = Switch Dashboard Context diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 7de657ea9..8ad520798 100755 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -198,6 +198,7 @@ no_reply_address_helper=具有隐藏电子邮件地址的用户的域名。例 [home] uname_holder=登录名或电子邮箱地址 +login_uname_holder=用户名/邮箱/手机号 uname_holder_cloud_brain=云脑登录名 password_holder=密码 switch_dashboard_context=切换控制面板用户 diff --git a/templates/user/auth/phone_verify.tmpl b/templates/user/auth/phone_verify.tmpl index c679334f7..2bf774e43 100644 --- a/templates/user/auth/phone_verify.tmpl +++ b/templates/user/auth/phone_verify.tmpl @@ -1,9 +1,9 @@ -
+
{{.i18n.Tr "phone.phone_number"}} @@ -268,7 +278,11 @@
{{.i18n.Tr "phone.drag_the_slider_to_fill_the_puzzle"}}
-
+
+ + + +
@@ -414,11 +428,15 @@ self.canSendCode = true; self.dom.find('.slide-bar').addClass('sucess'); self.dom.find('.slide-trigger').addClass('sucess'); + self.dom.find('.slide-trigger .icon').hide(); + self.dom.find('.slide-trigger .icon.check').show(); self.dom.find('.slide-image-big').slideUp(); self.dom.find('.verify-code-send-btn').removeClass('__disabled'); } else { self.dom.find('.slide-bar').addClass('error'); self.dom.find('.slide-trigger').addClass('error'); + self.dom.find('.slide-trigger .icon').hide(); + self.dom.find('.slide-trigger .icon.close').show(); setTimeout(function () { self.refreshImages(); }, 300); @@ -495,6 +513,8 @@ this.imgID = ''; this.dom.find('.slide-bar').removeClass('sucess error').css('width', '30px'); this.dom.find('.slide-trigger').removeClass('sucess error').css('left', '0px'); + this.dom.find('.slide-trigger .icon').hide(); + this.dom.find('.slide-trigger .icon.arrow').show(); this.dom.find('.slide-txt').show(); this.dom.find('.slide-image-small').css('left', '0'); this.dom.find('.verify-code-send-btn').addClass('__disabled'); @@ -519,12 +539,12 @@ var self = this; var sendBtnEl = this.dom.find('.verify-code-send-btn'); var count = this.countDownNumber; - sendBtnEl.addClass('__disabled').text(`${count}{{.i18n.Tr "phone.second_resend"}}`); + sendBtnEl.addClass('__disabled').text(count + {{.i18n.Tr "phone.second_resend"}}); this.canSendCode = false; this.countDownEnd = false; var timer = setInterval(function () { count--; - sendBtnEl.addClass('__disabled').text(`${count}{{.i18n.Tr "phone.second_resend"}}`); + sendBtnEl.addClass('__disabled').text(count + {{.i18n.Tr "phone.second_resend"}}); if (count <= 0) { sendBtnEl.removeClass('__disabled').text({{.i18n.Tr "phone.get_verification_code"}}); clearInterval(timer); @@ -534,7 +554,7 @@ }, 1000); }; - const phoneVerifyCode = new PhoneVerifyCode($('.phone-verify-code')); + const phoneVerifyCode = new PhoneVerifyCode($('.__phone-verify-code')); }); })(); diff --git a/templates/user/auth/signin_inner.tmpl b/templates/user/auth/signin_inner.tmpl index bdcd6a5c6..32ce80dc5 100644 --- a/templates/user/auth/signin_inner.tmpl +++ b/templates/user/auth/signin_inner.tmpl @@ -28,7 +28,7 @@
-
+
{{if .IsCourse}} {{else}} @@ -38,7 +38,7 @@
- +
{{if or (not .DisablePassword) .LinkAccountMode}} From 406614f0fca408e1e68c529bf876cc81a85d13e5 Mon Sep 17 00:00:00 2001 From: ychao_1983 Date: Wed, 22 Jun 2022 10:25:10 +0800 Subject: [PATCH 23/45] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- routers/user/auth.go | 1 + 1 file changed, 1 insertion(+) diff --git a/routers/user/auth.go b/routers/user/auth.go index a6d95507c..a23afd4c4 100755 --- a/routers/user/auth.go +++ b/routers/user/auth.go @@ -211,6 +211,7 @@ func SignInPhonePost(ctx *context.Context, form auth.PhoneNumberCodeForm) { if !phoneService.IsVerifyCodeRight(strings.TrimSpace(form.PhoneNumber), strings.TrimSpace(form.VerifyCode)) { ctx.RenderWithErr(ctx.Tr("phone.verify_code_fail"), tplSignInPhone, &form) + return } u, err := models.GetUserByPhoneNumber(strings.TrimSpace(form.PhoneNumber)) From 017b10c6e3918e220c7f43fa98c9c769f6e1b1f8 Mon Sep 17 00:00:00 2001 From: chenshihai Date: Wed, 22 Jun 2022 11:54:58 +0800 Subject: [PATCH 24/45] =?UTF-8?q?=E6=89=8B=E6=9C=BA=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- options/locale/locale_en-US.ini | 1 + options/locale/locale_zh-CN.ini | 1 + templates/user/auth/forgot_passwd.tmpl | 8 ++-- templates/user/auth/phone_verify.tmpl | 66 ++++++++++++++++++++------ templates/user/auth/signin_navbar.tmpl | 2 + templates/user/settings/profile.tmpl | 9 +++- 6 files changed, 67 insertions(+), 20 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 46c5d1453..b17134f6e 100755 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -407,6 +407,7 @@ mobile_number_retrieve_password=Phone number retrieve password mobile_login=Mobile login account_password_login=Account password login cloud_brain_user_login=Cloud brain user login +modify_phone_number=Modify phone number [mail] diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 8ad520798..01916cd9a 100755 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -411,6 +411,7 @@ mobile_number_retrieve_password=手机号找回密码 mobile_login=手机登录 account_password_login=账号密码登录 cloud_brain_user_login=云脑1用户登录 +modify_phone_number=修改手机号 [mail] diff --git a/templates/user/auth/forgot_passwd.tmpl b/templates/user/auth/forgot_passwd.tmpl index 773ec94c7..550d36fbf 100644 --- a/templates/user/auth/forgot_passwd.tmpl +++ b/templates/user/auth/forgot_passwd.tmpl @@ -1,13 +1,13 @@ {{template "base/head" .}}
diff --git a/templates/user/auth/phone_verify.tmpl b/templates/user/auth/phone_verify.tmpl index 2bf774e43..9f4d6429b 100644 --- a/templates/user/auth/phone_verify.tmpl +++ b/templates/user/auth/phone_verify.tmpl @@ -52,6 +52,7 @@ justify-content: center; align-items: center; box-sizing: border-box; + position: relative; } .__phone-verify-code .phone-c .phone-num-c input { @@ -62,6 +63,21 @@ outline: none; } + .__phone-verify-code .phone-c .modify-phone-number { + position: absolute; + left: 100%; + width: 200px; + height: 100%; + margin-left: 10px; + display: none; + } + + .__phone-verify-code .phone-c .modify-phone-number a { + font-weight: 400; + font-size: 14px; + color: rgba(0, 102, 255, 1); + } + .__phone-verify-code .slide-bar-wrap { display: flex; height: 38px; @@ -141,7 +157,7 @@ margin: 0; height: 16px; width: 16px; - margin-top: -6px; + margin-top: -5px; } .__phone-verify-code .slide-bar-c .slide-bar-bg .slide-trigger.sucess { @@ -253,7 +269,6 @@ width: 100% !important; padding: 9.5px 14px; box-sizing: border-box; - border: none; outline: none; } @@ -270,9 +285,10 @@
-
+
@@ -339,6 +355,14 @@ this.eventInit(); this.refreshImages(); var wrap = this.dom.closest('div.use-type'); + var readonly = wrap.attr('readonly'); + if (readonly) { + this.dom.find('.phone-area-c select').attr('disabled', true); + this.dom.find('.phone-num-c input').attr('disabled', true); + this.dom.find('.slide-bar-wrap').hide(); + this.dom.find('.verify-code-c').hide(); + this.dom.find('.modify-phone-number').show(); + } var oldPhoneNum = wrap.attr('ophonenumber'); if (oldPhoneNum) { this.dom.find('input.phoneNumber').val(oldPhoneNum); @@ -362,7 +386,7 @@ var verifyCodeNoRequired = wrap.attr('verifycodenorequired'); if (verifyCodeNoRequired) { this.dom.find('input.verifyCode').removeAttr('required'); - } + } }; PhoneVerifyCode.prototype.eventInit = function () { @@ -444,10 +468,10 @@ }, error: function(err) { self.dom.find('.slide-bar').addClass('error'); - self.dom.find('.slide-trigger').addClass('error'); - setTimeout(function () { - self.refreshImages(); - }, 300); + self.dom.find('.slide-trigger').addClass('error'); + setTimeout(function () { + self.refreshImages(); + }, 300); } }); } @@ -487,13 +511,17 @@ if (res && res.Code === 0) { self.countDown(); } else { - $('body').toast({ - message: res.Message, - showProgress: 'bottom', - showIcon:'warning circle', - class: 'warning', - position: 'top right', - }); + if ($('.ui.negative.message').length) { + $('.ui.negative.message').eq(0).show().find('p').text(res.Message); + } else { + $('body').toast({ + message: res.Message, + showProgress: 'bottom', + showIcon:'warning circle', + class: 'warning', + position: 'top right', + }); + } self.refreshImages(); } }, @@ -503,6 +531,14 @@ }); } }); + + this.dom.find('.modify-phone-number a').on('click', function() { + self.dom.find('.phone-area-c select').attr('disabled', false); + self.dom.find('.phone-num-c input').attr('disabled', false); + self.dom.find('.slide-bar-wrap').css('display', 'flex'); + self.dom.find('.verify-code-c').css('display', 'flex'); + self.dom.find('.modify-phone-number').hide(); + }); }; PhoneVerifyCode.prototype.refreshImages = function () { diff --git a/templates/user/auth/signin_navbar.tmpl b/templates/user/auth/signin_navbar.tmpl index 45475e46f..f53f6a820 100755 --- a/templates/user/auth/signin_navbar.tmpl +++ b/templates/user/auth/signin_navbar.tmpl @@ -1,8 +1,10 @@ {{if or .EnableOpenIDSignIn .EnableSSPI .EnableCloudBrain}}

9ohNM@@|v(BN9VW0%@$#cID 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 literal 0 HcmV?d00001 diff --git a/public/img/slide/mask/mask.png b/public/img/slide/mask/mask.png new file mode 100644 index 0000000000000000000000000000000000000000..562f75b05c9c7bbe39ee31baafe36f7c6129c126 GIT binary patch literal 199 zcmeAS@N?(olHy`uVBq!ia0vp^#vshW1|+Q(8fXD2&H|6fVg?3oArNM~bhqvgP_WX| z#W5tK@$D@`-UbH|mW!d8)tP^KSg6mR@PMJ~p^^VVzx~0XvYy-i+}~-V_?yA7 zmaXSUk73BM07>Umo~gt$OheB6Wy$djJ20($QXQktWzLh5Hv>(9j$rU~^>bP0l+XkK Dq^myV literal 0 HcmV?d00001 From 4f1ecf217b1a1247d76f2f33243ac5c6b67991af Mon Sep 17 00:00:00 2001 From: ychao_1983 Date: Mon, 6 Jun 2022 11:25:13 +0800 Subject: [PATCH 04/45] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- options/locale/locale_en-US.ini | 2 +- options/locale/locale_zh-CN.ini | 6 +++--- routers/user/setting/profile.go | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 52878cd7f..2bbca4f9e 100755 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -384,7 +384,7 @@ max_times=One phone number can not send verify code more than %s times. too_fast=Send too frequently, please try again later. manual_first=Please slide to finish the jigsaw first. verify_code_fail=Please input right verify code. -bind_phone=Please Bind Your Phone +bind_phone=Please Bind Your Phone. bind_phone_fail=Fail to bind phone number, please try again later. diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 875c1d5ba..f22f6dfb1 100755 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -381,9 +381,9 @@ sspi_auth_failed=SSPI 认证失败 [phone] format_err=手机号格式错误。 query_err=查询手机号失败,无法发送,请稍后再试。 -already_register=手机号已被注册 -not_register=手机号未注册 -not_modify=手机号未修改 +already_register=手机号已被注册。 +not_register=手机号未注册。 +not_modify=手机号未修改。 max_times=一个手机号发送验证码次数每天不能超过%s次。 too_fast=验证码发送太频繁,请稍后再试。 manual_first=请先拖动滑块填充拼图。 diff --git a/routers/user/setting/profile.go b/routers/user/setting/profile.go index aade07f2f..de1ba3144 100755 --- a/routers/user/setting/profile.go +++ b/routers/user/setting/profile.go @@ -91,7 +91,7 @@ func ProfilePost(ctx *context.Context, form auth.UpdateProfileForm) { return } - if setting.PhoneService.Enabled { + if setting.PhoneService.Enabled && strings.TrimSpace(form.PhoneNumber) != "" { if strings.TrimSpace(form.PhoneNumber) != ctx.User.PhoneNumber { if phoneService.IsVerifyCodeRight(strings.TrimSpace(form.PhoneNumber), strings.TrimSpace(form.VerifyCode)) { ctx.User.PhoneNumber = strings.TrimSpace(form.PhoneNumber) From 5d8662e674e7eaa04b347d33439616360aad57ea Mon Sep 17 00:00:00 2001 From: ychao_1983 Date: Mon, 6 Jun 2022 15:48:57 +0800 Subject: [PATCH 05/45] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- options/locale/locale_en-US.ini | 6 +++--- options/locale/locale_zh-CN.ini | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 2bbca4f9e..70d76d407 100755 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -377,10 +377,10 @@ sspi_auth_failed = SSPI authentication failed [phone] format_err=The format of phone number is wrong. query_err=Fail to query phone number, can not send verify code, please try again later. -already_register=The phone number is already registered. -not_register=The phone number is not registered. +already_register=The phone number is already used. +not_register=The phone number is wrong. not_modify=The phone number is not updated. -max_times=One phone number can not send verify code more than %s times. +max_times=One phone number can not send verify code more than %s times a day. too_fast=Send too frequently, please try again later. manual_first=Please slide to finish the jigsaw first. verify_code_fail=Please input right verify code. diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index f22f6dfb1..b66cdbb21 100755 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -381,14 +381,14 @@ sspi_auth_failed=SSPI 认证失败 [phone] format_err=手机号格式错误。 query_err=查询手机号失败,无法发送,请稍后再试。 -already_register=手机号已被注册。 -not_register=手机号未注册。 +already_register=手机号已被使用。 +not_register=手机号输入错误。 not_modify=手机号未修改。 -max_times=一个手机号发送验证码次数每天不能超过%s次。 +max_times=一个手机号每天发送验证码次数不能超过%s次。 too_fast=验证码发送太频繁,请稍后再试。 manual_first=请先拖动滑块填充拼图。 verify_code_fail=请输入正确的短信验证码。 -bind_phone=请绑定手机号 +bind_phone=请绑定手机号。 bind_phone_fail=绑定手机号失败,请稍后再试。 From 01901a3e2831109817ddd2c960a9175e6c93dae9 Mon Sep 17 00:00:00 2001 From: ychao_1983 Date: Tue, 14 Jun 2022 09:09:16 +0800 Subject: [PATCH 06/45] =?UTF-8?q?=E5=BF=98=E8=AE=B0=E5=AF=86=E7=A0=81?= =?UTF-8?q?=E9=82=AE=E7=AE=B1=E6=A3=80=E6=9F=A5=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- models/user.go | 20 ++++++++++++++++++++ options/locale/locale_en-US.ini | 2 ++ options/locale/locale_zh-CN.ini | 2 ++ routers/user/auth.go | 10 +++++++--- 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/models/user.go b/models/user.go index 5e7ddad86..ea23938bb 100755 --- a/models/user.go +++ b/models/user.go @@ -1625,6 +1625,26 @@ func GetUserByEmail(email string) (*User, error) { return GetUserByEmailContext(DefaultDBContext(), email) } +func GetUserByMainEmail(email string) (*User, error) { + if len(email) == 0 { + return nil, ErrUserNotExist{0, email, 0} + } + + email = strings.ToLower(email) + // First try to find the user by primary email + user := &User{Email: email} + has, err := DefaultDBContext().e.Get(user) + if err != nil { + return nil, err + } + if has { + return user, nil + } else { + return nil, ErrUserNotExist{0, email, 0} + } + +} + // GetUserByEmailContext returns the user object by given e-mail if exists with db context func GetUserByEmailContext(ctx DBContext, email string) (*User, error) { if len(email) == 0 { diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 70d76d407..7cf271c27 100755 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -335,6 +335,8 @@ resent_limit_prompt = You have already requested an activation email recently. P has_unconfirmed_mail = Hi %s, you have an unconfirmed email address (%s). If you haven't received a confirmation email or need to resend a new one, please click on the button below. resend_mail = Click here to resend your activation email email_not_associate = The email address is not associated with any account. +email_not_main=The email address is wrong, please input your primary email address. +email_not_right=The email address is not associated with any account, please input the right email address. send_reset_mail = Send Account Recovery Email reset_password = Account Recovery invalid_code = Your confirmation code is invalid or has expired. diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index b66cdbb21..415b85bfe 100755 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -339,6 +339,8 @@ resent_limit_prompt=您请求发送激活邮件过于频繁,请等待 3 分钟 has_unconfirmed_mail=%s 您好,系统检测到您有一封发送至 %s 但未被确认的邮件。如果您未收到激活邮件,或需要重新发送,请单击下方的按钮。 resend_mail=单击此处重新发送确认邮件 email_not_associate=您输入的邮箱地址未被关联到任何帐号! +email_not_main=电子邮箱地址不正确,请输入您设置的主要邮箱地址。 +email_not_right=您输入了不存在的邮箱地址,请输入正确的邮箱地址。 send_reset_mail=发送账户恢复邮件 reset_password=账户恢复 invalid_code=此确认密钥无效或已过期。 diff --git a/routers/user/auth.go b/routers/user/auth.go index c3af2b8d4..0de15f5f5 100755 --- a/routers/user/auth.go +++ b/routers/user/auth.go @@ -1425,12 +1425,16 @@ func ForgotPasswdPost(ctx *context.Context) { email := ctx.Query("email") ctx.Data["Email"] = email - u, err := models.GetUserByEmail(email) + u, err := models.GetUserByMainEmail(email) if err != nil { if models.IsErrUserNotExist(err) { ctx.Data["ResetPwdCodeLives"] = timeutil.MinutesToFriendly(setting.Service.ResetPwdCodeLives, ctx.Locale.Language()) - ctx.Data["IsResetSent"] = true - ctx.HTML(200, tplForgotPassword) + ctx.Data["IsResetSent"] = false + if used, _ := models.IsEmailUsed(email); used { + ctx.RenderWithErr(ctx.Tr("auth.email_not_main"), tplForgotPassword, nil) + } else { + ctx.RenderWithErr(ctx.Tr("auth.email_not_right"), tplForgotPassword, nil) + } return } From cac37dd21d8547cca4240ab36c3882278c78cf45 Mon Sep 17 00:00:00 2001 From: ychao_1983 Date: Tue, 14 Jun 2022 15:22:14 +0800 Subject: [PATCH 07/45] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/auth/user_form.go | 11 ++++ options/locale/locale_en-US.ini | 3 +- options/locale/locale_zh-CN.ini | 3 +- routers/routes/routes.go | 1 + routers/user/auth.go | 94 +++++++++++++++++++++++++++------ 5 files changed, 94 insertions(+), 18 deletions(-) diff --git a/modules/auth/user_form.go b/modules/auth/user_form.go index 23e3e5b71..b7c2c5ff3 100755 --- a/modules/auth/user_form.go +++ b/modules/auth/user_form.go @@ -389,6 +389,17 @@ func (f *PhoneNumberCodeForm) Validate(ctx *macaron.Context, errs binding.Errors return validate(errs, ctx.Data, f, ctx.Locale) } +type ResetPassWordByPhoneForm struct { + PhoneNumber string `binding:"Required;MaxSize(20)"` + VerifyCode string `binding:"Required;MaxSize(10)"` + Password string `binding:"MaxSize(255)"` + Remember bool +} + +func (f *ResetPassWordByPhoneForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { + return validate(errs, ctx.Data, f, ctx.Locale) +} + type SlideImageForm struct { SlideID string `binding:"Required"` X int `binding:"Required"` diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 7cf271c27..c0dfd456d 100755 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -342,6 +342,7 @@ reset_password = Account Recovery invalid_code = Your confirmation code is invalid or has expired. reset_password_helper = Recover Account reset_password_wrong_user = You are signed in as %s, but the account recovery link is for %s +reset_password_wrong_user_phone=You are signed in, but the phone number is used by other user. password_too_short = Password length cannot be less than %d characters. non_local_account = Non-local users can not update their password through the openi web interface. verify = Verify @@ -378,7 +379,7 @@ disable_forgot_password_mail = Account recovery is disabled. Please contact your sspi_auth_failed = SSPI authentication failed [phone] format_err=The format of phone number is wrong. -query_err=Fail to query phone number, can not send verify code, please try again later. +query_err=Fail to query phone number, please try again later. already_register=The phone number is already used. not_register=The phone number is wrong. not_modify=The phone number is not updated. diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 415b85bfe..ed513ca65 100755 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -346,6 +346,7 @@ reset_password=账户恢复 invalid_code=此确认密钥无效或已过期。 reset_password_helper=恢复账户 reset_password_wrong_user=您已作为 %s 登录,无法使用链接恢复 %s 的账户。 +reset_password_wrong_user_phone=您已登录,不能用别的账号的手机恢复。 password_too_short=密码长度不能少于 %d 位。 non_local_account=非本地帐户不能通过 openi 的 web 界面更改密码。 verify=验证 @@ -382,7 +383,7 @@ disable_forgot_password_mail = Account recovery is disabled. Please contact your sspi_auth_failed=SSPI 认证失败 [phone] format_err=手机号格式错误。 -query_err=查询手机号失败,无法发送,请稍后再试。 +query_err=查询手机号失败,请稍后再试。 already_register=手机号已被使用。 not_register=手机号输入错误。 not_modify=手机号未修改。 diff --git a/routers/routes/routes.go b/routers/routes/routes.go index a4127314a..eda71785e 100755 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -494,6 +494,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("/email2user", user.Email2User) m.Get("/recover_account", user.ResetPasswd) m.Post("/recover_account", user.ResetPasswdPost) + m.Post("/recover_account_by_phone", user.ResetPasswdByPhonePost) m.Get("/forgot_password", user.ForgotPasswd) m.Post("/forgot_password", user.ForgotPasswdPost) m.Post("/logout", user.SignOut) diff --git a/routers/user/auth.go b/routers/user/auth.go index 0de15f5f5..c0c715c31 100755 --- a/routers/user/auth.go +++ b/routers/user/auth.go @@ -50,13 +50,14 @@ const ( // tplSignUp template path for sign up page tplSignUp base.TplName = "user/auth/signup" // TplActivate template path for activate user - TplActivate base.TplName = "user/auth/activate" - tplForgotPassword base.TplName = "user/auth/forgot_passwd" - tplResetPassword base.TplName = "user/auth/reset_passwd" - tplTwofa base.TplName = "user/auth/twofa" - tplTwofaScratch base.TplName = "user/auth/twofa_scratch" - tplLinkAccount base.TplName = "user/auth/link_account" - tplU2F base.TplName = "user/auth/u2f" + TplActivate base.TplName = "user/auth/activate" + tplForgotPassword base.TplName = "user/auth/forgot_passwd" + tplForgotPasswordPhone base.TplName = "user/auth/forgot_passwd_phone" + tplResetPassword base.TplName = "user/auth/reset_passwd" + tplTwofa base.TplName = "user/auth/twofa" + tplTwofaScratch base.TplName = "user/auth/twofa_scratch" + tplLinkAccount base.TplName = "user/auth/link_account" + tplU2F base.TplName = "user/auth/u2f" ) // AutoSignIn reads cookie and try to auto-login. @@ -1398,18 +1399,30 @@ func ActivateEmail(ctx *context.Context) { // ForgotPasswd render the forget pasword page func ForgotPasswd(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("auth.forgot_password_title") + forgetType := ctx.Query("type") - if setting.MailService == nil { - ctx.Data["IsResetDisable"] = true - ctx.HTML(200, tplForgotPassword) - return - } + if forgetType == "phone" { + if !setting.PhoneService.Enabled { + ctx.Data["IsResetDisable"] = true + ctx.HTML(200, tplForgotPasswordPhone) + return + } + ctx.Data["IsResetRequest"] = true + ctx.HTML(200, tplForgotPasswordPhone) + } else { - email := ctx.Query("email") - ctx.Data["Email"] = email + if setting.MailService == nil { + ctx.Data["IsResetDisable"] = true + ctx.HTML(200, tplForgotPassword) + return + } - ctx.Data["IsResetRequest"] = true - ctx.HTML(200, tplForgotPassword) + email := ctx.Query("email") + ctx.Data["Email"] = email + + ctx.Data["IsResetRequest"] = true + ctx.HTML(200, tplForgotPassword) + } } // ForgotPasswdPost response for forget password request @@ -1622,6 +1635,55 @@ func ResetPasswdPost(ctx *context.Context) { handleSignInFull(ctx, u, remember, true) } +func ResetPasswdByPhonePost(ctx *context.Context, form auth.ResetPassWordByPhoneForm) { + phoneNumber := strings.TrimSpace(form.PhoneNumber) + verifyCode := strings.TrimSpace(form.VerifyCode) + isRight := phoneService.IsVerifyCodeRight(phoneNumber, verifyCode) + if !isRight { + ctx.RenderWithErr(ctx.Tr("phone.verify_code_fail"), tplForgotPasswordPhone, form) + return + } + + passwd := strings.TrimSpace(form.Password) + if len(passwd) < setting.MinPasswordLength { + ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplForgotPasswordPhone, form) + return + } else if !password.IsComplexEnough(passwd) { + ctx.RenderWithErr(password.BuildComplexityError(ctx), tplForgotPasswordPhone, form) + return + } + + u, err := models.GetUserByPhoneNumber(phoneNumber) + if err != nil { + log.Error("fail to query by phone number", err) + ctx.RenderWithErr(ctx.Tr("phone.query_err", setting.MinPasswordLength), tplForgotPasswordPhone, form) + return + } + + if nil != ctx.User && u.ID != ctx.User.ID { + ctx.RenderWithErr(ctx.Tr("auth.reset_password_wrong_user", ctx.User.Email, u.Email), tplForgotPasswordPhone, form) + return + } + + if u.Rands, err = models.GetUserSalt(); err != nil { + ctx.ServerError("UpdateUser", err) + return + } + if u.Salt, err = models.GetUserSalt(); err != nil { + ctx.ServerError("UpdateUser", err) + return + } + u.HashPassword(passwd) + u.MustChangePassword = false + if err := models.UpdateUserCols(u, "must_change_password", "passwd", "rands", "salt"); err != nil { + ctx.ServerError("UpdateUser", err) + return + } + + handleSignInFull(ctx, u, form.Remember, true) + +} + // MustChangePassword renders the page to change a user's password func MustChangePassword(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("auth.must_change_password") From 1f9becdf0b4c25e38adc0fb0815ff40123ce0cb5 Mon Sep 17 00:00:00 2001 From: chenshihai Date: Mon, 20 Jun 2022 14:49:08 +0800 Subject: [PATCH 08/45] =?UTF-8?q?=E6=89=8B=E6=9C=BA=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E7=A0=81=20Signed-off-by:=20chenshihai=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- templates/user/auth/phone_verify.tmpl | 397 ++++++++++++++++++++++++++ templates/user/auth/signup_inner.tmpl | 4 + 2 files changed, 401 insertions(+) create mode 100644 templates/user/auth/phone_verify.tmpl diff --git a/templates/user/auth/phone_verify.tmpl b/templates/user/auth/phone_verify.tmpl new file mode 100644 index 000000000..6898b4d36 --- /dev/null +++ b/templates/user/auth/phone_verify.tmpl @@ -0,0 +1,397 @@ + +