#2359 手机验证码feature

Closed
ychao_1983 wants to merge 42 commits from phone into V20220630
  1. +11
    -3
      go.mod
  2. +54
    -0
      go.sum
  3. +8
    -0
      models/login_source.go
  4. +47
    -0
      models/user.go
  5. +45
    -1
      modules/auth/user_form.go
  6. +4
    -0
      modules/context/auth.go
  7. +61
    -0
      modules/phone/phone.go
  8. +43
    -0
      modules/public/dynamic.go
  9. +29
    -0
      modules/public/static.go
  10. +60
    -0
      modules/redis/redis_client/client.go
  11. +47
    -0
      modules/setting/phone.go
  12. +1
    -0
      modules/setting/setting.go
  13. +12
    -0
      modules/setting/slideimage.go
  14. +308
    -0
      modules/slideimage/slideimage.go
  15. +38
    -0
      options/locale/locale_en-US.ini
  16. +39
    -1
      options/locale/locale_zh-CN.ini
  17. BIN
      public/img/slide/bg/1.png
  18. BIN
      public/img/slide/bg/2.png
  19. BIN
      public/img/slide/bg/3.png
  20. BIN
      public/img/slide/bg/4.png
  21. BIN
      public/img/slide/mask/mask.png
  22. +4
    -0
      routers/home.go
  23. +3
    -0
      routers/init.go
  24. +14
    -0
      routers/routes/routes.go
  25. +285
    -23
      routers/user/auth.go
  26. +14
    -0
      routers/user/setting/profile.go
  27. +93
    -0
      services/phone/phone.go
  28. +2
    -0
      templates/admin/user/list.tmpl
  29. +79
    -0
      templates/user/auth/bind_phone.tmpl
  30. +12
    -1
      templates/user/auth/forgot_passwd.tmpl
  31. +45
    -0
      templates/user/auth/forgot_passwd_phone.tmpl
  32. +75
    -0
      templates/user/auth/phone_verify.tmpl
  33. +2
    -2
      templates/user/auth/signin_inner.tmpl
  34. +7
    -2
      templates/user/auth/signin_navbar.tmpl
  35. +73
    -0
      templates/user/auth/signin_phone.tmpl
  36. +11
    -3
      templates/user/auth/signup_inner.tmpl
  37. +1
    -1
      templates/user/settings/organization.tmpl
  38. +16
    -1
      templates/user/settings/profile.tmpl
  39. +201
    -0
      vendor/github.com/alibabacloud-go/alibabacloud-gateway-spi/LICENSE
  40. +305
    -0
      vendor/github.com/alibabacloud-go/alibabacloud-gateway-spi/client/client.go
  41. +201
    -0
      vendor/github.com/alibabacloud-go/darabonba-openapi/LICENSE
  42. +1623
    -0
      vendor/github.com/alibabacloud-go/darabonba-openapi/client/client.go
  43. +201
    -0
      vendor/github.com/alibabacloud-go/debug/LICENSE
  44. +12
    -0
      vendor/github.com/alibabacloud-go/debug/debug/assert.go
  45. +36
    -0
      vendor/github.com/alibabacloud-go/debug/debug/debug.go
  46. +4124
    -0
      vendor/github.com/alibabacloud-go/dysmsapi-20170525/v2/client/client.go
  47. +41
    -0
      vendor/github.com/alibabacloud-go/endpoint-util/service/service.go
  48. +201
    -0
      vendor/github.com/alibabacloud-go/openapi-util/LICENSE
  49. +635
    -0
      vendor/github.com/alibabacloud-go/openapi-util/service/service.go
  50. +462
    -0
      vendor/github.com/alibabacloud-go/tea-utils/service/service.go
  51. +52
    -0
      vendor/github.com/alibabacloud-go/tea-utils/service/util.go
  52. +105
    -0
      vendor/github.com/alibabacloud-go/tea-xml/service/service.go
  53. +201
    -0
      vendor/github.com/alibabacloud-go/tea/LICENSE
  54. +333
    -0
      vendor/github.com/alibabacloud-go/tea/tea/json_parser.go
  55. +1121
    -0
      vendor/github.com/alibabacloud-go/tea/tea/tea.go
  56. +491
    -0
      vendor/github.com/alibabacloud-go/tea/tea/trans.go
  57. +64
    -0
      vendor/github.com/alibabacloud-go/tea/utils/assert.go
  58. +109
    -0
      vendor/github.com/alibabacloud-go/tea/utils/logger.go
  59. +60
    -0
      vendor/github.com/alibabacloud-go/tea/utils/progress.go
  60. +201
    -0
      vendor/github.com/aliyun/credentials-go/LICENSE
  61. +41
    -0
      vendor/github.com/aliyun/credentials-go/credentials/access_key_credential.go
  62. +40
    -0
      vendor/github.com/aliyun/credentials-go/credentials/bearer_token_credential.go
  63. +349
    -0
      vendor/github.com/aliyun/credentials-go/credentials/credential.go
  64. +25
    -0
      vendor/github.com/aliyun/credentials-go/credentials/credential_updater.go
  65. +136
    -0
      vendor/github.com/aliyun/credentials-go/credentials/ecs_ram_role.go
  66. +43
    -0
      vendor/github.com/aliyun/credentials-go/credentials/env_provider.go
  67. +28
    -0
      vendor/github.com/aliyun/credentials-go/credentials/instance_provider.go
  68. +350
    -0
      vendor/github.com/aliyun/credentials-go/credentials/profile_provider.go
  69. +13
    -0
      vendor/github.com/aliyun/credentials-go/credentials/provider.go
  70. +32
    -0
      vendor/github.com/aliyun/credentials-go/credentials/provider_chain.go
  71. +59
    -0
      vendor/github.com/aliyun/credentials-go/credentials/request/common_request.go
  72. +53
    -0
      vendor/github.com/aliyun/credentials-go/credentials/response/common_response.go
  73. +145
    -0
      vendor/github.com/aliyun/credentials-go/credentials/rsa_key_pair_credential.go
  74. +7
    -0
      vendor/github.com/aliyun/credentials-go/credentials/session_credential.go
  75. +43
    -0
      vendor/github.com/aliyun/credentials-go/credentials/sts_credential.go
  76. +163
    -0
      vendor/github.com/aliyun/credentials-go/credentials/sts_role_arn_credential.go
  77. +35
    -0
      vendor/github.com/aliyun/credentials-go/credentials/utils/runtime.go
  78. +146
    -0
      vendor/github.com/aliyun/credentials-go/credentials/utils/utils.go
  79. +4
    -0
      vendor/github.com/clbanning/mxj/v2/.travis.yml
  80. +22
    -0
      vendor/github.com/clbanning/mxj/v2/LICENSE
  81. +201
    -0
      vendor/github.com/clbanning/mxj/v2/anyxml.go
  82. +54
    -0
      vendor/github.com/clbanning/mxj/v2/atomFeedString.xml
  83. +138
    -0
      vendor/github.com/clbanning/mxj/v2/doc.go
  84. +93
    -0
      vendor/github.com/clbanning/mxj/v2/escapechars.go
  85. +9
    -0
      vendor/github.com/clbanning/mxj/v2/exists.go
  86. +287
    -0
      vendor/github.com/clbanning/mxj/v2/files.go
  87. +2
    -0
      vendor/github.com/clbanning/mxj/v2/files_test.badjson
  88. +9
    -0
      vendor/github.com/clbanning/mxj/v2/files_test.badxml
  89. +2
    -0
      vendor/github.com/clbanning/mxj/v2/files_test.json
  90. +9
    -0
      vendor/github.com/clbanning/mxj/v2/files_test.xml
  91. +1
    -0
      vendor/github.com/clbanning/mxj/v2/files_test_dup.json
  92. +1
    -0
      vendor/github.com/clbanning/mxj/v2/files_test_dup.xml
  93. +12
    -0
      vendor/github.com/clbanning/mxj/v2/files_test_indent.json
  94. +8
    -0
      vendor/github.com/clbanning/mxj/v2/files_test_indent.xml
  95. +3
    -0
      vendor/github.com/clbanning/mxj/v2/go.mod
  96. +35
    -0
      vendor/github.com/clbanning/mxj/v2/gob.go
  97. +323
    -0
      vendor/github.com/clbanning/mxj/v2/json.go
  98. +668
    -0
      vendor/github.com/clbanning/mxj/v2/keyvalues.go
  99. +112
    -0
      vendor/github.com/clbanning/mxj/v2/leafnode.go
  100. +86
    -0
      vendor/github.com/clbanning/mxj/v2/misc.go

