From bdcc5134b101498c84578c73e288e1922565392c Mon Sep 17 00:00:00 2001 From: ychao_1983 Date: Thu, 26 May 2022 17:08:44 +0800 Subject: [PATCH 01/47] 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 ++++++++ vendor/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 +++ .../alibabacloud-go/tea-utils/service/service.go | 462 +++ .../alibabacloud-go/tea-utils/service/util.go | 52 + .../alibabacloud-go/tea-xml/service/service.go | 105 + vendor/github.com/alibabacloud-go/tea/LICENSE | 201 + .../alibabacloud-go/tea/tea/json_parser.go | 333 ++ vendor/github.com/alibabacloud-go/tea/tea/tea.go | 1121 ++++++ vendor/github.com/alibabacloud-go/tea/tea/trans.go | 491 +++ .../github.com/alibabacloud-go/tea/utils/assert.go | 64 + .../github.com/alibabacloud-go/tea/utils/logger.go | 109 + .../alibabacloud-go/tea/utils/progress.go | 60 + vendor/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-go/credentials/ecs_ram_role.go | 136 + .../credentials-go/credentials/env_provider.go | 43 + .../credentials/instance_provider.go | 28 + .../credentials-go/credentials/profile_provider.go | 350 ++ .../aliyun/credentials-go/credentials/provider.go | 13 + .../credentials-go/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-go/credentials/sts_credential.go | 43 + .../credentials/sts_role_arn_credential.go | 163 + .../credentials-go/credentials/utils/runtime.go | 35 + .../credentials-go/credentials/utils/utils.go | 146 + vendor/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 + .../github.com/clbanning/mxj/v2/atomFeedString.xml | 54 + vendor/github.com/clbanning/mxj/v2/doc.go | 138 + vendor/github.com/clbanning/mxj/v2/escapechars.go | 93 + vendor/github.com/clbanning/mxj/v2/exists.go | 9 + vendor/github.com/clbanning/mxj/v2/files.go | 287 ++ .../github.com/clbanning/mxj/v2/files_test.badjson | 2 + .../github.com/clbanning/mxj/v2/files_test.badxml | 9 + vendor/github.com/clbanning/mxj/v2/files_test.json | 2 + vendor/github.com/clbanning/mxj/v2/files_test.xml | 9 + .../clbanning/mxj/v2/files_test_dup.json | 1 + .../github.com/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 ++ vendor/github.com/clbanning/mxj/v2/keyvalues.go | 668 ++++ vendor/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 + vendor/github.com/clbanning/mxj/v2/setfieldsep.go | 20 + vendor/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 + vendor/github.com/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 +- vendor/github.com/json-iterator/go/iter_object.go | 4 +- .../json-iterator/go/reflect_extension.go | 2 +- vendor/github.com/json-iterator/go/reflect_map.go | 80 +- .../json-iterator/go/reflect_optional.go | 4 - .../json-iterator/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 +- .../github.com/yuin/goldmark/parser/atx_heading.go | 2 +- vendor/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/47] =?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 ++ .../github.com/disintegration/imaging/.travis.yml | 12 + vendor/github.com/disintegration/imaging/LICENSE | 21 + vendor/github.com/disintegration/imaging/README.md | 226 +++++ vendor/github.com/disintegration/imaging/adjust.go | 253 ++++++ .../disintegration/imaging/convolution.go | 148 +++ vendor/github.com/disintegration/imaging/doc.go | 7 + .../github.com/disintegration/imaging/effects.go | 169 ++++ vendor/github.com/disintegration/imaging/go.mod | 3 + vendor/github.com/disintegration/imaging/go.sum | 3 + .../github.com/disintegration/imaging/histogram.go | 52 ++ vendor/github.com/disintegration/imaging/io.go | 444 +++++++++ vendor/github.com/disintegration/imaging/resize.go | 595 +++++++++++++ .../github.com/disintegration/imaging/scanner.go | 285 ++++++ vendor/github.com/disintegration/imaging/tools.go | 249 ++++++ .../github.com/disintegration/imaging/transform.go | 268 ++++++ vendor/github.com/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/47] =?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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