+ 11
- 3
go.mod View File

@@ -22,14 +22,21 @@ 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-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
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
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
@@ -56,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
@@ -112,9 +120,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 +134,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


+ 54
- 0
go.sum View File

@@ -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=
@@ -170,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=
@@ -369,6 +402,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 +466,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 +709,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 +745,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 +761,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 +802,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,14 +843,19 @@ 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=
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=
@@ -848,6 +896,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 +917,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 +984,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 +1064,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=


+ 8
- 0
models/login_source.go View File

@@ -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 {


+ 47
- 0
models/user.go View File

@@ -184,6 +184,8 @@ type User struct {
//Wechat
WechatOpenId string `xorm:"INDEX"`
WechatBindUnix timeutil.TimeStamp
//Mobile phone
PhoneNumber string `xorm:"UNIQUE"`
}

// SearchOrganizationsOptions options to filter organizations
@@ -1442,6 +1444,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 {
@@ -1610,6 +1637,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 {


+ 45
- 1
modules/auth/user_form.go View File

@@ -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"`
}
@@ -105,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
@@ -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,43 @@ 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)"`
Mode int `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 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"`
}

func (f *SlideImageForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}

+ 4
- 0
modules/context/auth.go View File

@@ -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 setting.PhoneService.Enabled && 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
}

if ctx.User.MustChangePassword {


+ 61
- 0
modules/phone/phone.go View File

@@ -0,0 +1,61 @@
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{}
_, _err = client.SendSmsWithOptions(sendSmsRequest, runtime)
return _err
}

+ 43
- 0
modules/public/dynamic.go View File

@@ -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)
}

+ 29
- 0
modules/public/static.go View File

@@ -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))


+ 60
- 0
modules/redis/redis_client/client.go View File

@@ -41,6 +41,66 @@ 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 SET(conn redis.Conn, key, value string, seconds int) (bool, error) {
reply, err := conn.Do("SETEX", key, seconds, value)
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()


+ 47
- 0
modules/setting/phone.go View File

@@ -0,0 +1,47 @@
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() {
PhoneService = &Phone{
Enabled: 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")
}

+ 1
- 0
modules/setting/setting.go View File

@@ -1519,4 +1519,5 @@ func NewServices() {
newIndexerService()
newTaskService()
NewQueueService()
newPhoneService()
}

+ 12
- 0
modules/setting/slideimage.go View File

@@ -0,0 +1,12 @@
package setting

import (
"image"
)

var (
// the original images for generate slide image
SlideImagesBg []*image.Image
SlideMaskImage *image.Image
SlideImagesCount int
)

+ 308
- 0
modules/slideimage/slideimage.go View File

@@ -0,0 +1,308 @@
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 true
}
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")

}
}

+ 38
- 0
options/locale/locale_en-US.ini View File

@@ -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
@@ -335,11 +336,16 @@ resent_limit_prompt = You have already requested an activation email recently. P
has_unconfirmed_mail = Hi %s, you have an unconfirmed email address (<b>%s</b>). 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
please_enter_main_email=Please enter your primary email
please_enter_main_email_tips=Tips: Only the primary email can receive the recovery email.
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
@@ -374,6 +380,37 @@ 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, 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.
max_times=One phone number can not send verification 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 verification 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
modify_phone_number=Modify phone number


[mail]
activate_account = Please activate your account
@@ -2495,6 +2532,7 @@ users.new_account = Create User Account
users.name = Username
users.full_name = Full Name
users.activated = Activated
users.bind_phone = Bind Phone
users.admin = Admin
users.restricted = Restricted
users.repos = Repos


+ 39
- 1
options/locale/locale_zh-CN.ini View File

@@ -198,6 +198,7 @@ no_reply_address_helper=具有隐藏电子邮件地址的用户的域名。例

[home]
uname_holder=登录名或电子邮箱地址
login_uname_holder=用户名/邮箱/手机号
uname_holder_cloud_brain=云脑登录名
password_holder=密码
switch_dashboard_context=切换控制面板用户
@@ -339,11 +340,16 @@ resent_limit_prompt=您请求发送激活邮件过于频繁,请等待 3 分钟
has_unconfirmed_mail=%s 您好,系统检测到您有一封发送至 <b>%s</b> 但未被确认的邮件。如果您未收到激活邮件,或需要重新发送,请单击下方的按钮。
resend_mail=单击此处重新发送确认邮件
email_not_associate=您输入的邮箱地址未被关联到任何帐号!
send_reset_mail=发送账户恢复邮件
email_not_main=电子邮箱地址不正确,请输入您设置的主要邮箱地址。
email_not_right=您输入了不存在的邮箱地址,请输入正确的邮箱地址。
send_reset_mail=发送密码找回邮件
please_enter_main_email=请输入接收通知提醒的主要邮箱地址
please_enter_main_email_tips=说明:如果您设置了多个邮箱地址,只有主要邮箱可以收到密码找回邮件,其他邮箱无法收到。
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=验证
@@ -378,6 +384,37 @@ 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=绑定手机号失败,请稍后再试。
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用户登录
modify_phone_number=修改手机号


[mail]
activate_account=请激活您的帐户
@@ -2510,6 +2547,7 @@ users.new_account=创建新帐户
users.name=用户名
users.full_name=全名
users.activated=已激活
users.bind_phone=手机验证
users.admin=管理员
users.restricted=受限
users.repos=项目数


BIN
public/img/slide/bg/1.png View File

Before After
Width: 391  |  Height: 196  |  Size: 168 KiB

BIN
public/img/slide/bg/2.png View File

Before After
Width: 391  |  Height: 196  |  Size: 113 KiB

BIN
public/img/slide/bg/3.png View File

Before After
Width: 391  |  Height: 196  |  Size: 160 KiB

BIN
public/img/slide/bg/4.png View File

Before After
Width: 391  |  Height: 196  |  Size: 159 KiB

BIN
public/img/slide/mask/mask.png View File

Before After
Width: 51  |  Height: 51  |  Size: 199 B

+ 4
- 0
routers/home.go View File

@@ -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 setting.PhoneService.Enabled && 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)
}


+ 3
- 0
routers/init.go View File

@@ -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)
}


+ 14
- 0
routers/routes/routes.go View File

@@ -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,
@@ -368,6 +374,9 @@ func RegisterRoutes(m *macaron.Macaron) {
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).
@@ -407,6 +416,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)
@@ -486,6 +499,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",bindIgnErr(auth.ResetPassWordByPhoneForm{}), user.ResetPasswdByPhonePost)
m.Get("/forgot_password", user.ForgotPasswd)
m.Post("/forgot_password", user.ForgotPasswdPost)
m.Post("/logout", user.SignOut)


+ 285
- 23
routers/user/auth.go View File

@@ -8,9 +8,19 @@ package user
import (
"errors"
"fmt"
"github.com/gomodule/redigo/redis"
"net/http"
"strconv"
"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,16 +48,18 @@ 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
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.
@@ -176,6 +188,49 @@ 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["PageIsPhoneLogin"] = true

ctx.HTML(200, tplSignInPhone)
}

func SignInPhonePost(ctx *context.Context, form auth.PhoneNumberCodeForm) {
ctx.Data["Title"] = ctx.Tr("sign_in")
ctx.Data["PageIsPhoneLogin"] = true
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)
return
}

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)
}

func SignInPostAPI(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("sign_in")
UserName := ctx.Query("UserName")
@@ -1252,11 +1307,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 {
@@ -1427,18 +1492,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
@@ -1454,12 +1531,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
}

@@ -1647,6 +1728,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")
@@ -1709,3 +1839,135 @@ 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 form.Mode != 2 {
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.Mode==0 { //注册

if has {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.already_register")))
return
}
} else { //手机号验证码登录 mode=1 忘记密码 mode=3
if !has {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.not_register")))
return
}

}

} 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()

sendTimes, err := phoneService.GetPhoneNumberSendTimes(redisConn, phoneNumber)
if err != nil && err!=redis.ErrNil {
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", strconv.Itoa(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)

}

+ 14
- 0
routers/user/setting/profile.go View File

@@ -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 && 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)
} 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


+ 93
- 0
services/phone/phone.go View File