H_rHfg1`_@~B{W^c}FJAf! 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�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

*aM%sC+uMWf6AbY?(Uen39XS>q$>D;9RD4 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 literal 0 KcmV+b0RR6000031 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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

B7-KVY?EMAA z6q6c}jOw!;W$gG6&U+*g*=^q>Xm8lW&p;Nw_^iSh6tc;H7d2 zoP@-gSXfnZN?B#F+HYV)WbSGyOU}8GOr?;Bg{urb3_BYVPx-=A%5v=O$?)S@14%{|*l8w&7wKJ%ibJlrh4o5GxadJvI}88|Nuy zy}HFd4(_F!vTNpjO^_sZSpGqqxGgdmE4^$}aU%3_ywuiMI)uoe18vrn=mH^z=q zM8f+qjZ?NnM29jd#Mxz3(9Wi6f{9Ljjr$1TI#9M<{EwHN6WgwJhmW!<_{hHUQMJqB zEGz;_5Ot^G!+Pi{v8t1R0t;YCS(-%_YKoh=fe8vpyl7HO$(;)vFlHff1tnE=f+$F# zqy%_XCO0>CcbAcP=pM{nZhZ&1XP(mFCWL+~iWb0W_S+0<7cpRg^LrASG|iZw@abco zpM0BOKH*qi&a#lLBrSOLA*i|D8O;m+$>iSW7dNxC=c>0mF3n8YpJbOc1>=C6nUjf# z%Sar76O5=bn5Y|iucoA2!NMu!Bs!9m%!5iX&y~}H_PtWRc~=mAMnSFFY&M&|gQyXz zd1B33n!H$~e7?G>ZbIrtIjOq~F@bW*2L}fa?%jXz;K8qc@r%FzyTAMB)1RlI+*|JN zH+xOao34Lxdh+JYn^$jM|MrWozWV;_qmwtAzU%wG3>8#~1b`cuHH$Kda7&UjNzN(s z0c~oBt{;YhSQtpq#bS|CimZVqw{6qhKYWl=`|#nzcjs?UPfyx*|8mupm2*|n)V;9& zRbRbC16b|cU3>sJGnGW};%+X1$WnFlsg!4COmJN+r(Gj^cJ^Q=`8^a)p5#OmZKVM=TW!BhLMuZoG4^saSJbExK7)Ao$wfOopiuDcGqL9$Yho>Z;&u? zufe-Af$^0wTnPk=!Z>l17#zv2OvIHzA{opqm6a$7H{N6?7W2WYxkK4Z2*m87&PvPF zEDx4FHr-m#i#sxNBg(7=Bk(GujV5O&FG`{*#b^W=5gTbKUTZZFtQJ9>945`Cr5X~e zY3+w-)L6os8_bbdIsHOoCd24w=J1HA6ru6qz4K!hNeqm_!oshtdy;MFs6*5l~l7X_1px zD=48RQ!qe2U15$po&?{OOon~oG)mN3H76okG|QIBy*4T5c45^FPAXPPc>AYVgqtVP zPal8q5C8BFPo6xvfB*jT$IqT0JUTr+B~IVIIqGUTI)3w;@4kNV#n(q~-W(qvcm1Z; z8n?GNY$AWDibx8`JPC1bnv_*liQBei=3!vXDTeO7y}kSQAB)KS!+Vb(KYsl9@ss6Y zAzZ9C?IJHWZ3J(6ftk6vh!hy65SLmjp~9IxZX=6$1#)+fK?54WKVw&k9q9H?7_4@y zAu`%w8D1Oi*ymC>6ChK_L{#SfX!c_Z8SR|k4S>XBYP&h+-?UD|G{iYZiOg7|cCgBD zRSPyZcGPxo1DKr103*0j*n*jhrWBx-6U2D z&TeoU*DR`GUOM`qaAZr8yH`W4ZXm1P5!5&{Wl$ngrOa}mZCU2R()VS2+jp!i-_uXX zG=pK3&JS9gh)w{JOSH+hrl~&M3{>rN3O0{QNP3 zkAox{=2!#>Q`p)u@o3Gds>FM3V}@ZE1cb215{3#nz;O5K&~dN~fc+ZZH-_ikgKmIe zT>grPAQ9R)!Vl(ULSq`>c!D_-<_WvpS(RTRyRXuRse+UOo_#nA!w?~lA{c%rj)X1&Y78q zp}Lbcso7tuYHCx`=HTGo{YQ`Q?;U*d{G(R&V7a(|aQNoz?A^QL?_PX&es=o$?a^wr z>T3tGxHZj-`DoNz-P8*~d<+n+GFpZ?Ax&L4;ZBSvD09J!T>*j#xS~`wiIo)+aKS2oZAkoOGKe5YykZ z*Jqi!&9GUUm*L}NNDM;A^vBZTm{$6JW2ZLZroP+v{P-vMNBMHH>eFF@Xw0uSTf&f} za~Kc!=xO5D%Jf8|xKWa1oHs{%!&w6{QnAcov|KI^4i3z$w;GYXB-}_^EEo3=4uAcZ zfAy=+|Kj5E@}K|ZH|LjUo1uSmdHU_^7tfx3@NRSU^|#-={y*Nlc>DV8*}IF&%Wkt0 zL#YFtSY}=;Gf7yBGyxl>(9mXP9Y<`3b2Lw>+1p#>=5V=p|6u>#a{qAe@X^yJxoIUQ zcRM~kKDmH>U0$BNd2{ye`0RAEzPh|Tzv?%ezB9PEn3w7WaHjEtb`!vY`Z%(@B)D96 z@{DB8&|nb$jP-~|lQ-7chlEBiS|VZ&&b=mKbyBV2FUWIv(U$YS?agIsh$f2QPh1X0 zUW$i_sq*N+6p+ytF!fmRz=_>q%vNb|21%VReRZ;0Q11nnt&dI@nMKnyeXpG^S(_RnTBnK<1`A{?2A0*pM;j-6^G% znq2z98M^(fN6)KU=r9}>0(DUe%4OP8hvrQKMeizfEvfA~%#5iua*qMWtWHEDyLB3P zrjz{{j^K8LWQ)|eDIsDa$~xRVr#!PXgVq>DYD;D0(dT%^A$LX-lX|MVX|eD?9_`T1VE_-|i*adPqQ?Xdbk z|MCA$P4o8n`1R}87iX8&55)_tdNp>4C`4siJqe`I#+gK9xm+@H&Z?SIN|8zB?rtP) zUM%;P`+H9wJ>F~D5AHvD^5DVq`;YgV#ldoUa(RBTKKt_M#aG{ddvE)Ira5OvM@#4fp!6UM+8ut1;;rUjwaN^!xz*A;IzMBpjG5KL5S9qIfKRu-_R z2#r*1nYL!OdrQninS=wT8G!1((-6_*j(&&4af>Vcu`AT>brddHa5fl4Z;rA38~3~p z1`S>Bp=Ndu7K-TT1821w!ACo+_qL#daWjn_lRz^|qN-Z@!3oTKaBz@WAKW|mcmMY9 zKL7aV&mKLzx3~Avv*#>&xWE6|&p*qH_SNyx_eU@P<%{3;L+QG1v*`z`xq`?Q#HudD zs_HvwJS5t*jcU%hX&Mmw6-gqhP*nvpSV&k%Cw2=@CG4hci15}`f`##8tc7_w zE#!e#D+A*;b~KZaRr%~!d2B(h=oWUW6^LgDT#mq1)2(n zJF&)~%!wJ?GE35wQUcOE2*q#VkjQN@&Fis3IT2aE-tR#oSwXWcXE9E?yIl9d3XGF zwOXy$>r#pdHO-=sDHQHbCP|f(w5pmD=RhU1GBYolcFRuSxac+XG*6#C{n_&mKYsfB z@x2EJi)GukB+?Dto6~ov=cix&@YO$k^Dp1Ndhzz;WY|>ehk|tivpG4OxDt5OF0uyq zGD2`5V#cziDeusr6WzLW@MpxfZ?#JvleMxWVG#}8Dx;XOKvhMk z<(%8b%!XdV!I& zCc72_2!=o4!g@!I=NrbUL=?pF<^JAY(=OY+gC|cu{rt1P{mWnP=l1Er;iLU~i9`~g zygPe$a{BW4?2E6y`r+k^*Kd!`R_9GMau3@xhS;V@Y6z4>9YmTENn*`So3s(pBqPx; z7+CHumQ=ZImQB;_?d{#Wckkm5Kltq9kDuLta?mVr(6#W3AGv66m=-X3M2UbCc;XKTF{vpriC1&t>R#YZ zs9;Y-Oq?`H7USp$n3z;FCC#KMWl3g5h&2g=++8&%G9zQ=%)}yvOjVNzNn|X6cA$5d zYf?9a--ua=QW!Rhi!l`nBO`L=G=&>*B%#BpxG9glsEF8mGGKOLrN|LXE`{owHN-~Re<4)^vFJFA?WpA^TdqoaTR z=F2a?`|9NN>x-+)zVE#_fQveqnH#bysi;aOh&nMhBt()Id9he5?;WJvy3;V!zAxkh zudWO(O-_`T%jM5L_;}eaKKqN$nfc?VPao_ZEOWZpT)ugC+zrFaqgP-3@XfbBe0y~A z_Hw;i_g&X@TQ3)nsm;VqFqWM8GlY=mQl_9LCy-9RMIa#} zAZKWhj3W1RzJnE&nKKbl9k^ORoQ_HxrIb<|u8$%~H4o_5gmt$uCv%1*O^sD>rdIO) z{z8Q6z^Cu3J1>cs3vSyiA|Bc}5oK#QDdhnrtGoJG^oW+@ARFy2EEH<)IcHG}>3 zu2-AEDv45rKr`4}L=&gJ*m@}L(WiyMCmUSAUaJYTR-sJJUbDiy$<>QjK^?8##F!vp zcV>c^R%UVXltwDOB#|+{Y^vf6hDU!+LEDMuXRz`mu^|?Jj=T z55pNUU62yyoWv@eMO8%OEmh%pt2235=m>&iXJosUoko0(g~VgPrHp$r3Ix1{9T7($ zbJ%8cB+M|(CPkGHoS70tSk2k9h-OiBQRi&tLv&0WiC{InxbEHCJ3KsGESt^MX0z`5 zzG_aA(qhpB?IXC3s##dYtPwSgxc*pcjm^Edd8snhbPcOH5VM8=tGFTDhj&(s?jBA6 z9_IfdOzNmI8u}0sQ!z6Pj+7E4vNK`~5?3==XL;_^GG%27P?erff-=H(ZWKDY}fCHJ>th98Tn= z6!I|>X5z}Rh*mccNQ51+AD#@4`R=A2Ogt{Be)HuQpZw;ZpFVi}^7X6Ve)r9$U!R|! zzIk)}`t8xW8wQlFRJW2{C!bX1fINg%!`P?^+ml)cLnRUelO|DO&~ka$=2k>DL*K90 zSG88# z?|_@R!<|K7Ho`-2-#Sk4jW+pDhfm))0MiF;^qzQkA`clpkH#1fkt|`+4@n zLC)r?rpieRd2c=xXJWXGffPo&q(8G#Yjv+8O%kcKx_jT-(3dRXTuy+S70EnnJk4O0 z>nyG)1;PSqa>g*UZObFniIh@x_KI0b1){)MgU!#(smX2>BNTbGk{*)p3J{IIP$Rj* zRY{huRR?)smEmTti6uL#Cgq$Z3pqnMvMU)nlH+5AO@KMo-OpY`{&2z3j~&OIdh}k> z$Mr$)j(HVRnAXng9FzVWPL(K;2tc}1A{`ip!DXf|aeOd2wd&N@0<+s2!`)}&+C=H> zprrcWzW8E*?X`=p>n|@aMPwNIzVA2vK#XdR#KMUiO)M0YGhu16sxIb@AmlUsfKR`RL87#}DqWyQ|Gm-kqPnJUV)F zdU|$wF<9;U9@YAO<1ivZEeNqo+&aNKCBVn3g!j+U*NgHWr$T|Zyt(W58LoZC+pb2& zOG09IEz$F^NM=gPs+?3*HIwLcz(yXi2=B<>u+6{5)bq+L%HDlJOf;_0!9=WMxTzC| zv=GW8J5!irI~Lgh26ghz9n5a*mY9=}G@evjW0sUb3Q(kJ5eZpg0^p=lt8Y46bi;bR z?ygpS-z#Sr!>BTqVw-i(EKV$vyp||oUX3DU-n~{I`dUHm&PEUpC7Rc=<}^Rh5Jptjy3t zOpQjZ9*9i4ZqtMSu^i6EGBxLPTO@qABOhX2pxwN*>q*3tjWiyj_?8xN@~Z%3r_u zzFjV?RB{r6cb$V%N@iH3RI91N3ZR~IJ1*3+=C;Z0k|zqEyLUsMQfivT{{Hgu!}}k7 z^!)L?XV0E~`1J9!2M334{p<6$m&4`B)$zso`DS%lYE{*BDOW>xadFXYN+k?KFHSIL zfm;<8wko4)(Y76BHHHxR_5J-F==C4BU7F+7?lQyOHD%x%TJeaS6&XpR5Qs>U&_Jjt zCl*CqO9I}M7u6Jg6XMqsnkXeShnH5l%r%Z@w9MPqY4 zfQq>}Ig3jb(}kp_ug#(*qw3ymHnl*~!umltwA-#kRBafB)6=ufW{tt%iBdZh&}Ohv zXR6^1-gRrYG7JOLsICWM1Foq}J`7ahAotNI3WTAQlr%P6DXV(|dm^beaw(OV9KsTt zG9##J&M9eAhGA6(t09URJwM!Cl--%d8j^5DOC;vRpfJG=sFqX#V{=9*5k-mP?*ay=+Bl2^jix9R z5|}wn*~V~$-lH1xVShv zI{Ny>_un7=aCv!oxmr=R%G7lox!btHhK_1k4_jBVz$$i@YkZCF@P|X%zZ2R$I^;X% ziHUCdHFLN@#*UhZRaH`Cut*kW@c_2N_>_ngVBR`mQ8)nY6w@0(=N2rJ5Sshs`!{KK z!7{36eO_#H^hz|}hD2b8L#RM1d~h}w7cnAp@2uJ+hhj8_3dy9TiQEf|FP7PejnM8d zYCqV}3p=?fCvz~PWnp3#1It2$n3EEK#7tftRI6L%TExwrsc^|zbCXECX`7~L)_u+W zrmP2%#LmQ0iwP@vQD)7-sbLnd2;9S$HAD`iDiX$;QycSEeW7Zid4kKw9e&bx;8S8di^7eii9_`M6 zm>p~ITf9wvZ|ws|63cEOmyWo98~CJt~XyzYZuhG|fr%u%QC(aB9DWLP6hbWE$=xn-j&*~r0mmz^LC z7AH0u$#5Z1k`a_;s?g5T;22a58kWsQ1J&ZS&yA=g$*Ikn8$zy-Ewi@KVt6u@DVUW^^i#AepZK*(@&5(tx%h(<(CsJxogv}()Lbaq9g zM^%nYcR5V{M7ZIa_BEVl0U3R&MKSNl za7T*4WO5uPo&b3A{qu=;D|h{@QBCV`cQGSjf=i&3<|2Z@3VUF2If<%@q~MHUr~usn zyln?W1-;xe*|RX90|la*R4NHqf(iUrSqW99MT{pU}f-di62>}NmwgDUB?_Rw)K07(RI$f{VwbojzStVjJCpQNfSww%y)UQpS zFVl@Z+KxsR*VU7MEbwUO$K3@{$xfiwq)C{{xUj^w%o8DzG@4kzs-%=e1fH3bHrt=& z+qq9flsT&M^&-22)boh1h?9?+G*9GU6XNj61%WZPVJyTj7YidKCn-wsWad`Ax)hfR zD%A>!=a%v^r=r?QO|`B8+pJpx87O7dJ!+P;*EE`2C%9M6EyGJOUL?yAJfqBPl^gaP zcEfO%Ko!7nCbBSGa(1uq%0%W40kgZ4SIDAmnkKf$G06%f@EyRxWW`wvAgn2+l(Ukw zwK(NkiQH?Hdvd_Ma5Z@3tws(jgS zH*-`DfN)n=k+_DLxWY9&TgHSa4Os~jvxqZp_|M&#d*Mb| z4)b2ST!_fv4xnFe_MSff%TNE}!NL9Ia#=SkYn!(hXWiB6Tss0LcZqc-E1!WhmP8~`&lmapklR&ekM%9)}S+txr^?eRK{Lop>dC{hW{q|t56^5!yQl{n7 zrS_c=XI+F;Al`?eIuC<*AxGrl6f>}hTh3XTiaW7{36cRU>||shPDE0T*Xu!q3|fni z&K8j-?(Pm|snwZ9nHo-Q3K2vB6adv7KOzTLXCbRKGEcIo605Q%qLf0~pAvWGR!SiT zc@}aoSdC%u>ZL0^${1?DjSF^|Au--lEN<<>f21e6mhX7$Z|*`)+qUAm?IcfX{aq&U z4GG__CiVtmH9vUR$^5%kD6xgZ=a&&tZZSBf;H^xqS0hV;NR*f?oD?D@YpZ2>e}Nu7 zDw$YFlZc2@_*F>wKJ4!wXuJ6I(Fe`4?aOd{a&mrgs*A=LRjIe3T=e~$t9M`j=FRKV zv-9(_%j4C>+2v~0t^0l$9F+*-m8_0)idT3zZbnA9`H1!Mc9y|pME<8-sNH#BXs2*o zujaNx=b+hPLWM=b51zt_HnS!giQ01_mP{a|M;kp(pt4=>PU~3gvR=1pHWExc!4dR_ zM7+C&WBVM3H8M)agaELL2*fx+as@l{;B|2BZL~VeDnznu{oWyc_rBq=OP3}mkai>ZKxWFhvGt8A?V(v9-&Pp{gz<^qDVKEz6qSBKC zEYadfqe&R5l(M8mfN2t8TGfFZ+%(2)?gSR)x-~B|b4-g@jI6+Ll%HFqi7v;tm+&`w zgx~29-_B6Ffp*Vaq^-;4Es`YjBtye{PrqUtKyt^ECOomC9DF@2cl_09Zcs_Z9l=zp z@B~(ofG28rH}F{I?wsScJsJ}UvkFAuB&0?uYUIF!^aQbot^!^ z|M@@FzB}DqynOjXVmjR0?=DxHZuR=&^yFeyHeI1XhH7va+|3EXVBcBlhl-PSa;tXi z&VTG96aJI-N;iM>2q5kRH8X{4f0uho_&B5eG)!YoK;uinh7kkaK zr^`=2efr?=9;`n*-w=6Ux~6FYfA9OD4A%F9E_9Li7$j~~Ev3XF405y5_rq#aN~sP5 z*XpPdio>;Ksw&FN!UUDnG*YVj(2ry zd-3Y{`0eF-HAGS)j5ljrudd8Y*;_&BtC^McQ2W7?ldu`;P`bYKeW|rp2Uy(6o!AYG zWRgk&Os)-tr3?!n&3YZE;&vQz|GSs0KtF@L&!VrZjnnm8<-)~Yqt5)j}5lu9j zw%OK;@_mJ7mo)gV%k;0W^KWm^uYdEK;BiQ=;C}mG-QHN=ew}lpq+=7$GuR{{ADF}_ zG?`{9F{xfFSZ2-+KvlzsQ*u%kxT6-BIYGwUn`^z&{OilsWS)KUep#lxe{k=~qYs`x z`S4dCfAWh@K6|)-KUFI&^`-W!_0aYGYTXZ;u3wkhM=M?cEpF_>umX*36j^xDP7FYu zAIM1_w#{n!T^rj!uq55$YK+<3yXQx+jb;!Xn;;Ph@47Umo07658M84&lyz*U$LKv7 zb1T9P@@5Tk8i^d^4xeeqQ+gfFZtS^J+yf+NPdL^+eQ|0|d8v1&AdfKo1 z)m29%wUlDjt6A~tW zEQvQ4z9VO0a~E@|CL&q5QLV}*$O%Qws zE%8Jv*fdLOq;1>%{rxp}tVHAxA$Ra-=>l9&6WEnj;QbnlpMrw#_BcOTkv!71N@ zL{oMJ-hZXvepN!(YM$)COuLVVyN@f%(FstZR*R4TS;pN*bwr$}2?Imeg7NG&weuV4 zHD^L%mIzG|$3eUN;L(%MKl$v#4?cYU`03%pLt*Xe@b2Q`^8Dhr-+XcO`sJoyhjzcz zYF?d;A+=DbKv4hg!?#>Rb zB)xfsII#e_q?A}QH7REzUv9-PD1j%VBoFl_Bs-GTBmxk(<@(r6RQ|LQ0xUomquywFvzgAvBrk z7X%?lnVfJZceU|K9Rm}E6{MsrrJA={36hmXUBn?stO7E#0J#ikvzilWW@*=@@77%@ zAb?eqa3Cikk}$Jc9j?TAk|8R9h!aTHdhgMW8n>9WKb5+2X93!+P}>mQ>t)8^Y@>ko zP8Ndx2;qfqDy63N3GMds*oHeX2<&dkGL2k8>d{GjAWXvKZX^jdGRDox2^w)de2Y68 zuXbVs+O$(>B|SR0_sfrd{+FNs^5Nr0X65+o-D=gnc=_`Cm*2iTI(mJ4baMW#>(^_K zIbd{vVmOnJpE((;qPkFKfsG8B0LYw4ZlK77X@KSb|0~n355E#|cs$EEsWmFhoOHy( zMaMK94hIpH>u1l$;6UcM6$F0cxH9pL&V3A(o=%96GZ|AA1Q}qy^;DT1ZiZ@ZkV;k^ zwV;qyXfZD!O1el~*}UO{`}=!o(d_xvrq-cUVuiU4#PC{%j-=*I*ROn2$N@;=Mm1$t zl{Po38g!phO7U8Ym!XzY*s&Rg-fL(Bk~X9sqzD&KLAXJ<0}eP>slAlNqG?j{`V71oT9JN@EpT)%8HOsw!Vhk`ciOcQ?ED zoW1wb;9N6zj|U%>%$8zAB0S8l+vD8*SZjT90%4B{=~y$JcGdSv|II7KNZm+(w#gnL zVw!PskmLz0c9LGIBv~>51e}7x3F4U{N+62Xh#bqEL@F65W1mEFB}3}5A1T@yDH^iN zsEJ5r`XbOim$ccY`Pqk0o?d-$x7@UT7unjPy6%>BOJ5wRd@VE6ODR)DEjCwFBeTwG zMW@alWLJp{2vc+=*27{7hC(P>f_=7m-dU-_#NFL-z3JjpO{8XucF*E1Tn9MQm?k>c zog+OKB1B7W?yFQv634qc8Tk-HTB2&XMaHp;rWB)LEviPqQ0H05Qll8>c~|BS*2UZ5 zt{Gm(QPU?VgSwM&tg%#?dsTRg3_y#hWYF@sW?PxLW-?b1MJebUxH+R((28f2QV5;q z8ZMBGNE7A5ZO*r|wtY?B$hVnp{4*vXU*8gbgAtD$^P@Me_B2;b_j``u`IJl|VsoA(E!SKi~*-Qla(H($K`@^HM3h;(P>xIJx>i6rU5 z03W95ZDiK(TD|`9{30WUa1?XrPECY~93q&Fq$VP&1zRfmWaT_eHM}_yXUbT{pMgB1 z^3Act&m5p_03j+u6+tQ-n`7*)7L^dg%v&5_KG~_uOiktjJW%iW+D|j8r%? zMihF+2D8u1*1MAk0yb>N(_E+7E-&V3suAfe7V8Y5Y$}8ZE^8N60%kVP`$X%V9;C|1 z7#XW~5fw<4 z*t>|*#7svDo0OsoDJDfqt+pjHOM?3La#kKo8B`?s&$iLz001BWNklZ3g7;87MnpI|j_ZOXB8#Y|do<7?hRC5d(3I`1$~?X3f%}EG5&xf^NAJtz zJNNSUZhqV9%>ft_*=+q~BghO0{lN2W0n~$7%Llf#`z=WvQ*NeVj9(yBRCNEV-q4alHBGUxr#p~M_zkmMumoNQrG}#-9Gdv**57t@+ zB7wNb$xQ( zSGQW;7z6z?&ZGAoQr}Dc_2?=1rjPgAoRTTqY%3*C3f_ASCxJ{?*rP4scE>B3{(hVb z14)5LsOmOf*sgX&7ia3g8Tw zJ&Rzs^JqBxZLr3oNRiW;>?DhoL6Q~d<0akwbetp@Cm29(u$6%@8z&ndBu?C0m;f*l zy5(Qw<^iWbj8IU6s!|~eOOL#x9UD6`7*Y*@8TWhri${ak~XGpSlKip)-L3L@XM=$eq4P=W3@1&#?lJ36twiWnVO4Ssrtizs}%p}dq zW`|Y+w4t2WG%H0Q$a$F3RT|hwF{`?I6xC|h4ozFXxw&b#chLe=#K^R=IB^cxveqia zHsr0UnK6PR4kfwmD>$aJgXvVJx%8H8X-F@B^EZF<@BZDtyS+WE-scaVcyC&4m{Y!& z8PG%LD&CQG{XMjb@63w(`t$TWu*r1Q0&tLU&d%}^y|N2WY`c-jBg5~h!4VhwK*R{9 zAQ=hv%oNH9{~DEZk_;Vf0~3;xK%9uictoT*CGT-yuOA--`98VE_ajZp$8Xcgt&$if zRcvmL4I?8a+^mN?Z z3;55;bdQtXl_`Mc5P@^ND}s_th=j;c2&fnVm}HpKmeiJ=NRYy;ifXvHn1A%sk3YCB zZSB3Ux3`DauiA0#6o}~3{rQXA=A)_pTF6Mj-E~h27HRq|TpA-HamHMMOa*wrHAR1}>$N84;OaO8N5D%P(HP z&YmqpMG^@I)ZXBHedCSv`#fU4MGf%iDfkXq=GR=q*d0GmW(BAO<32ZM`&&i^v7wH) zKO29cc}I@^>cM_dPNb%YEuIDdW5W}rZw`43`-9>X!?L-qDLC0?Hz)t*>dLPfoqgXr zNqw;N5<}f# z5HQZRw$oEnWfL@kIntL9xm85m0Ai2=O;94G14$WM(h=rOF{pa7A*!GQ>R( zYijiu&tLuacVFGy^xM1e3~IDi$d&!oZnkL_Hm}{jdVO>)eYquHhjtive{Yl|n;?ij`Wq+w%uMs5X@^zsTD=zjKd- zLIWvj9&9ZuS1T|D4M@!(AlrvoU`4CZ29uT$aw-KF`<6&Gm0Dx{6gIg=g08M^q}E~piVDmj8pzz@m>~m2q$1W@>%A?9b=X=9OnXL% zq9_SvN#+|Opg)FJ^PjOh`!)ySx4M$YsTv}JtvP#)X~vmaGf=}D6J@MChDkD!XDm3$ zL;~1QqT!I-tTd4)YgMGvRCDXQ$NfCEshFrxh56QtuXrnF&utN=P+#EH!s(WqsX#v;6Mvc765Yju9fqREvarW=QW_JZd5$f-1-WG$9H0 z@YUto_ES~p&Jiu?A?cDJBS<9y?_ySEzPOMl*Zky%`0%5rbK!43JI2ylw1}h%GMO39 zDj_ypgoKi&Qf$cTN9KYKQgCt@k~7l=>5|MToxD39?q1*Ya1{tNv9T;l(Na-&deQfb z>Tx|l6NGJ3S3`2_?;=7KYE>vYlEcE+H8wkWx|F&19uX9HTS+NWR7){2s=)vsUzEJR zS)DbF$@}{w-*M2qF)ey$Tk)Ot+2>>N?f-c1Pmb^HDZ1nvL<5`WJgHl3;iI2d5kh%V zLJ2}hre=ya3VM$gZ99hKgeW4%NpDOhWsLX6Om}OIG!qoWR)-G36mYhhZuyDtz4U+f z9O)bRG4j?{@m`3y727(}>qNwK+oPG;NT5GM#z*|i7WM(C+8fkd#l3z1q+A==*MSDndo5s`eu<@CNX=bg;Vb6N4$4f&hDZtwd>-TMys4qMhc zhcA3!hB`&(=(sPLh^UTQRDsBV4l*HwLsh>uXpA#)ZqtR2&Vr;8olG&$M&x6q#rfNp}fLTu!LTH_%WoeUYRWqyAvZ%8t85^Wp8B&rRl%>hq%F7p7>RoSuPJxB& z<6Z#3v(u%hi0Qm9R*Q>zhKh=%2^GfFBt#e?5nK5W6;rF#)-~FlBUy?%5rCvB1Uv!sl;Pr0RvUp$yQ^s~Wz7Y4 z(biRD-M9v@a1Qn6HVsyap;(!wX%?$$)il|P8B&zeg}J45@z}ZT@=2fKk(`Q5u#%`KI-&P2gMP$hP(-5V ziiTu69z!HLm}&(;Wu)hNwhLLUyWQ@1Jhryf$%<7`!ce4?R(d#)W|rw&eN?K5npvqR zfBkoVS9bg3?JW%n?k+AeQ`JZQJWeQx#7Rr?R@b({r$B|^t+2PR7l?n2`1_6HCgnXk zg9ovb!f&Arc&9O#v=MDMq{3Sc$v}>#g8_~-C}lDcgYz7TBslJhkPKx~MmXGQPbCkW zH7Cf}Scp@;fA!Iu$-)oX-t+6Z{q6nQG9MN@0Tt6ROCITVL&|)-h0wr z*D#spVILKpvNaS>I9Rk!Dpn^;V49{US64R+>aO~#(Ho~CVoR*sZ;BikV<|df!V9Qy zFug++eQlFzl4{$xp+`n0vT1HiI4V^J;!C7xO`WW`OpFx(JSjC{i^{>8Dt7ZU6>|+Y zn2(4%?Fe|6IxGOTteLUaHMLl4ZS9#_#RNpD10S4LL`_tY9zqzTSWS{;d#T;?*w(|+ z2XIX*QVkSgUR}&7xLKRkl4}N3MHs1O+1&$@Y(Ph=dI?T-m&(kTWAP+Wq{Gn%;6Yoo za95?71rQlhswL99s}GlU)uI-)7|AM9Uc7j*9_~gRzT3?|{qd*X`{%!Zal76P)X6~m zLf*RfCQoe;&ZMLFnLtivL5-F$DsqIx7xq`#lKgO72<$wM#gg}%5 z#uy<dP-EO+L+yPBgzyj~85TyyU*qMGJ$z(qPSEwcdmQq&lnFwn4HgpkU$Y2T)W{MHs z7Ahi|YH4PigdjsR7}8fi>Si9Fs?C!+kf9M0eN+GrmWUo;TY6Zw&J=IU>Md1EDGKqa zY7wbsvrz;+T!a+R#3K@nETA;+hh@DzEZ#!IsFKh%I90nSwwtCzG?$f@W|@7gFOXa4 znD+pn)8t`AwNBXW%i(p~%e?gF5z;lKS1C{vmhmx@Kp7$Fld2-t2&RfoLrrd~OfxGc zrPdk|pZxG+1OM|s|2J&;U;LN<)qnfn{C9^qtu03hl6+Y9>-L$43^US5Eq_;A_deO= zcU6AN{ipwt;7GnxJ2Tw1n=S?UC>Cy;K%c9Lk!GX0^v^}Y(Rlkxk1OMD0YWuJ!zNp!y;^TCuXpM)O?8@zQk&DnPuBufpFEwb<;|;f_jTIVhOOO~Cr*fx-vr;t&B$wjbNHaF6jMZ>I}bzy-N2gvNAQ2YQHSB|wfCx}+wAL(deO?be}NrIjc5m%S59^=2sAc^J%ZJ2?C2 zok#cSxTjh&AG|O;XB^4B2XKH744df4Mlq$E`u4#)XpaO z>cfvNudeq@{fEDQwf0xzke#Nfq$$gBIrjdF;uh4*DkSs5tnMcPydyK6nXN^4k`Z)w z&?392RxOjv5G_+hfvwBhIPZ6@X=V$$4Af<0Y&l$Ng<7Ldw3;l|T)PN;74M?O-TE=R zWfq&Ks<8CFg0$4C*^{8EQA{E&O>Ml^I+;}~r9iC|&7x|iqSX}x1K8bLklv!9d3dLL z<^WA^!VQXOdh5L|s}#-MJniSxn|$>HCt=ufU=sAnr6;fGRZ6P1gy1qw;+i4Srt>-(UP`o;~ObTZtW~IbdRo zB1>$6S{txR&*ae&y#^!DA(1Yo2_ej+mO*DQ^PZi-2=Nq-48PRGWZ2E++0)BUKK^i; z%iUcEO^5WXm}jWV`uezV0kR6NN{!ZHUFbBMO*E2>=-yZFnThDV_pwE1#wMT%Z6Qy0 z`+2vU%OtSh_1DE_fp*6jAs5Q{Xr)MqI(=3=y_|NtUGM$*E6*EdisXQ!_0_c;Y(JNI z-fC4<*hm!j;p3~tXkt}ns~#yLn3S_+Eyc`4qm-g$cCnt(divsP>#a2nGL!-aRiUa# zLkuw|nGGz7To7V2CX;HH`-{Y3ZE0rdaq5fms|7rQ zVVkn9LW{{1+6!N)5>lX9C8Q93h#h3Aksc zh-5NR(o}+kBShO8{kv8cv|7uj$vmYh7^eAKs7!6Ze6W_u+GREA{QH2^@z0Q`$c^ zBK%2XJ>>AoWXJ>C#p6e^Ga&2_w+g=ZyuNqRyln?L|3e#Ax>IA7DcH7%r<~aFOR8*G z@=}Zf6|b>O(2mpg;+O52s0DZt{puSzxnm?i_c&7 z?uWy|cG0&4qv&g_P*v=9GlSmzF1a3--g`tcrT5;cBqGMWSkHukZQ`yNi=ISw_jza6 z*Vi}4<)h{4as9jpODQRuPBF_Ar3n++V3>F1lOKQZXMg(Xv@ZwKukL>L`lXX`O7HTP zSl9K|axS5&CY)4du4VwL5=EtewG0~oIuX4~DI6qLu{vGAre%5k^3_+-yV;}}3Z+#_ zRu!AdG>aArOYaol-Q2v&5U5DN#nG~ul$us_x0{%Hw?w_%uZLq_8&sfTNrr@H&-F-a z%}BPjudPdl7E57OgQ}Hk2-RXL08A@033Z7p@GhXwVt8^ zp;qKznYj$mPu04X`40;5T+%QmPnJh&~iC_;G0lY-h zW^G;Lt5-KqKm1^(U0mA!qImDJtF2Fs?kylg98b|=MXc)na{l1M%iFud<$l`l%+x(2 zPnmgLqO}H0JdjxuslV}vii-TJ%4>05lR`1rc9{l zU|P+^E-w7T4=*k+FMs!&ulh1A$3kC4kZjqJP*qcwsnmI{lFj?ElQz}fR}p0-)V6xo zJ<~U5n@|;M1{F1x0qo$gSkXJx6C#09pb|W=!KFuLhQT7KT0FJvcDqtaI5)32$52hf zeSCt8=seZ^et&awGfmTf_VYizy1LR@?v97gKKtzD%a<1y7nV4-bwGY=oqF$Pb`B)^ zPor9XW6FLnC?_8jSv-5+HOEn{0@xr?ZF=h*qMJ9Z8246?Kj=4l7_E)(nDaSS`KSF? z;64g!>%TSyIdVkmiomRx4r|CT&DuDIhO6cctIZi<`sl(&VhJ#Yc*)#o6oc5fHEww# z3~~S?CFArEA%twE926aaV?^efxw?38)53{~6jexxiBnrO5xX3MdC{^g6Sr~40{UfYz{SM_+?Q&4pmdhpXUO{GScb|;4kzxmZG z^nWa+ym;Bx1sNu?H?;trWAqUo3~$J4MJfv-C?R8T28~QrjXqK}2cHg$_x&_S^3db; zx>noi%-#c3k8UhrTFH8`v#QhNKlya}^d}#Pe)aq_-rSUR1&l(#6e$s0y3c7APQGmG!SW8mYl-|83IHq;5457e) zq?V^w*Hftx(b+NV6~I(0OEI%uopy8G?RJ0q^I!b(m%nUnsRHl)#pj=Y@$##iyW5C( z{rYum>E%i2=1nCZ@+SZA9USF7RTAIrEZIobr*V>D!+OckJPrtM42#mRoSY|DDffic zM?K>kQ?Pe7U;K{E>3ee*9(hwAw6PC%VPgq>{$DxAM2VV=`y$NBh`+HS<5YS>J2sfb z=LddYXp=xsaXH(+*({QR!D!VjObp5xR^DyhmI)aNox?6ircrG;_=onod-TYJtYmkj zz*8c|H&(z<8}rptL`x~PR!Ht;5>#E&vxiwAm_zxYqB@jPQ#F;HYTr#_hGHDPQ3*$i z(jB^bbn4pOlf8!s6=n3mV$fpqepZpDt@kciM3guH+WWk&ez=p*e)~$ah{(LN%d08e zfiiK5%#6WTae>7#?sWYJ_RpWcnpKWXUVU{KtCmg5Wul@`a|tscB73H%256c<3OboU z_8e8-u(tLhWj_^&PNjD4v!+RxCD=Iq001BWNkllp$es3X}gPE?HY^y!mlPp;Q5jzz81O0t+t^M1G6sp>}`J-fKL zxV*e<;q~gGAMk(u&0l}=@h8upKM&EnQu+31Nsyu{0@1TQ z4b{{HI?kn&L4?3LQf|9v9eGktr{x5cHTF%b|Q1F0cs zdhkw(r3z32$UqPGWb4^OQsEGXkj5NiHDmffMeC%yxt4j-Ds?v}TZv-K-1RlR)uCLK zP>5$$E2Ty@RnC*qM#r}qv%Rktq*XH$6FCJyxDT)Oy}n;*MQxrY01*Kp)RI!Qdr#4N z`}$7h$rsOC$cqo3O=5a=z3c68JYbTNu(rUxk)iO+cDP&b)_8quMKltvIniuV5iM3! z#vVWjah)gtwLn26EZRzeqPca+Tlxg_;s&jsZ}`rpn$@M$E37sufyQ znC^UN$_qtAbjWzzS!yj(a_#8e)7#;w0jh!!8HTuFa+LHa|M07SdinYlM0WeSg~CnK zPk!=~pZ>|8{QB3w2Cyv4&Fj~%UcFk^wY4^v`S-v7y-8`Y%J4p>c<=n<>7DUt?MX>8wQ=r!~mRl=Z9$-B>_Z*1e< zcA4>Aa`XGgm&f_z+sESsJLALc;<<<4w)vtotx!>goam-o=+=q;c-{kyZCEgv+eYH? z{B9FopAP7e9H=TICoRDd=Q6bM!=OA;##4riPD!Q?Pv?g5f~ra}8#o{;DB-z=uWL3( z4|k{*NtKi_|0*Q2CuJ{6lo3ar7ni05WjD7T5gD2~m${epo}ds0+Mt1}7MWGSXlCXa z8igVqNs-H`$ULiPRjv^t%!I0rqlX*|MeA_(gmYYjo@)@)5SbZ`lQt5E>}t@PYJuTRxG{ z08j11P|BZ9#5}Fw6PrT`cw89CgN5BYpLtj_-acX9OcVcljXS@g5F6m7&5x?X4vg@(;yB^2x%_Bo{a|P1WO0nHEn^i`R@Exe`FLzVL zQvgYI#7mHR|K%4T8FxAY;TK+S)qCoV&Tzyy1PdVK4_Vzy4%&r#qUD;wn5*l8ak+p zr55|y&wi$=7Z(@3_hR-B|M;s~%HRF{-}l~MzkWT>^YztLDdourAJkeu`uO8#PoJ*q zzyJKJ7XZpM(L==^tZp7mo%Ih0rTp4Rpp*wa+9nV_ZR-;tGfu+m;oc^2lJGn3t7Tx@= zTll4%ver%^ypO~aCxaT9IbhkN93)ggMG~ou&D)R2jLp9qw5__}_O*`F7`~JN0wOS# zNs5`4&^cpmD97;Iqo0%3pheW?{k-2zwN}*<5xR;;clTQO(T7j>bN%4SRrmGgw!;@v z(|ggJCpCpyhSN5P-rx~!84vF6y(cMRG}Ea#`E)nW^X!y4^ROPKVl=%QR+ywfb(q(H zgwn#OTFX^=rt~J#``UVD1#{7?t(EmAZf_1h{N!0*?=G(|=G&L?Rg_Xni42GuNs%zs zQVL+IC=xs6ELM!WN!?S$L<7u~BJjnd^~koeqtnL>LLnhQ5lV4}G9`4$Q1WnGE>JIa zbv9i~s`n~p`w|P`d%!i-9d}$;*W2YlVW4CNOf^Ofw73um1|ZVul1QJ#F;!HJl0v1E z!|$jg@P?xb7+)*3d4B!+a9pDIXv?aK!6DaLtknH}k4QhR8A0e-U`H~Y5m7$4y!yq@ zfBw-&AN|!|{neMxpZDIEWjXuWE-x=HE-rrlv!9LHr}zHk$&)X>_~QEV>TX>cLy0bZ z3mB3y;o?K5=sUTI-)83h4o~TWuf1;FB2MW~;AyqLh3=0_+LXi(J-9=JonLb*{ecps z@APPAQRo;#ojD)6UF|4QPOu9R15AgeUQM))U`SKdk=?h==}(q4xrdJPk@N0Mb9&@> zCeFif*%Y25)INsB{ZvwhN{SFdi_@d=oXVsW1)0!; zYOV7$4Xc2vM9MkpGkTKhonL(Z@_1a2w+m9}6b%(bL}nBaVDlyMBu;)7`fliSC_kDeL2R9=dB-f=t8|MN0h_7 zKL7Id_4T#NB;YhnH#jP+*yIE9u3>_?Xes5gq%*5v7BjGvLWC(rp>^uL_r;^f>fJl( zt^@BWf?7rj|Cl$Q?vawtzC;&UEq9ZYsm>{sSi|>aT1$E*JQQTOGddKhnJH8ewXvMu zvLLoHmom!Ok?&rAu&7|3@$G(U%h(uyWjopcs$o%^p%Of%j=sR$DcgwP{eYwq6N8Pa>+y*l=}+}zwD8NGvr2{rL-?#=)Z zOoY2{JX$QWY}Wloh>*X5@_ zp7;CRS1B%J2M24#BZf&m* zt4E3sTQ!Sl+$SALU;6QQ+-$RF+~Jr-X;Ia3b93{zfBU!NEhJfMeR6d@tk1W%w-NE= zgAZb$l)$s*+qSR3$=C^_)i+ohfK6GIvCN3qN?31k zdwaL{88PCe)5c2|?3qMK#yFcYvqcPqUm%j1%%VlORnrlX-AP+|`#|~Z!|TH_*CTFj zZkWn$i^F9>gp`^oR2VQ#yNh}L^oN)K`d>eN_Tj}Z|DP9Myu5gIcgzOB!Ax&$k+s;1 z=P!XLh?dc(BB3yoT8oJ7X1Ux=RYpC@?gN_KMz&XGDvZK|`(5L*EMy?EXibn-AzrGM zQYR~=l>J4?WLuYYO>cqdDP`?`Yr>_~i5In0lVve&>grzMot}jfg`zoR6b67EIg&qQ zTT|)?yn~8x7A=r)SymTOt>*3l8>aOh@Z1D*H7yjc563k^>%>TyGBc^$+_C53_6VfA z+b{-?6hBcB187#wrt<3c=5RP%US68n<<*sn{QMWcXzQw~zxmB?{^=k8QGnaq+i|v; zS*`WM4?m0uNq+k2kMEXatv^~1$7JihZ~pGbfS|W+ZtqCfRe9&n$EMADaF*o>Gnvf$ zSn-s6)H#|wkQ&Lu@w2^Sbp5?JW8X(ic(B}iQ@17gM!j|({f+u(a2>a?5uwG9+=y$U zBSu#Bq4xV;U=VL9#U8y8dQZOIW`HC#Wjo4+V;(2lzF(Otc^1|ndG^c&(+BQ|bfQHb zBNuoERI*q_ViK_+dXJ9isYFq8WS(SX?wJg*M~f5;)~ZS&q7<#Q?&fJ|Jkf4wqNcr}f#j?%S2VzTEZWfo1L% zkzEwbEv1Lz5y7B!-R=c{`&2zuJ&WzGp zxqX!ex$y(TM~H2AZ?4U8Rm)WE@?sK3r*yGyHa4PUDhy+G21qgJSP11<_7}A(QbkQ& zb4A+HGLcj>tHq|pz4zAK-SPkO_O3sYCCQba9W!&kHzFe|>(N~^!4BK(>k zJLh*OSTxv4h#R%e%oRoR8Wg=loP4|8P_u|$!qxBm5CYMbdg{Zerj(vOdo~OM19=!2 z`0h8qNhw`lU$@o{hr{dFuXE1*Lio)$-vHR{_c^8857)0=yoB(FyW7tuqx)XwuL?sy zGHApXtE-Qh*xm?y|LGpyhpONpWB={Du-^sJKc7wV&oME5iO2jnGxlZuNA_IbMx&=g z5Tw1>s4v@u5hNnPaUY<)rzSmE@8L(Xbwm%poS&m?3?e2698N*jN2qQP?^7k{JmPOW zsXpQXgzD^u9;0n;)uLLgPOFy=X{d@w#*`9^^%{U)Ly(g7(#FlT8g5oLdI<#)t7O$I z7{_58(#4Pm;Ur3KYxAhBU(&-{DXo@f-n<#W&4LrFs%FVKF>_gpS#DPPXOyy}l1>vo zyuZ8saC`N5C%W!0@^n|ieDGA9?<3CP4vE_4Qic~V-~Rdkclze(qw6~-B9(wN)(l9C zC^ei4lt8jqjFK})8VtgceHdgn4EsS-mI%A9xVvfdOX(#m<}Eo+hgqxB$CZ z1rTn|5QJjf3mpdh^cVP_{+DlW-VJ~8=WibGe)9g^ZL2m-leZe?5fF{(6c?Qu@`eepS{rB3`|IJ>+~komy+9 z6lP8-?RL9Ij~=~x^@^EyyIszCS(dkN-@2%d#{(cV2@Z65zg+nZ7Q*+~eFn zgOi=*m~ihrw)2(4wnfq={@;JC_-cagOUw1Azm$J66)8U1JbuMi`oRC~5&WPhRz$$0 zEZb&{08u%k*Mz$b`{SipXsfOde3j|)l^O6pzP|-ciJ1%=_Q*POYpXd3m)IJEiKF{8 z`zIPvgE+y=TfmH(p@mm7>(wFzcR63Pg&@AMw?mbk)IiCpHE(tw?}bOe9q3P58B!X@ zaa7q2oYN+yHnLKR*-92f(hGdd>S~qD!yE`AX4aHa9&~>(s_I?DQYsLDcU7^JCW()C z{14x~`d9R`2&9zqj+do~hNhv>8t!Odf|s^b6O)F!W12S z;0`vSq=XutxK|1IIJU!qzWMg@^6F46{MGv5-Rq^;?WyL$4o@zIA<1r-=CU~P;c~iJ zg2-!2!cLwcsIk8~TnzTFfBpDR|A#;Nv;XvBl!`$w>+ap{EvDe0{;Xc-vIvf;?1GqC z2Z%?4J5xQ6s4aS}9{0*P2FW&yRIOG#G#yxgRGOYxUgs<-Q^+Nmu0!Rxj7z>w`RjQ0_b#3 z#DI2e2sR>QkA?W=?)&20_4u6|#V#bBb*pU@;Esaa>NOo3SKiOjHlhgGGIJUn6zqr^ z+=y1EQli>wEh560n_E!C88ghxB+Nn~MyM>L!V#>>V!~XRNo7mO5j{}x6|9;E$x36& zjB3@YyN40Ef%R*qACs^L}Hf}(OT<_ zz$z(mlJRiy_}RtdZ!Vubdr}>*e|YsbfBW(`fA@nMkTpV?_43hfw?klm`u=FG?e=M2 z!pzN#I5n$d8rITQL#@02{MR2|y}sVn+GK#&A&onry2+h8oc__@|HPfMM{nMg8)ZC1KKt!A(=OCk*nxT<;! zLpU}0Qcz|POHLSe!+tjq+Bu=-dYYHN_{+Zx_nfo4=bXn~e*Vo*)^#0*0l?$OkAL{# zhrZ8SmSrvF(dFfkv%5ce`V_#kr%&I!eQRrBGk0f}jlSFg^85+k{=u;Gh0^3bb*+xJD2*uFKOhrUjV;EP2Q(W5D$Nevk*)KSU8{2 z*DAuY<@kvxiQd03<<(+RSs;&s&{>%yo5eeW7bTcWoU~SildX~Mz zIi;_6qyL+{M*;gRMi7zC(l0I%G({<8Z6=FhwP7H0;D2vN9Mn_G?68WEYVc}SEb=K-92S%bt$JsJf_ zv||Z8+&enbN_C;;f$iv)?=|mh|1)} z$(^Q|>#+rSadQgJoD~`4I7CW`SUERlZ^=L=DrvtH9rEsy(>N7;_=mSIUS9vzU;X{@ z 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 z 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

c>Cdw0c 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%0Alv0kyd97{SY&nERt9x{G+-Y<~#4lXv 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 z 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 Hu0mjfE84{~ literal 0 KcmV+b0RR6000031 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 zcmV;&0671NP) Date: Mon, 6 Jun 2022 11:25:13 +0800 Subject: [PATCH 04/47] =?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/47] =?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/47] =?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/47] =?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/47] =?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 @@ + +

+ 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/47] =?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/47] =?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/47] =?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/47] =?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/47] =?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/47] =?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/47] =?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/47] =?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/47] =?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/47] =?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/47] =?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/47] =?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/47] =?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/47] =?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/47] =?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/47] =?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}}