@@ -0,0 +1,93 @@
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.SET(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 {
err = redis_client.HINCRBY(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 {
if value == "" {
return false
}
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()
}

+ 2
- 0
templates/admin/user/list.tmpl View File

@@ -20,6 +20,7 @@
<th>{{.i18n.Tr "admin.users.name"}}</th>
<th>{{.i18n.Tr "email"}}</th>
<th>{{.i18n.Tr "admin.users.activated"}}</th>
<th>{{.i18n.Tr "admin.users.bind_phone"}}</th>
<th>{{.i18n.Tr "admin.users.admin"}}</th>
<th>{{.i18n.Tr "admin.users.restricted"}}</th>
<th>{{.i18n.Tr "admin.users.repos"}}</th>
@@ -35,6 +36,7 @@
<td><a href="{{AppSubUrl}}/{{.Name}}">{{.Name}}</a></td>
<td><span class="text truncate email">{{.Email}}</span></td>
<td><i class="fa fa{{if .IsActive}}-check{{end}}-square-o"></i></td>
<td><i class="fa fa{{if .PhoneNumber}}-check{{end}}-square-o"></i></td>
<td><i class="fa fa{{if .IsAdmin}}-check{{end}}-square-o"></i></td>
<td><i class="fa fa{{if .IsRestricted}}-check{{end}}-square-o"></i></td>
<td>{{.NumRepos}}</td>


+ 79
- 0
templates/user/auth/bind_phone.tmpl View File

@@ -0,0 +1,79 @@
{{template "base/head" .}}
<div class="user bindphone forgot password" style="margin-top: 20px;">
<div class="ui middle very relaxed page grid">
<div class="column">
<form class="ui form ignore-dirty" action="" method="">
{{.CsrfTokenHtml}}
<h2 class="ui top attached header">
{{.i18n.Tr "phone.please_bind_your_mobile_number"}}
</h2>
<div class="ui attached segment">
{{template "base/alert" .}}
<div style="display:none;" class="ui negative message">
<p></p>
</div>
{{if .EnablePhone }}
<div style="display:flex;justify-content:center;">
<div class="use-type" usetype="2" autofocus="true" style="width:491px;" showlabel="true" >
{{template "user/auth/phone_verify" .}}
</div>
</div>
<style>
.use-type ._label-c {
display: flex;
}
</style>
{{end}}
<div class="ui divider"></div>
<div class="inline field">
<label></label>
<button class="ui blue button">{{.i18n.Tr "phone.submit"}}</button>
</div>
</div>
</form>
<script>
(function() {
window.addEventListener('load', function () {
var bindPhoneEl = $('.bindphone');
bindPhoneEl.find('button.button').off('click').on('click', function(e) {
var phoneNumber = bindPhoneEl.find('input.phoneNumber').val();
var verifyCode = bindPhoneEl.find('input.verifyCode').val();
if (phoneNumber && verifyCode) {
e.preventDefault();
if (!/^1[3578]\d{9}$/.test(phoneNumber)) {
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({{.i18n.Tr "phone.please_enter_the_correct_mobile_phone_verification_code"}});
return;
}
$.ajax({
url: '/bindPhone',
type: 'post',
dataType: 'json',
data: {
_csrf: bindPhoneEl.find('input[name="_csrf"]').val(),
phone_number: phoneNumber,
verify_code: verifyCode
},
success: function(res) {
if (res && res.Code === 0) {
window.location.href = '/dashboard';
} else {
bindPhoneEl.find('.ui.negative.message').show().find('p').text(res.Message);
}
},
error: function(err) {
console.log(err);
}
});
}
});
});
})();
</script>
</div>
</div>
</div>
{{template "base/footer" .}}

+ 12
- 1
templates/user/auth/forgot_passwd.tmpl View File

@@ -1,4 +1,14 @@
{{template "base/head" .}}
<div class="ui secondary pointing tabular top attached borderless menu new-menu navbar">
<a class="active item" rel="nofollow" href="{{AppSubUrl}}/user/forgot_password">
{{.i18n.Tr "phone.email_retrieve_password"}}
</a>
{{if .EnablePhone }}
<a class="item" rel="nofollow" href="{{AppSubUrl}}/user/forgot_password?type=phone">
{{.i18n.Tr "phone.mobile_number_retrieve_password"}}
</a>
{{end}}
</div>
<div class="user forgot password">
<div class="ui middle very relaxed page grid">
<div class="column">
@@ -14,8 +24,9 @@
{{else if .IsResetRequest}}
<div class="required inline field {{if .Err_Email}}error{{end}}">
<label for="email">{{.i18n.Tr "email"}}</label>
<input id="email" name="email" type="email" value="{{.Email}}" autofocus required>
<input id="email" name="email" type="email" value="{{.Email}}" autofocus required placeholder="{{.i18n.Tr "auth.please_enter_main_email"}}">
</div>
<div style="text-align:center;">{{.i18n.Tr "auth.please_enter_main_email_tips"}}</div>
<div class="ui divider"></div>
<div class="inline field">
<label></label>


+ 45
- 0
templates/user/auth/forgot_passwd_phone.tmpl View File

@@ -0,0 +1,45 @@
{{template "base/head" .}}
<div class="ui secondary pointing tabular top attached borderless menu new-menu navbar">
<a class="item" rel="nofollow" href="{{AppSubUrl}}/user/forgot_password">
{{.i18n.Tr "phone.email_retrieve_password"}}
</a>
<a class="active item" rel="nofollow" href="{{AppSubUrl}}/user/forgot_password?type=phone">
{{.i18n.Tr "phone.mobile_number_retrieve_password"}}
</a>
</div>
<div class="user forgot password">
<div class="ui middle very relaxed page grid">
<div class="column">
<form class="ui form ignore-dirty" action="{{AppSubUrl}}/user/recover_account_by_phone" method="post">
{{.CsrfTokenHtml}}
<h2 class="ui top attached header">
{{.i18n.Tr "auth.forgot_password_title"}}
</h2>
<div class="ui attached segment">
{{template "base/alert" .}}
<div style="display:none;" class="ui negative message">
<p></p>
</div>
{{if .EnablePhone }}
<div style="display:flex;justify-content:center;">
<div class="use-type" usetype="3" style="width:491px;" showlabel="true" shownewpwd="true" autofocus="true">
{{template "user/auth/phone_verify" .}}
</div>
</div>
<style>
.use-type ._label-c, .use-type .new-pass-word-wrap {
display: flex;
}
</style>
{{end}}
<div class="ui divider"></div>
<div class="inline field">
<label></label>
<button class="ui blue button">{{.i18n.Tr "phone.submit"}}</button>
</div>
</div>
</form>
</div>
</div>
</div>
{{template "base/footer" .}}

+ 75
- 0
templates/user/auth/phone_verify.tmpl View File

@@ -0,0 +1,75 @@
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/_phoneverify.css?v={{MD5 AppVer}}" />
<div class="__phone-verify-code">
<div class="phone-c">
<div class="phone-label _label-c required">
<span class="_label">{{.i18n.Tr "phone.phone_number"}}</span>
</div>
<div class="phone-area-c">
<select value="+86">
<option value="+86">+86</option>
</select>
</div>
<div class="field phone-num-c">
<input class="phoneNumber" style="width:100% !important" name="phone_number" value="{{.phone_number}}" placeholder="{{.i18n.Tr "phone.phone_number"}}" required autocomplete="off" />
<div class="modify-phone-number"><div style="display:flex;align-items:center;height:100%;"><a>{{.i18n.Tr "phone.modify_phone_number"}}</a></div></div>
</div>
</div>
<div class="slide-bar-wrap">
<div class="slide-bar-label _label-c required" style=""></div>
<div class="slide-bar-c" style="flex:1;">
<div class="slide-bar-bg">
<div class="slide-txt">{{.i18n.Tr "phone.drag_the_slider_to_fill_the_puzzle"}}</div>
<div class="slide-bar"></div>
<div class="slide-trigger">
<i class="arrow right icon"></i>
<i class="check icon" style="display:none;"></i>
<i class="close icon" style="display:none;"></i>
</div>
</div>
<div class="slide-image-big">
<div class="slide-image-small" style="top:{{.SlideImageInfo.ImageY}}px"></div>
</div>
</div>
</div>
<div class="verify-code-c">
<div class="verify-code-label _label-c required">
<span class="_label">{{.i18n.Tr "phone.mobile_phone_verification_code"}}</span>
</div>
<div class="verify-code-num-c">
<input class="verifyCode" style="width:100% !important" name="verify_code" value="{{.verify_code}}" placeholder="{{.i18n.Tr "phone.please_enter_SMS_verification_code"}}" required autocomplete="off" />
</div>
<div class="verify-code-send">
<div class="verify-code-send-btn __disabled">{{.i18n.Tr "phone.get_verification_code"}}</div>
</div>
</div>
<div class="new-pass-word-wrap">
<div class="new-pass-word-label _label-c required">
<span class="_label">{{.i18n.Tr "phone.new_login_password"}}</span>
</div>
<div class="new-pass-word-c">
<input class="newPassword" style="width:100% !important" name="password" type="password" placeholder="{{.i18n.Tr "phone.please_enter_new_password"}}" required autocomplete="off" />
</div>
</div>
<div class="new-pass-word-wrap">
<div class="new-pass-word-label _label-c required"></div>
<div class="field" style="flex:1;">
<div class="ui checkbox">
<label>{{.i18n.Tr "auth.remember_me"}}</label>
<input name="remember" type="checkbox">
</div>
</div>
</div>
</div>
<script src="{{StaticUrlPrefix}}/js/phoneverify.js?v={{MD5 AppVer}}" type="text/javascript"></script>
<script>
(function(){
window.addEventListener('load', function () {
var phoneVerifyCode = new PhoneVerifyCode($('.__phone-verify-code'), {
Lang: {
second_resend: {{.i18n.Tr "phone.second_resend"}},
get_verification_code: {{.i18n.Tr "phone.get_verification_code"}},
},
});
});
})();
</script>

+ 2
- 2
templates/user/auth/signin_inner.tmpl View File

@@ -28,7 +28,7 @@
</div>

<div class="ui grid">
<div class="column">
<div class="column">
{{if .IsCourse}}
<form class="ui form" action="{{.SignInLink}}?course=true" method="post">
{{else}}
@@ -38,7 +38,7 @@
<div class="field">
<div class="ui left icon input {{if and (.Err_UserName) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn))}}error{{end}}">
<i class="user icon"></i>
<input id="user_name" name="user_name" value="{{.user_name}}" placeholder="{{.i18n.Tr "home.uname_holder"}}" autofocus required>
<input id="user_name" name="user_name" value="{{.user_name}}" placeholder="{{.i18n.Tr "home.login_uname_holder"}}" autofocus required>
</div>
</div>
{{if or (not .DisablePassword) .LinkAccountMode}}


+ 7
- 2
templates/user/auth/signin_navbar.tmpl View File

@@ -1,10 +1,15 @@
{{if or .EnableOpenIDSignIn .EnableSSPI .EnableCloudBrain}}
<div class="ui secondary pointing tabular top attached borderless menu new-menu navbar">
{{if .EnablePhone }}
<a class="{{if .PageIsPhoneLogin}}active{{end}} item" rel="nofollow" href="{{AppSubUrl}}/user/login/phone">
{{.i18n.Tr "phone.mobile_login"}}
</a>
{{end}}
<a class="{{if .PageIsLogin}}active{{end}} item" rel="nofollow" href="{{AppSubUrl}}/user/login">
{{.i18n.Tr "auth.login_userpass"}}
{{.i18n.Tr "phone.account_password_login"}}
</a>
<a class="{{if .PageIsCloudBrainLogin}}active{{end}} item" rel="nofollow" href="{{AppSubUrl}}/user/login/cloud_brain">
{{.i18n.Tr "auth.login_cloudbrain"}}
{{.i18n.Tr "phone.cloud_brain_user_login"}}
</a>
{{if .EnableOpenIDSignIn}}
<a class="{{if .PageIsLoginOpenID}}active{{end}} item" rel="nofollow" href="{{AppSubUrl}}/user/login/openid">


+ 73
- 0
templates/user/auth/signin_phone.tmpl View File

@@ -0,0 +1,73 @@
{{template "base/head" .}}
<div class="user signin">
{{template "user/auth/signin_navbar" .}}
<div class="ui container">
<div class="ui raised very padded text container segment">
<style>
.full.height{background-color: #F9F9F9;}
.ui.left:not(.action){ float:none;}
.ui.left{ float:none;}
.ui.secondary.pointing.menu{ border-bottom:none;}
</style>
{{template "base/alert" .}}
<div class="ui negative message" style="display:none;">
<p></p>
</div>
<div class="ui centered grid">
<div class="sixteen wide mobile ten wide tablet ten wide computer column">
<div class="ui bottom aligned two column grid">
<div class="column">
<h2 class="ui header">
{{if .LinkAccountMode}}
{{.i18n.Tr "auth.oauth_signin_title"}}
{{else}}
{{.i18n.Tr "auth.login_userpass"}}
{{end}}
</h2>
</div>
{{if .ShowRegistrationButton}}
<div class="ui right floated column">
<a href="{{AppSubUrl}}/user/sign_up">{{.i18n.Tr "auth.sign_up_now" | Str2html}}</a>
</div>
{{end}}
</div>
<div class="ui grid">
<div class="column">
<form class="ui form" action="/user/login/phone" method="post">
{{.CsrfTokenHtml}}

{{if .EnablePhone }}
<div class="use-type" usetype="1" autofocus="true">
{{template "user/auth/phone_verify" .}}
</div>
{{end}}

<div class="two fields inline">
<div class="field">
<div class="ui checkbox">
<label>{{.i18n.Tr "auth.remember_me"}}</label>
<input name="remember" type="checkbox">
</div>
</div>
<div class="field" style="padding-right: 0; text-align: right;">
<a href="{{AppSubUrl}}/user/forgot_password?type=phone">{{.i18n.Tr "auth.forgot_password"}}</a>
</div>
</div>

<div class="ui hidden divider"></div>

<div class="center aligned field">
<button class="fluid large ui blue button">
{{.i18n.Tr "sign_in"}}
</button>
</div>
</form>
</div>
</div>
</div>
</div>

</div>
</div>
</div>
{{template "base/footer" .}}

+ 11
- 3
templates/user/auth/signup_inner.tmpl View File

@@ -7,7 +7,7 @@
<div class="ui centered grid">
<div class="sixteen wide mobile ten wide tablet ten wide computer column">
<div class="ui bottom aligned two column grid">
<div class="column">
<div class="column">
<h2 class="ui header">
{{if .LinkAccountMode}}
{{.i18n.Tr "auth.oauth_signup_title"}}
@@ -23,13 +23,15 @@
{{end}}
</div>
<div class="ui grid">
<div class="column">
<div class="column">
<form class="ui form" action="{{.SignUpLink}}" method="post">
{{.CsrfTokenHtml}}
{{if or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister)}}
{{template "base/alert" .}}
{{end}}
<div class="ui negative message" style="display:none;">
<p></p>
</div>
{{if .DisableRegistration}}
<p>{{.i18n.Tr "auth.disable_register_prompt"}}</p>
{{else}}
@@ -64,6 +66,12 @@
</div>
{{end}}
{{if .EnablePhone }}
<div class="use-type" usetype="0">
{{template "user/auth/phone_verify" .}}
</div>
{{end}}

<div class="ui hidden divider"></div>

<div class="center aligned field">


+ 1
- 1
templates/user/settings/organization.tmpl View File

@@ -1,5 +1,5 @@
{{template "base/head" .}}
<div class="user settings organization">
<div class="user settings organization" style="padding-top:15px;">
{{template "user/settings/navbar" .}}
<div class="ui container">
{{template "base/alert" .}}


+ 16
- 1
templates/user/settings/profile.tmpl View File

@@ -4,6 +4,9 @@
<div class="alert" style="top: 0;"></div>
<div class="ui container">
{{template "base/alert" .}}
<div style="display:none;" class="ui negative message">
<p></p>
</div>
<h4 class="ui top attached header">
{{.i18n.Tr "settings.public_profile"}}
</h4>
@@ -29,10 +32,22 @@
</div>
<div class="inline field">
<div class="ui checkbox" id="keep-email-private">
<label class="poping up" data-content="{{.i18n.Tr "settings.keep_email_private_popup"}}"><strong>{{.i18n.Tr "settings.keep_email_private"}}</strong></label>
<label class="poping up" data-content="{{.i18n.Tr "settings.keep_email_private_popup"}}"><strong style="font-weight:500;">{{.i18n.Tr "settings.keep_email_private"}}</strong></label>
<input name="keep_email_private" type="checkbox" {{if .SignedUser.KeepEmailPrivate}}checked{{end}}>
</div>
</div>
{{if .EnablePhone }}
<div class="field required" style="width:391px;">
<label for="phone">{{.i18n.Tr "phone.phone_number"}}</label>
<div class="use-type" usetype="2" ophonenumber="{{.SignedUser.PhoneNumber}}" readonly="true" verifycodenorequired="true">
{{template "user/auth/phone_verify" .}}
</div>
</div>
<style>
.use-type .modify-phone-number { display: flex;}
.use-type .slide-bar-wrap, .use-type .verify-code-c{ display: none; }
</style>
{{end}}
<div class="field {{if .Err_Description}}error{{end}}">
<label for="description">{{$.i18n.Tr "user.user_bio"}}</label>
<textarea id="description" name="description" rows="2" maxlength="255">{{.SignedUser.Description}}</textarea>


+ 201
- 0
vendor/github.com/alibabacloud-go/alibabacloud-gateway-spi/LICENSE View File

@@ -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.

+ 305
- 0
vendor/github.com/alibabacloud-go/alibabacloud-gateway-spi/client/client.go View File

@@ -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!")
}

+ 201
- 0
vendor/github.com/alibabacloud-go/darabonba-openapi/LICENSE View File

@@ -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.

+ 1623
- 0
vendor/github.com/alibabacloud-go/darabonba-openapi/client/client.go View File

@@ -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
}

+ 201
- 0
vendor/github.com/alibabacloud-go/debug/LICENSE View File

@@ -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.

+ 12
- 0
vendor/github.com/alibabacloud-go/debug/debug/assert.go View File

@@ -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)
}
}

+ 36
- 0
vendor/github.com/alibabacloud-go/debug/debug/debug.go View File

@@ -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...))
}
}
}

+ 4124
- 0
vendor/github.com/alibabacloud-go/dysmsapi-20170525/v2/client/client.go
File diff suppressed because it is too large
View File


+ 41
- 0
vendor/github.com/alibabacloud-go/endpoint-util/service/service.go View File

@@ -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("<product><suffix><network>.<region_id>.aliyuncs.com",
"<region_id>", tea.StringValue(regionId), 1))
} else {
_result = tea.String("<product><suffix><network>.aliyuncs.com")
}
_result = tea.String(strings.Replace(tea.StringValue(_result),
"<product>", strings.ToLower(tea.StringValue(product)), 1))
if tea.StringValue(network) == "" || tea.StringValue(network) == "public" {
_result = tea.String(strings.Replace(tea.StringValue(_result), "<network>", "", 1))
} else {
_result = tea.String(strings.Replace(tea.StringValue(_result),
"<network>", "-"+tea.StringValue(network), 1))
}
if tea.StringValue(suffix) == "" {
_result = tea.String(strings.Replace(tea.StringValue(_result), "<suffix>", "", 1))
} else {
_result = tea.String(strings.Replace(tea.StringValue(_result),
"<suffix>", "-"+tea.StringValue(suffix), 1))
}
return _result, nil
}

+ 201
- 0
vendor/github.com/alibabacloud-go/openapi-util/LICENSE View File

@@ -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.

+ 635
- 0
vendor/github.com/alibabacloud-go/openapi-util/service/service.go View File

@@ -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)
}

+ 462
- 0
vendor/github.com/alibabacloud-go/tea-utils/service/service.go View File

@@ -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
}

+ 52
- 0
vendor/github.com/alibabacloud-go/tea-utils/service/util.go View File

@@ -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)
}
}

+ 105
- 0
vendor/github.com/alibabacloud-go/tea-xml/service/service.go View File

@@ -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 += `</` + key + `>`
default:
if fmt.Sprintf("%v", v) != `<nil>` {
res += `<` + key + `>`
res += fmt.Sprintf("%v", v)
res += `</` + key + `>`
}
}
}
case map[string]interface{}:
res += `<` + key + `>`
res += mapToXML(value.(map[string]interface{}))
res += `</` + key + `>`
default:
if fmt.Sprintf("%v", value) != `<nil>` {
res += `<` + key + `>`
res += fmt.Sprintf("%v", value)
res += `</` + key + `>`
}
}
}
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
}

+ 201
- 0
vendor/github.com/alibabacloud-go/tea/LICENSE View File

@@ -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.

+ 333
- 0
vendor/github.com/alibabacloud-go/tea/tea/json_parser.go View File

@@ -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")
}
}

+ 1121
- 0
vendor/github.com/alibabacloud-go/tea/tea/tea.go View File

@@ -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)))
}

+ 491
- 0
vendor/github.com/alibabacloud-go/tea/tea/trans.go View File

@@ -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
}

+ 64
- 0
vendor/github.com/alibabacloud-go/tea/utils/assert.go View File

@@ -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)
}
}
}

+ 109
- 0
vendor/github.com/alibabacloud-go/tea/utils/logger.go View File

@@ -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")
}

+ 60
- 0
vendor/github.com/alibabacloud-go/tea/utils/progress.go View File

@@ -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
}

+ 201
- 0
vendor/github.com/aliyun/credentials-go/LICENSE View File

@@ -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.

+ 41
- 0
vendor/github.com/aliyun/credentials-go/credentials/access_key_credential.go View File

@@ -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")
}

+ 40
- 0
vendor/github.com/aliyun/credentials-go/credentials/bearer_token_credential.go View File

@@ -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")
}

+ 349
- 0
vendor/github.com/aliyun/credentials-go/credentials/credential.go View File

@@ -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
}

+ 25
- 0
vendor/github.com/aliyun/credentials-go/credentials/credential_updater.go View File

@@ -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)
}

+ 136
- 0
vendor/github.com/aliyun/credentials-go/credentials/ecs_ram_role.go View File

@@ -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
}

+ 43
- 0
vendor/github.com/aliyun/credentials-go/credentials/env_provider.go View File

@@ -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
}

+ 28
- 0
vendor/github.com/aliyun/credentials-go/credentials/instance_provider.go View File

@@ -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
}

+ 350
- 0
vendor/github.com/aliyun/credentials-go/credentials/profile_provider.go View File

@@ -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
}

+ 13
- 0
vendor/github.com/aliyun/credentials-go/credentials/provider.go View File

@@ -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)
}

+ 32
- 0
vendor/github.com/aliyun/credentials-go/credentials/provider_chain.go View File

@@ -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")

}

+ 59
- 0
vendor/github.com/aliyun/credentials-go/credentials/request/common_request.go View File

@@ -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
}

+ 53
- 0
vendor/github.com/aliyun/credentials-go/credentials/response/common_response.go View File

@@ -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
}

+ 145
- 0
vendor/github.com/aliyun/credentials-go/credentials/rsa_key_pair_credential.go View File

@@ -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
}

+ 7
- 0
vendor/github.com/aliyun/credentials-go/credentials/session_credential.go View File

@@ -0,0 +1,7 @@
package credentials

type sessionCredential struct {
AccessKeyId string
AccessKeySecret string
SecurityToken string
}

+ 43
- 0
vendor/github.com/aliyun/credentials-go/credentials/sts_credential.go View File

@@ -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")
}

+ 163
- 0
vendor/github.com/aliyun/credentials-go/credentials/sts_role_arn_credential.go View File

@@ -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
}

+ 35
- 0
vendor/github.com/aliyun/credentials-go/credentials/utils/runtime.go View File

@@ -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)
}
}

+ 146
- 0
vendor/github.com/aliyun/credentials-go/credentials/utils/utils.go View File

@@ -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)
}

+ 4
- 0
vendor/github.com/clbanning/mxj/v2/.travis.yml View File

@@ -0,0 +1,4 @@
language: go

go:
- 1.x

+ 22
- 0
vendor/github.com/clbanning/mxj/v2/LICENSE View File

@@ -0,0 +1,22 @@
Copyright (c) 2012-2021 Charles Banning <clbanning@gmail.com>. 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.


+ 201
- 0
vendor/github.com/clbanning/mxj/v2/anyxml.go View File

@@ -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:
<mydoc>
<somekey>somevalue</somekey>
<element>string</element>
<element>3.14159265</element>
<element>true</element>
</mydoc>

An extreme example is available in examples/goofy_map.go.
*/
// Alternative values for DefaultRootTag and DefaultElementTag can be set as:
// AnyXml( v, myRootTag, myElementTag).
func AnyXml(v interface{}, tags ...string) ([]byte, error) {
var rt, et string
if len(tags) == 1 || len(tags) == 2 {
rt = tags[0]
} else {
rt = DefaultRootTag
}
if len(tags) == 2 {
et = tags[1]
} else {
et = DefaultElementTag
}

if v == nil {
if useGoXmlEmptyElemSyntax {
return []byte("<" + rt + "></" + rt + ">"), nil
}
return []byte("<" + rt + "/>"), nil
}
if reflect.TypeOf(v).Kind() == reflect.Struct {
return xml.Marshal(v)
}

var err error
s := new(bytes.Buffer)
p := new(pretty)

var b []byte
switch v.(type) {
case []interface{}:
if _, err = s.WriteString("<" + rt + ">"); err != nil {
return nil, err
}
for _, vv := range v.([]interface{}) {
switch vv.(type) {
case map[string]interface{}:
m := vv.(map[string]interface{})
if len(m) == 1 {
for tag, val := range m {
err = marshalMapToXmlIndent(false, s, tag, val, p)
}
} else {
err = marshalMapToXmlIndent(false, s, et, vv, p)
}
default:
err = marshalMapToXmlIndent(false, s, et, vv, p)
}
if err != nil {
break
}
}
if _, err = s.WriteString("</" + rt + ">"); err != nil {
return nil, err
}
b = s.Bytes()
case map[string]interface{}:
m := Map(v.(map[string]interface{}))
b, err = m.Xml(rt)
default:
err = marshalMapToXmlIndent(false, s, rt, v, p)
b = s.Bytes()
}

return b, err
}

// Encode an arbitrary value as a pretty XML string.
// Alternative values for DefaultRootTag and DefaultElementTag can be set as:
// AnyXmlIndent( v, "", " ", myRootTag, myElementTag).
func AnyXmlIndent(v interface{}, prefix, indent string, tags ...string) ([]byte, error) {
var rt, et string
if len(tags) == 1 || len(tags) == 2 {
rt = tags[0]
} else {
rt = DefaultRootTag
}
if len(tags) == 2 {
et = tags[1]
} else {
et = DefaultElementTag
}

if v == nil {
if useGoXmlEmptyElemSyntax {
return []byte(prefix + "<" + rt + "></" + rt + ">"), nil
}
return []byte(prefix + "<" + rt + "/>"), nil
}
if reflect.TypeOf(v).Kind() == reflect.Struct {
return xml.MarshalIndent(v, prefix, indent)
}

var err error
s := new(bytes.Buffer)
p := new(pretty)
p.indent = indent
p.padding = prefix

var b []byte
switch v.(type) {
case []interface{}:
if _, err = s.WriteString("<" + rt + ">\n"); err != nil {
return nil, err
}
p.Indent()
for _, vv := range v.([]interface{}) {
switch vv.(type) {
case map[string]interface{}:
m := vv.(map[string]interface{})
if len(m) == 1 {
for tag, val := range m {
err = marshalMapToXmlIndent(true, s, tag, val, p)
}
} else {
p.start = 1 // we 1 tag in
err = marshalMapToXmlIndent(true, s, et, vv, p)
// *s += "\n"
if _, err = s.WriteString("\n"); err != nil {
return nil, err
}
}
default:
p.start = 0 // in case trailing p.start = 1
err = marshalMapToXmlIndent(true, s, et, vv, p)
}
if err != nil {
break
}
}
if _, err = s.WriteString(`</` + rt + `>`); err != nil {
return nil, err
}
b = s.Bytes()
case map[string]interface{}:
m := Map(v.(map[string]interface{}))
b, err = m.XmlIndent(prefix, indent, rt)
default:
err = marshalMapToXmlIndent(true, s, rt, v, p)
b = s.Bytes()
}

return b, err
}

+ 54
- 0
vendor/github.com/clbanning/mxj/v2/atomFeedString.xml View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-us" updated="2009-10-04T01:35:58+00:00"><title>Code Review - My issues</title><link href="http://codereview.appspot.com/" rel="alternate"></link><link href="http://codereview.appspot.com/rss/mine/rsc" rel="self"></link><id>http://codereview.appspot.com/</id><author><name>rietveld&lt;&gt;</name></author><entry><title>rietveld: an attempt at pubsubhubbub
</title><link href="http://codereview.appspot.com/126085" rel="alternate"></link><updated>2009-10-04T01:35:58+00:00</updated><author><name>email-address-removed</name></author><id>urn:md5:134d9179c41f806be79b3a5f7877d19a</id><summary type="html">
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 &amp;lt;link rel=&amp;quot;hub&amp;quot; href=&amp;quot;hub-server&amp;quot;&amp;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&amp;#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&amp;#39;s actual URL in
the link rel=&amp;quot;self&amp;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).


</summary></entry><entry><title>rietveld: correct tab handling
</title><link href="http://codereview.appspot.com/124106" rel="alternate"></link><updated>2009-10-03T23:02:17+00:00</updated><author><name>email-address-removed</name></author><id>urn:md5:0a2a4f19bb815101f0ba2904aed7c35a</id><summary type="html">
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&amp;#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.


</summary></entry></feed> `


+ 138
- 0
vendor/github.com/clbanning/mxj/v2/doc.go View File

@@ -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[<tag>:map["#text":<value>]] rather than map[<tag>:<value>].
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[<attr_label>]map[string]interface{}`values
where the `<attr_label>` value has "#text" and "#seq" keys - the "#text" key holds the
value for `<attr_label>`.
- 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:
- <ns:key>something</ns.key> 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 "<tag/>".
NOTE: the operation is not symmetric as "<tag/>" 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

+ 93
- 0
vendor/github.com/clbanning/mxj/v2/escapechars.go View File

@@ -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 '&amp;' will be re-escaped as '&amp;amp;'.
//
/*
The values are:
" &quot;
' &apos;
< &lt;
> &gt;
& &amp;
*/
//
// 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 "&amp;" that is parsed to "&amp;amp;"
// - or "&lt;" that is parsed to "&amp;lt;".
var escapechars = [][2][]byte{
{[]byte(`&`), []byte(`&amp;`)},
{[]byte(`<`), []byte(`&lt;`)},
{[]byte(`>`), []byte(`&gt;`)},
{[]byte(`"`), []byte(`&quot;`)},
{[]byte(`'`), []byte(`&apos;`)},
}

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
}
}

+ 9
- 0
vendor/github.com/clbanning/mxj/v2/exists.go View File

@@ -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
}

+ 287
- 0
vendor/github.com/clbanning/mxj/v2/files.go View File

@@ -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
}

+ 2
- 0
vendor/github.com/clbanning/mxj/v2/files_test.badjson View File

@@ -0,0 +1,2 @@
{ "this":"is", "a":"test", "file":"for", "files_test.go":"case" }
{ "with":"some", "bad":JSON, "in":"it" }

+ 9
- 0
vendor/github.com/clbanning/mxj/v2/files_test.badxml View File

@@ -0,0 +1,9 @@
<doc>
<some>test</some>
<data>for files.go</data>
</doc>
<msg>
<just>some</just>
<another>doc</other>
<for>test case</for>
</msg>

+ 2
- 0
vendor/github.com/clbanning/mxj/v2/files_test.json View File

@@ -0,0 +1,2 @@
{ "this":"is", "a":"test", "file":"for", "files_test.go":"case" }
{ "with":"just", "two":2, "JSON":"values", "true":true }

+ 9
- 0
vendor/github.com/clbanning/mxj/v2/files_test.xml View File

@@ -0,0 +1,9 @@
<doc>
<some>test</some>
<data>for files.go</data>
</doc>
<msg>
<just>some</just>
<another>doc</another>
<for>test case</for>
</msg>

+ 1
- 0
vendor/github.com/clbanning/mxj/v2/files_test_dup.json View File

@@ -0,0 +1 @@
{"a":"test","file":"for","files_test.go":"case","this":"is"}{"JSON":"values","true":true,"two":2,"with":"just"}

+ 1
- 0
vendor/github.com/clbanning/mxj/v2/files_test_dup.xml View File

@@ -0,0 +1 @@
<doc><data>for files.go</data><some>test</some></doc><msg><another>doc</another><for>test case</for><just>some</just></msg>

+ 12
- 0
vendor/github.com/clbanning/mxj/v2/files_test_indent.json View File

@@ -0,0 +1,12 @@
{
"a": "test",
"file": "for",
"files_test.go": "case",
"this": "is"
}
{
"JSON": "values",
"true": true,
"two": 2,
"with": "just"
}

+ 8
- 0
vendor/github.com/clbanning/mxj/v2/files_test_indent.xml View File

@@ -0,0 +1,8 @@
<doc>
<data>for files.go</data>
<some>test</some>
</doc><msg>
<another>doc</another>
<for>test case</for>
<just>some</just>
</msg>

+ 3
- 0
vendor/github.com/clbanning/mxj/v2/go.mod View File

@@ -0,0 +1,3 @@
module github.com/clbanning/mxj/v2

go 1.15

+ 35
- 0
vendor/github.com/clbanning/mxj/v2/gob.go View File

@@ -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
}

+ 323
- 0
vendor/github.com/clbanning/mxj/v2/json.go View File

@@ -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
}

+ 668
- 0
vendor/github.com/clbanning/mxj/v2/keyvalues.go View File

@@ -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
}

+ 112
- 0
vendor/github.com/clbanning/mxj/v2/leafnode.go View File

@@ -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]
}

+ 86
- 0
vendor/github.com/clbanning/mxj/v2/misc.go View File

@@ -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)
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save