forked from TrueCloudLab/lego
Add DNS Provider for inwx (#687)
This commit is contained in:
parent
286c44337e
commit
42d8637d87
28 changed files with 4444 additions and 1 deletions
|
@ -60,6 +60,7 @@ owners to license your work under the terms of the [MIT License](LICENSE).
|
||||||
| Go Daddy | `godaddy` | [documentation](https://developer.godaddy.com/doc/endpoint/domains) | - |
|
| Go Daddy | `godaddy` | [documentation](https://developer.godaddy.com/doc/endpoint/domains) | - |
|
||||||
| hosting.de | `hostingde` | [documentation](https://www.hosting.de/api/#dns) | - |
|
| hosting.de | `hostingde` | [documentation](https://www.hosting.de/api/#dns) | - |
|
||||||
| Internet Initiative Japan | `iij` | [documentation](http://manual.iij.jp/p2/pubapi/) | [Go client](https://github.com/iij/doapi) |
|
| Internet Initiative Japan | `iij` | [documentation](http://manual.iij.jp/p2/pubapi/) | [Go client](https://github.com/iij/doapi) |
|
||||||
|
| INWX | `inwx` | [documentation](https://www.inwx.de/en/help/apidoc) | [Go client](https://github.com/smueller18/goinwx) |
|
||||||
| Lightsail | `lightsail` | ? | [Go client](https://github.com/aws/aws-sdk-go/aws) |
|
| Lightsail | `lightsail` | ? | [Go client](https://github.com/aws/aws-sdk-go/aws) |
|
||||||
| Linode (deprecated) | `linode` | [documentation](https://www.linode.com/api/dns) | [Go client](https://github.com/timewasted/linode) |
|
| Linode (deprecated) | `linode` | [documentation](https://www.linode.com/api/dns) | [Go client](https://github.com/timewasted/linode) |
|
||||||
| Linodev4 | `linodev4` | [documentation](https://developers.linode.com/api/v4) | [Go client](https://github.com/linode/linodego) |
|
| Linodev4 | `linodev4` | [documentation](https://developers.linode.com/api/v4) | [Go client](https://github.com/linode/linodego) |
|
||||||
|
|
33
Gopkg.lock
generated
33
Gopkg.lock
generated
|
@ -191,6 +191,14 @@
|
||||||
revision = "0863d555d5198557e0bf2b61b6c59a873ab0173a"
|
revision = "0863d555d5198557e0bf2b61b6c59a873ab0173a"
|
||||||
version = "v0.11.1"
|
version = "v0.11.1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:aa3ed0a71c4e66e4ae6486bf97a3f4cab28edc78df2e50c5ad01dc7d91604b88"
|
||||||
|
name = "github.com/fatih/structs"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
|
revision = "4966fc68f5b7593aafa6cbbba2d65ec6e1416047"
|
||||||
|
version = "v1.1.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:74d9b0a7b4107b41e0ade759fac64502876f82d29fb23d77b3dd24b194ee3dd5"
|
digest = "1:74d9b0a7b4107b41e0ade759fac64502876f82d29fb23d77b3dd24b194ee3dd5"
|
||||||
name = "github.com/go-ini/ini"
|
name = "github.com/go-ini/ini"
|
||||||
|
@ -265,6 +273,14 @@
|
||||||
revision = "59fac5042749a5afb9af70e813da1dd5474f0167"
|
revision = "59fac5042749a5afb9af70e813da1dd5474f0167"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
digest = "1:ec142582cd3bb5cc29a2bc7181a6e67367b90b19f6a957ce506dcd7d1500bf95"
|
||||||
|
name = "github.com/kolo/xmlrpc"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
|
revision = "16bdd962781df9696f40cc2bab924f1a855a7f89"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:417193ba917954c4837c6fc48c6ac241b3fefd13fc0889367b4a7e43b69d582c"
|
digest = "1:417193ba917954c4837c6fc48c6ac241b3fefd13fc0889367b4a7e43b69d582c"
|
||||||
name = "github.com/ldez/go-auroradns"
|
name = "github.com/ldez/go-auroradns"
|
||||||
|
@ -297,6 +313,14 @@
|
||||||
revision = "ae18d6b8b3205b561c79e8e5f69bff09736185f4"
|
revision = "ae18d6b8b3205b561c79e8e5f69bff09736185f4"
|
||||||
version = "v1.0.0"
|
version = "v1.0.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:a45ae66dea4c899d79fceb116accfa1892105c251f0dcd9a217ddc276b42ec68"
|
||||||
|
name = "github.com/mitchellh/mapstructure"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
|
revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe"
|
||||||
|
version = "v1.1.2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:2f42fa12d6911c7b7659738758631bec870b7e9b4c6be5444f963cdcfccc191f"
|
digest = "1:2f42fa12d6911c7b7659738758631bec870b7e9b4c6be5444f963cdcfccc191f"
|
||||||
name = "github.com/modern-go/concurrent"
|
name = "github.com/modern-go/concurrent"
|
||||||
|
@ -382,6 +406,14 @@
|
||||||
revision = "3e01752db0189b9157070a0e1668a620f9a85da2"
|
revision = "3e01752db0189b9157070a0e1668a620f9a85da2"
|
||||||
version = "v1.0.6"
|
version = "v1.0.6"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
digest = "1:94fcec8ba983a96bb3f123ab9690955baf44d6d32de342f95f5a2f665c70457a"
|
||||||
|
name = "github.com/smueller18/goinwx"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
|
revision = "5d138389109eca96463f44f692408f0d1c731278"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:60a46e2410edbf02b419f833372dd1d24d7aa1b916a990a7370e792fada1eadd"
|
digest = "1:60a46e2410edbf02b419f833372dd1d24d7aa1b916a990a7370e792fada1eadd"
|
||||||
name = "github.com/stretchr/objx"
|
name = "github.com/stretchr/objx"
|
||||||
|
@ -620,6 +652,7 @@
|
||||||
"github.com/rainycape/memcache",
|
"github.com/rainycape/memcache",
|
||||||
"github.com/sacloud/libsacloud/api",
|
"github.com/sacloud/libsacloud/api",
|
||||||
"github.com/sacloud/libsacloud/sacloud",
|
"github.com/sacloud/libsacloud/sacloud",
|
||||||
|
"github.com/smueller18/goinwx",
|
||||||
"github.com/stretchr/testify/assert",
|
"github.com/stretchr/testify/assert",
|
||||||
"github.com/stretchr/testify/mock",
|
"github.com/stretchr/testify/mock",
|
||||||
"github.com/stretchr/testify/require",
|
"github.com/stretchr/testify/require",
|
||||||
|
|
|
@ -53,6 +53,10 @@
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/timewasted/linode"
|
name = "github.com/timewasted/linode"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/smueller18/goinwx"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
name = "github.com/linode/linodego"
|
name = "github.com/linode/linodego"
|
||||||
|
|
2
cli.go
2
cli.go
|
@ -227,6 +227,7 @@ Here is an example bash command using the CloudFlare DNS provider:
|
||||||
fmt.Fprintln(w, "\tgodaddy:\tGODADDY_API_KEY, GODADDY_API_SECRET")
|
fmt.Fprintln(w, "\tgodaddy:\tGODADDY_API_KEY, GODADDY_API_SECRET")
|
||||||
fmt.Fprintln(w, "\thostingde:\tHOSTINGDE_API_KEY, HOSTINGDE_ZONE_NAME")
|
fmt.Fprintln(w, "\thostingde:\tHOSTINGDE_API_KEY, HOSTINGDE_ZONE_NAME")
|
||||||
fmt.Fprintln(w, "\tiij:\tIIJ_API_ACCESS_KEY, IIJ_API_SECRET_KEY, IIJ_DO_SERVICE_CODE")
|
fmt.Fprintln(w, "\tiij:\tIIJ_API_ACCESS_KEY, IIJ_API_SECRET_KEY, IIJ_DO_SERVICE_CODE")
|
||||||
|
fmt.Fprintln(w, "\tinwx:\tINWX_USERNAME, INWX_PASSWORD")
|
||||||
fmt.Fprintln(w, "\tlightsail:\tAWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, DNS_ZONE")
|
fmt.Fprintln(w, "\tlightsail:\tAWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, DNS_ZONE")
|
||||||
fmt.Fprintln(w, "\tlinode:\tLINODE_API_KEY")
|
fmt.Fprintln(w, "\tlinode:\tLINODE_API_KEY")
|
||||||
fmt.Fprintln(w, "\tlinodev4:\tLINODE_TOKEN")
|
fmt.Fprintln(w, "\tlinodev4:\tLINODE_TOKEN")
|
||||||
|
@ -274,6 +275,7 @@ Here is an example bash command using the CloudFlare DNS provider:
|
||||||
fmt.Fprintln(w, "\tgodaddy:\tGODADDY_POLLING_INTERVAL, GODADDY_PROPAGATION_TIMEOUT, GODADDY_TTL, GODADDY_HTTP_TIMEOUT")
|
fmt.Fprintln(w, "\tgodaddy:\tGODADDY_POLLING_INTERVAL, GODADDY_PROPAGATION_TIMEOUT, GODADDY_TTL, GODADDY_HTTP_TIMEOUT")
|
||||||
fmt.Fprintln(w, "\thostingde:\tHOSTINGDE_POLLING_INTERVAL, HOSTINGDE_PROPAGATION_TIMEOUT, HOSTINGDE_TTL, HOSTINGDE_HTTP_TIMEOUT")
|
fmt.Fprintln(w, "\thostingde:\tHOSTINGDE_POLLING_INTERVAL, HOSTINGDE_PROPAGATION_TIMEOUT, HOSTINGDE_TTL, HOSTINGDE_HTTP_TIMEOUT")
|
||||||
fmt.Fprintln(w, "\tiij:\tIIJ_POLLING_INTERVAL, IIJ_PROPAGATION_TIMEOUT, IIJ_TTL")
|
fmt.Fprintln(w, "\tiij:\tIIJ_POLLING_INTERVAL, IIJ_PROPAGATION_TIMEOUT, IIJ_TTL")
|
||||||
|
fmt.Fprintln(w, "\tinwx:\tINWX_POLLING_INTERVAL, INWX_PROPAGATION_TIMEOUT, INWX_TTL, INWX_SANDBOX")
|
||||||
fmt.Fprintln(w, "\tlightsail:\tLIGHTSAIL_POLLING_INTERVAL, LIGHTSAIL_PROPAGATION_TIMEOUT")
|
fmt.Fprintln(w, "\tlightsail:\tLIGHTSAIL_POLLING_INTERVAL, LIGHTSAIL_PROPAGATION_TIMEOUT")
|
||||||
fmt.Fprintln(w, "\tlinode:\tLINODE_POLLING_INTERVAL, LINODE_TTL, LINODE_HTTP_TIMEOUT")
|
fmt.Fprintln(w, "\tlinode:\tLINODE_POLLING_INTERVAL, LINODE_TTL, LINODE_HTTP_TIMEOUT")
|
||||||
fmt.Fprintln(w, "\tlinodev4:\tLINODE_POLLING_INTERVAL, LINODE_TTL, LINODE_HTTP_TIMEOUT")
|
fmt.Fprintln(w, "\tlinodev4:\tLINODE_POLLING_INTERVAL, LINODE_TTL, LINODE_HTTP_TIMEOUT")
|
||||||
|
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"github.com/xenolf/lego/providers/dns/godaddy"
|
"github.com/xenolf/lego/providers/dns/godaddy"
|
||||||
"github.com/xenolf/lego/providers/dns/hostingde"
|
"github.com/xenolf/lego/providers/dns/hostingde"
|
||||||
"github.com/xenolf/lego/providers/dns/iij"
|
"github.com/xenolf/lego/providers/dns/iij"
|
||||||
|
"github.com/xenolf/lego/providers/dns/inwx"
|
||||||
"github.com/xenolf/lego/providers/dns/lightsail"
|
"github.com/xenolf/lego/providers/dns/lightsail"
|
||||||
"github.com/xenolf/lego/providers/dns/linode"
|
"github.com/xenolf/lego/providers/dns/linode"
|
||||||
"github.com/xenolf/lego/providers/dns/linodev4"
|
"github.com/xenolf/lego/providers/dns/linodev4"
|
||||||
|
@ -104,6 +105,8 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error)
|
||||||
return hostingde.NewDNSProvider()
|
return hostingde.NewDNSProvider()
|
||||||
case "iij":
|
case "iij":
|
||||||
return iij.NewDNSProvider()
|
return iij.NewDNSProvider()
|
||||||
|
case "inwx":
|
||||||
|
return inwx.NewDNSProvider()
|
||||||
case "lightsail":
|
case "lightsail":
|
||||||
return lightsail.NewDNSProvider()
|
return lightsail.NewDNSProvider()
|
||||||
case "linode":
|
case "linode":
|
||||||
|
|
166
providers/dns/inwx/inwx.go
Normal file
166
providers/dns/inwx/inwx.go
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
// Package inwx implements a DNS provider for solving the DNS-01 challenge using inwx dom robot
|
||||||
|
package inwx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/smueller18/goinwx"
|
||||||
|
"github.com/xenolf/lego/acme"
|
||||||
|
"github.com/xenolf/lego/log"
|
||||||
|
"github.com/xenolf/lego/platform/config/env"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config is used to configure the creation of the DNSProvider
|
||||||
|
type Config struct {
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
Sandbox bool
|
||||||
|
PropagationTimeout time.Duration
|
||||||
|
PollingInterval time.Duration
|
||||||
|
TTL int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDefaultConfig returns a default configuration for the DNSProvider
|
||||||
|
func NewDefaultConfig() *Config {
|
||||||
|
return &Config{
|
||||||
|
PropagationTimeout: env.GetOrDefaultSecond("INWX_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout),
|
||||||
|
PollingInterval: env.GetOrDefaultSecond("INWX_POLLING_INTERVAL", acme.DefaultPollingInterval),
|
||||||
|
TTL: env.GetOrDefaultInt("INWX_TTL", 300),
|
||||||
|
Sandbox: env.GetOrDefaultBool("INWX_SANDBOX", false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSProvider is an implementation of the acme.ChallengeProvider interface
|
||||||
|
type DNSProvider struct {
|
||||||
|
config *Config
|
||||||
|
client *goinwx.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDNSProvider returns a DNSProvider instance configured for Dyn DNS.
|
||||||
|
// Credentials must be passed in the environment variables:
|
||||||
|
// INWX_USERNAME and INWX_PASSWORD.
|
||||||
|
func NewDNSProvider() (*DNSProvider, error) {
|
||||||
|
values, err := env.Get("INWX_USERNAME", "INWX_PASSWORD")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("inwx: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := NewDefaultConfig()
|
||||||
|
config.Username = values["INWX_USERNAME"]
|
||||||
|
config.Password = values["INWX_PASSWORD"]
|
||||||
|
|
||||||
|
return NewDNSProviderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDNSProviderConfig return a DNSProvider instance configured for Dyn DNS
|
||||||
|
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, errors.New("inwx: the configuration of the DNS provider is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Username == "" || config.Password == "" {
|
||||||
|
return nil, fmt.Errorf("inwx: credentials missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Sandbox {
|
||||||
|
log.Infof("inwx: sandbox mode is enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
client := goinwx.NewClient(config.Username, config.Password, &goinwx.ClientOptions{Sandbox: config.Sandbox})
|
||||||
|
|
||||||
|
return &DNSProvider{config: config, client: client}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Present creates a TXT record using the specified parameters
|
||||||
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
|
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
|
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("inwx: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = d.client.Account.Login()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("inwx: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
errL := d.client.Account.Logout()
|
||||||
|
if errL != nil {
|
||||||
|
log.Infof("inwx: failed to logout: %v", errL)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
var request = &goinwx.NameserverRecordRequest{
|
||||||
|
Domain: acme.UnFqdn(authZone),
|
||||||
|
Name: acme.UnFqdn(fqdn),
|
||||||
|
Type: "TXT",
|
||||||
|
Content: value,
|
||||||
|
Ttl: d.config.TTL,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = d.client.Nameservers.CreateRecord(request)
|
||||||
|
if err != nil {
|
||||||
|
switch err.(type) {
|
||||||
|
case *goinwx.ErrorResponse:
|
||||||
|
if err.(*goinwx.ErrorResponse).Message == "Object exists" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("inwx: %v", err)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("inwx: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CleanUp removes the TXT record matching the specified parameters
|
||||||
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
|
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
|
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("inwx: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = d.client.Account.Login()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("inwx: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
errL := d.client.Account.Logout()
|
||||||
|
if errL != nil {
|
||||||
|
log.Infof("inwx: failed to logout: %v", errL)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
response, err := d.client.Nameservers.Info(&goinwx.NameserverInfoRequest{
|
||||||
|
Domain: acme.UnFqdn(authZone),
|
||||||
|
Name: acme.UnFqdn(fqdn),
|
||||||
|
Type: "TXT",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("inwx: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastErr error
|
||||||
|
for _, record := range response.Records {
|
||||||
|
err = d.client.Nameservers.DeleteRecord(record.Id)
|
||||||
|
if err != nil {
|
||||||
|
lastErr = fmt.Errorf("inwx: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timeout returns the timeout and interval to use when checking for DNS propagation.
|
||||||
|
// Adjusting here to cope with spikes in propagation times.
|
||||||
|
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||||
|
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||||
|
}
|
140
providers/dns/inwx/inwx_test.go
Normal file
140
providers/dns/inwx/inwx_test.go
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
package inwx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/xenolf/lego/platform/tester"
|
||||||
|
)
|
||||||
|
|
||||||
|
var envTest = tester.NewEnvTest(
|
||||||
|
"INWX_USERNAME",
|
||||||
|
"INWX_PASSWORD",
|
||||||
|
"INWX_SANDBOX",
|
||||||
|
"INWX_TTL").
|
||||||
|
WithDomain("INWX_DOMAIN").
|
||||||
|
WithLiveTestRequirements("INWX_USERNAME", "INWX_PASSWORD", "INWX_DOMAIN")
|
||||||
|
|
||||||
|
func TestNewDNSProvider(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
envVars map[string]string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "success",
|
||||||
|
envVars: map[string]string{
|
||||||
|
"INWX_USERNAME": "123",
|
||||||
|
"INWX_PASSWORD": "456",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "missing credentials",
|
||||||
|
envVars: map[string]string{
|
||||||
|
"INWX_USERNAME": "",
|
||||||
|
"INWX_PASSWORD": "",
|
||||||
|
},
|
||||||
|
expected: "inwx: some credentials information are missing: INWX_USERNAME,INWX_PASSWORD",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "missing username",
|
||||||
|
envVars: map[string]string{
|
||||||
|
"INWX_USERNAME": "",
|
||||||
|
"INWX_PASSWORD": "456",
|
||||||
|
},
|
||||||
|
expected: "inwx: some credentials information are missing: INWX_USERNAME",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "missing password",
|
||||||
|
envVars: map[string]string{
|
||||||
|
"INWX_USERNAME": "123",
|
||||||
|
"INWX_PASSWORD": "",
|
||||||
|
},
|
||||||
|
expected: "inwx: some credentials information are missing: INWX_PASSWORD",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
defer envTest.RestoreEnv()
|
||||||
|
envTest.ClearEnv()
|
||||||
|
|
||||||
|
envTest.Apply(test.envVars)
|
||||||
|
|
||||||
|
p, err := NewDNSProvider()
|
||||||
|
|
||||||
|
if len(test.expected) == 0 {
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, p)
|
||||||
|
require.NotNil(t, p.config)
|
||||||
|
require.NotNil(t, p.client)
|
||||||
|
} else {
|
||||||
|
require.EqualError(t, err, test.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewDNSProviderConfig(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
username string
|
||||||
|
password string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "success",
|
||||||
|
username: "123",
|
||||||
|
password: "456",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "missing credentials",
|
||||||
|
expected: "inwx: credentials missing",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
config := NewDefaultConfig()
|
||||||
|
config.Username = test.username
|
||||||
|
config.Password = test.password
|
||||||
|
|
||||||
|
p, err := NewDNSProviderConfig(config)
|
||||||
|
|
||||||
|
if len(test.expected) == 0 {
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, p)
|
||||||
|
require.NotNil(t, p.config)
|
||||||
|
require.NotNil(t, p.client)
|
||||||
|
} else {
|
||||||
|
require.EqualError(t, err, test.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLivePresentAndCleanup(t *testing.T) {
|
||||||
|
if !envTest.IsLiveTest() {
|
||||||
|
t.Skip("skipping live test")
|
||||||
|
}
|
||||||
|
|
||||||
|
envTest.RestoreEnv()
|
||||||
|
envTest.Apply(map[string]string{
|
||||||
|
"INWX_SANDBOX": "true",
|
||||||
|
"INWX_TTL": "3600", // In sandbox mode, the minimum allowed TTL is 3600
|
||||||
|
})
|
||||||
|
defer envTest.RestoreEnv()
|
||||||
|
|
||||||
|
provider, err := NewDNSProvider()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = provider.Present(envTest.GetDomain(), "", "123d==")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Verify that no error is thrown if record already exists
|
||||||
|
err = provider.Present(envTest.GetDomain(), "", "123d==")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = provider.CleanUp(envTest.GetDomain(), "", "123d==")
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
21
vendor/github.com/fatih/structs/LICENSE
generated
vendored
Normal file
21
vendor/github.com/fatih/structs/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Fatih Arslan
|
||||||
|
|
||||||
|
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.
|
141
vendor/github.com/fatih/structs/field.go
generated
vendored
Normal file
141
vendor/github.com/fatih/structs/field.go
generated
vendored
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
package structs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errNotExported = errors.New("field is not exported")
|
||||||
|
errNotSettable = errors.New("field is not settable")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Field represents a single struct field that encapsulates high level
|
||||||
|
// functions around the field.
|
||||||
|
type Field struct {
|
||||||
|
value reflect.Value
|
||||||
|
field reflect.StructField
|
||||||
|
defaultTag string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tag returns the value associated with key in the tag string. If there is no
|
||||||
|
// such key in the tag, Tag returns the empty string.
|
||||||
|
func (f *Field) Tag(key string) string {
|
||||||
|
return f.field.Tag.Get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the underlying value of the field. It panics if the field
|
||||||
|
// is not exported.
|
||||||
|
func (f *Field) Value() interface{} {
|
||||||
|
return f.value.Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmbedded returns true if the given field is an anonymous field (embedded)
|
||||||
|
func (f *Field) IsEmbedded() bool {
|
||||||
|
return f.field.Anonymous
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsExported returns true if the given field is exported.
|
||||||
|
func (f *Field) IsExported() bool {
|
||||||
|
return f.field.PkgPath == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsZero returns true if the given field is not initialized (has a zero value).
|
||||||
|
// It panics if the field is not exported.
|
||||||
|
func (f *Field) IsZero() bool {
|
||||||
|
zero := reflect.Zero(f.value.Type()).Interface()
|
||||||
|
current := f.Value()
|
||||||
|
|
||||||
|
return reflect.DeepEqual(current, zero)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the name of the given field
|
||||||
|
func (f *Field) Name() string {
|
||||||
|
return f.field.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kind returns the fields kind, such as "string", "map", "bool", etc ..
|
||||||
|
func (f *Field) Kind() reflect.Kind {
|
||||||
|
return f.value.Kind()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets the field to given value v. It returns an error if the field is not
|
||||||
|
// settable (not addressable or not exported) or if the given value's type
|
||||||
|
// doesn't match the fields type.
|
||||||
|
func (f *Field) Set(val interface{}) error {
|
||||||
|
// we can't set unexported fields, so be sure this field is exported
|
||||||
|
if !f.IsExported() {
|
||||||
|
return errNotExported
|
||||||
|
}
|
||||||
|
|
||||||
|
// do we get here? not sure...
|
||||||
|
if !f.value.CanSet() {
|
||||||
|
return errNotSettable
|
||||||
|
}
|
||||||
|
|
||||||
|
given := reflect.ValueOf(val)
|
||||||
|
|
||||||
|
if f.value.Kind() != given.Kind() {
|
||||||
|
return fmt.Errorf("wrong kind. got: %s want: %s", given.Kind(), f.value.Kind())
|
||||||
|
}
|
||||||
|
|
||||||
|
f.value.Set(given)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zero sets the field to its zero value. It returns an error if the field is not
|
||||||
|
// settable (not addressable or not exported).
|
||||||
|
func (f *Field) Zero() error {
|
||||||
|
zero := reflect.Zero(f.value.Type()).Interface()
|
||||||
|
return f.Set(zero)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fields returns a slice of Fields. This is particular handy to get the fields
|
||||||
|
// of a nested struct . A struct tag with the content of "-" ignores the
|
||||||
|
// checking of that particular field. Example:
|
||||||
|
//
|
||||||
|
// // Field is ignored by this package.
|
||||||
|
// Field *http.Request `structs:"-"`
|
||||||
|
//
|
||||||
|
// It panics if field is not exported or if field's kind is not struct
|
||||||
|
func (f *Field) Fields() []*Field {
|
||||||
|
return getFields(f.value, f.defaultTag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Field returns the field from a nested struct. It panics if the nested struct
|
||||||
|
// is not exported or if the field was not found.
|
||||||
|
func (f *Field) Field(name string) *Field {
|
||||||
|
field, ok := f.FieldOk(name)
|
||||||
|
if !ok {
|
||||||
|
panic("field not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
|
||||||
|
// FieldOk returns the field from a nested struct. The boolean returns whether
|
||||||
|
// the field was found (true) or not (false).
|
||||||
|
func (f *Field) FieldOk(name string) (*Field, bool) {
|
||||||
|
value := &f.value
|
||||||
|
// value must be settable so we need to make sure it holds the address of the
|
||||||
|
// variable and not a copy, so we can pass the pointer to strctVal instead of a
|
||||||
|
// copy (which is not assigned to any variable, hence not settable).
|
||||||
|
// see "https://blog.golang.org/laws-of-reflection#TOC_8."
|
||||||
|
if f.value.Kind() != reflect.Ptr {
|
||||||
|
a := f.value.Addr()
|
||||||
|
value = &a
|
||||||
|
}
|
||||||
|
v := strctVal(value.Interface())
|
||||||
|
t := v.Type()
|
||||||
|
|
||||||
|
field, ok := t.FieldByName(name)
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Field{
|
||||||
|
field: field,
|
||||||
|
value: v.FieldByName(name),
|
||||||
|
}, true
|
||||||
|
}
|
584
vendor/github.com/fatih/structs/structs.go
generated
vendored
Normal file
584
vendor/github.com/fatih/structs/structs.go
generated
vendored
Normal file
|
@ -0,0 +1,584 @@
|
||||||
|
// Package structs contains various utilities functions to work with structs.
|
||||||
|
package structs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// DefaultTagName is the default tag name for struct fields which provides
|
||||||
|
// a more granular to tweak certain structs. Lookup the necessary functions
|
||||||
|
// for more info.
|
||||||
|
DefaultTagName = "structs" // struct's field default tag name
|
||||||
|
)
|
||||||
|
|
||||||
|
// Struct encapsulates a struct type to provide several high level functions
|
||||||
|
// around the struct.
|
||||||
|
type Struct struct {
|
||||||
|
raw interface{}
|
||||||
|
value reflect.Value
|
||||||
|
TagName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new *Struct with the struct s. It panics if the s's kind is
|
||||||
|
// not struct.
|
||||||
|
func New(s interface{}) *Struct {
|
||||||
|
return &Struct{
|
||||||
|
raw: s,
|
||||||
|
value: strctVal(s),
|
||||||
|
TagName: DefaultTagName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map converts the given struct to a map[string]interface{}, where the keys
|
||||||
|
// of the map are the field names and the values of the map the associated
|
||||||
|
// values of the fields. The default key string is the struct field name but
|
||||||
|
// can be changed in the struct field's tag value. The "structs" key in the
|
||||||
|
// struct's field tag value is the key name. Example:
|
||||||
|
//
|
||||||
|
// // Field appears in map as key "myName".
|
||||||
|
// Name string `structs:"myName"`
|
||||||
|
//
|
||||||
|
// A tag value with the content of "-" ignores that particular field. Example:
|
||||||
|
//
|
||||||
|
// // Field is ignored by this package.
|
||||||
|
// Field bool `structs:"-"`
|
||||||
|
//
|
||||||
|
// A tag value with the content of "string" uses the stringer to get the value. Example:
|
||||||
|
//
|
||||||
|
// // The value will be output of Animal's String() func.
|
||||||
|
// // Map will panic if Animal does not implement String().
|
||||||
|
// Field *Animal `structs:"field,string"`
|
||||||
|
//
|
||||||
|
// A tag value with the option of "flatten" used in a struct field is to flatten its fields
|
||||||
|
// in the output map. Example:
|
||||||
|
//
|
||||||
|
// // The FieldStruct's fields will be flattened into the output map.
|
||||||
|
// FieldStruct time.Time `structs:",flatten"`
|
||||||
|
//
|
||||||
|
// A tag value with the option of "omitnested" stops iterating further if the type
|
||||||
|
// is a struct. Example:
|
||||||
|
//
|
||||||
|
// // Field is not processed further by this package.
|
||||||
|
// Field time.Time `structs:"myName,omitnested"`
|
||||||
|
// Field *http.Request `structs:",omitnested"`
|
||||||
|
//
|
||||||
|
// A tag value with the option of "omitempty" ignores that particular field if
|
||||||
|
// the field value is empty. Example:
|
||||||
|
//
|
||||||
|
// // Field appears in map as key "myName", but the field is
|
||||||
|
// // skipped if empty.
|
||||||
|
// Field string `structs:"myName,omitempty"`
|
||||||
|
//
|
||||||
|
// // Field appears in map as key "Field" (the default), but
|
||||||
|
// // the field is skipped if empty.
|
||||||
|
// Field string `structs:",omitempty"`
|
||||||
|
//
|
||||||
|
// Note that only exported fields of a struct can be accessed, non exported
|
||||||
|
// fields will be neglected.
|
||||||
|
func (s *Struct) Map() map[string]interface{} {
|
||||||
|
out := make(map[string]interface{})
|
||||||
|
s.FillMap(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// FillMap is the same as Map. Instead of returning the output, it fills the
|
||||||
|
// given map.
|
||||||
|
func (s *Struct) FillMap(out map[string]interface{}) {
|
||||||
|
if out == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := s.structFields()
|
||||||
|
|
||||||
|
for _, field := range fields {
|
||||||
|
name := field.Name
|
||||||
|
val := s.value.FieldByName(name)
|
||||||
|
isSubStruct := false
|
||||||
|
var finalVal interface{}
|
||||||
|
|
||||||
|
tagName, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
||||||
|
if tagName != "" {
|
||||||
|
name = tagName
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the value is a zero value and the field is marked as omitempty do
|
||||||
|
// not include
|
||||||
|
if tagOpts.Has("omitempty") {
|
||||||
|
zero := reflect.Zero(val.Type()).Interface()
|
||||||
|
current := val.Interface()
|
||||||
|
|
||||||
|
if reflect.DeepEqual(current, zero) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tagOpts.Has("omitnested") {
|
||||||
|
finalVal = s.nested(val)
|
||||||
|
|
||||||
|
v := reflect.ValueOf(val.Interface())
|
||||||
|
if v.Kind() == reflect.Ptr {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Map, reflect.Struct:
|
||||||
|
isSubStruct = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
finalVal = val.Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
if tagOpts.Has("string") {
|
||||||
|
s, ok := val.Interface().(fmt.Stringer)
|
||||||
|
if ok {
|
||||||
|
out[name] = s.String()
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if isSubStruct && (tagOpts.Has("flatten")) {
|
||||||
|
for k := range finalVal.(map[string]interface{}) {
|
||||||
|
out[k] = finalVal.(map[string]interface{})[k]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out[name] = finalVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values converts the given s struct's field values to a []interface{}. A
|
||||||
|
// struct tag with the content of "-" ignores the that particular field.
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// // Field is ignored by this package.
|
||||||
|
// Field int `structs:"-"`
|
||||||
|
//
|
||||||
|
// A value with the option of "omitnested" stops iterating further if the type
|
||||||
|
// is a struct. Example:
|
||||||
|
//
|
||||||
|
// // Fields is not processed further by this package.
|
||||||
|
// Field time.Time `structs:",omitnested"`
|
||||||
|
// Field *http.Request `structs:",omitnested"`
|
||||||
|
//
|
||||||
|
// A tag value with the option of "omitempty" ignores that particular field and
|
||||||
|
// is not added to the values if the field value is empty. Example:
|
||||||
|
//
|
||||||
|
// // Field is skipped if empty
|
||||||
|
// Field string `structs:",omitempty"`
|
||||||
|
//
|
||||||
|
// Note that only exported fields of a struct can be accessed, non exported
|
||||||
|
// fields will be neglected.
|
||||||
|
func (s *Struct) Values() []interface{} {
|
||||||
|
fields := s.structFields()
|
||||||
|
|
||||||
|
var t []interface{}
|
||||||
|
|
||||||
|
for _, field := range fields {
|
||||||
|
val := s.value.FieldByName(field.Name)
|
||||||
|
|
||||||
|
_, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
||||||
|
|
||||||
|
// if the value is a zero value and the field is marked as omitempty do
|
||||||
|
// not include
|
||||||
|
if tagOpts.Has("omitempty") {
|
||||||
|
zero := reflect.Zero(val.Type()).Interface()
|
||||||
|
current := val.Interface()
|
||||||
|
|
||||||
|
if reflect.DeepEqual(current, zero) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tagOpts.Has("string") {
|
||||||
|
s, ok := val.Interface().(fmt.Stringer)
|
||||||
|
if ok {
|
||||||
|
t = append(t, s.String())
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
|
||||||
|
// look out for embedded structs, and convert them to a
|
||||||
|
// []interface{} to be added to the final values slice
|
||||||
|
t = append(t, Values(val.Interface())...)
|
||||||
|
} else {
|
||||||
|
t = append(t, val.Interface())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fields returns a slice of Fields. A struct tag with the content of "-"
|
||||||
|
// ignores the checking of that particular field. Example:
|
||||||
|
//
|
||||||
|
// // Field is ignored by this package.
|
||||||
|
// Field bool `structs:"-"`
|
||||||
|
//
|
||||||
|
// It panics if s's kind is not struct.
|
||||||
|
func (s *Struct) Fields() []*Field {
|
||||||
|
return getFields(s.value, s.TagName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Names returns a slice of field names. A struct tag with the content of "-"
|
||||||
|
// ignores the checking of that particular field. Example:
|
||||||
|
//
|
||||||
|
// // Field is ignored by this package.
|
||||||
|
// Field bool `structs:"-"`
|
||||||
|
//
|
||||||
|
// It panics if s's kind is not struct.
|
||||||
|
func (s *Struct) Names() []string {
|
||||||
|
fields := getFields(s.value, s.TagName)
|
||||||
|
|
||||||
|
names := make([]string, len(fields))
|
||||||
|
|
||||||
|
for i, field := range fields {
|
||||||
|
names[i] = field.Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFields(v reflect.Value, tagName string) []*Field {
|
||||||
|
if v.Kind() == reflect.Ptr {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
t := v.Type()
|
||||||
|
|
||||||
|
var fields []*Field
|
||||||
|
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
field := t.Field(i)
|
||||||
|
|
||||||
|
if tag := field.Tag.Get(tagName); tag == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
f := &Field{
|
||||||
|
field: field,
|
||||||
|
value: v.FieldByName(field.Name),
|
||||||
|
}
|
||||||
|
|
||||||
|
fields = append(fields, f)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields
|
||||||
|
}
|
||||||
|
|
||||||
|
// Field returns a new Field struct that provides several high level functions
|
||||||
|
// around a single struct field entity. It panics if the field is not found.
|
||||||
|
func (s *Struct) Field(name string) *Field {
|
||||||
|
f, ok := s.FieldOk(name)
|
||||||
|
if !ok {
|
||||||
|
panic("field not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// FieldOk returns a new Field struct that provides several high level functions
|
||||||
|
// around a single struct field entity. The boolean returns true if the field
|
||||||
|
// was found.
|
||||||
|
func (s *Struct) FieldOk(name string) (*Field, bool) {
|
||||||
|
t := s.value.Type()
|
||||||
|
|
||||||
|
field, ok := t.FieldByName(name)
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Field{
|
||||||
|
field: field,
|
||||||
|
value: s.value.FieldByName(name),
|
||||||
|
defaultTag: s.TagName,
|
||||||
|
}, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsZero returns true if all fields in a struct is a zero value (not
|
||||||
|
// initialized) A struct tag with the content of "-" ignores the checking of
|
||||||
|
// that particular field. Example:
|
||||||
|
//
|
||||||
|
// // Field is ignored by this package.
|
||||||
|
// Field bool `structs:"-"`
|
||||||
|
//
|
||||||
|
// A value with the option of "omitnested" stops iterating further if the type
|
||||||
|
// is a struct. Example:
|
||||||
|
//
|
||||||
|
// // Field is not processed further by this package.
|
||||||
|
// Field time.Time `structs:"myName,omitnested"`
|
||||||
|
// Field *http.Request `structs:",omitnested"`
|
||||||
|
//
|
||||||
|
// Note that only exported fields of a struct can be accessed, non exported
|
||||||
|
// fields will be neglected. It panics if s's kind is not struct.
|
||||||
|
func (s *Struct) IsZero() bool {
|
||||||
|
fields := s.structFields()
|
||||||
|
|
||||||
|
for _, field := range fields {
|
||||||
|
val := s.value.FieldByName(field.Name)
|
||||||
|
|
||||||
|
_, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
||||||
|
|
||||||
|
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
|
||||||
|
ok := IsZero(val.Interface())
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// zero value of the given field, such as "" for string, 0 for int
|
||||||
|
zero := reflect.Zero(val.Type()).Interface()
|
||||||
|
|
||||||
|
// current value of the given field
|
||||||
|
current := val.Interface()
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(current, zero) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasZero returns true if a field in a struct is not initialized (zero value).
|
||||||
|
// A struct tag with the content of "-" ignores the checking of that particular
|
||||||
|
// field. Example:
|
||||||
|
//
|
||||||
|
// // Field is ignored by this package.
|
||||||
|
// Field bool `structs:"-"`
|
||||||
|
//
|
||||||
|
// A value with the option of "omitnested" stops iterating further if the type
|
||||||
|
// is a struct. Example:
|
||||||
|
//
|
||||||
|
// // Field is not processed further by this package.
|
||||||
|
// Field time.Time `structs:"myName,omitnested"`
|
||||||
|
// Field *http.Request `structs:",omitnested"`
|
||||||
|
//
|
||||||
|
// Note that only exported fields of a struct can be accessed, non exported
|
||||||
|
// fields will be neglected. It panics if s's kind is not struct.
|
||||||
|
func (s *Struct) HasZero() bool {
|
||||||
|
fields := s.structFields()
|
||||||
|
|
||||||
|
for _, field := range fields {
|
||||||
|
val := s.value.FieldByName(field.Name)
|
||||||
|
|
||||||
|
_, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
||||||
|
|
||||||
|
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
|
||||||
|
ok := HasZero(val.Interface())
|
||||||
|
if ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// zero value of the given field, such as "" for string, 0 for int
|
||||||
|
zero := reflect.Zero(val.Type()).Interface()
|
||||||
|
|
||||||
|
// current value of the given field
|
||||||
|
current := val.Interface()
|
||||||
|
|
||||||
|
if reflect.DeepEqual(current, zero) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the structs's type name within its package. For more info refer
|
||||||
|
// to Name() function.
|
||||||
|
func (s *Struct) Name() string {
|
||||||
|
return s.value.Type().Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
// structFields returns the exported struct fields for a given s struct. This
|
||||||
|
// is a convenient helper method to avoid duplicate code in some of the
|
||||||
|
// functions.
|
||||||
|
func (s *Struct) structFields() []reflect.StructField {
|
||||||
|
t := s.value.Type()
|
||||||
|
|
||||||
|
var f []reflect.StructField
|
||||||
|
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
field := t.Field(i)
|
||||||
|
// we can't access the value of unexported fields
|
||||||
|
if field.PkgPath != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't check if it's omitted
|
||||||
|
if tag := field.Tag.Get(s.TagName); tag == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
f = append(f, field)
|
||||||
|
}
|
||||||
|
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func strctVal(s interface{}) reflect.Value {
|
||||||
|
v := reflect.ValueOf(s)
|
||||||
|
|
||||||
|
// if pointer get the underlying element≤
|
||||||
|
for v.Kind() == reflect.Ptr {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Kind() != reflect.Struct {
|
||||||
|
panic("not struct")
|
||||||
|
}
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map converts the given struct to a map[string]interface{}. For more info
|
||||||
|
// refer to Struct types Map() method. It panics if s's kind is not struct.
|
||||||
|
func Map(s interface{}) map[string]interface{} {
|
||||||
|
return New(s).Map()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FillMap is the same as Map. Instead of returning the output, it fills the
|
||||||
|
// given map.
|
||||||
|
func FillMap(s interface{}, out map[string]interface{}) {
|
||||||
|
New(s).FillMap(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values converts the given struct to a []interface{}. For more info refer to
|
||||||
|
// Struct types Values() method. It panics if s's kind is not struct.
|
||||||
|
func Values(s interface{}) []interface{} {
|
||||||
|
return New(s).Values()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fields returns a slice of *Field. For more info refer to Struct types
|
||||||
|
// Fields() method. It panics if s's kind is not struct.
|
||||||
|
func Fields(s interface{}) []*Field {
|
||||||
|
return New(s).Fields()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Names returns a slice of field names. For more info refer to Struct types
|
||||||
|
// Names() method. It panics if s's kind is not struct.
|
||||||
|
func Names(s interface{}) []string {
|
||||||
|
return New(s).Names()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsZero returns true if all fields is equal to a zero value. For more info
|
||||||
|
// refer to Struct types IsZero() method. It panics if s's kind is not struct.
|
||||||
|
func IsZero(s interface{}) bool {
|
||||||
|
return New(s).IsZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasZero returns true if any field is equal to a zero value. For more info
|
||||||
|
// refer to Struct types HasZero() method. It panics if s's kind is not struct.
|
||||||
|
func HasZero(s interface{}) bool {
|
||||||
|
return New(s).HasZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsStruct returns true if the given variable is a struct or a pointer to
|
||||||
|
// struct.
|
||||||
|
func IsStruct(s interface{}) bool {
|
||||||
|
v := reflect.ValueOf(s)
|
||||||
|
if v.Kind() == reflect.Ptr {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
// uninitialized zero value of a struct
|
||||||
|
if v.Kind() == reflect.Invalid {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.Kind() == reflect.Struct
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the structs's type name within its package. It returns an
|
||||||
|
// empty string for unnamed types. It panics if s's kind is not struct.
|
||||||
|
func Name(s interface{}) string {
|
||||||
|
return New(s).Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
// nested retrieves recursively all types for the given value and returns the
|
||||||
|
// nested value.
|
||||||
|
func (s *Struct) nested(val reflect.Value) interface{} {
|
||||||
|
var finalVal interface{}
|
||||||
|
|
||||||
|
v := reflect.ValueOf(val.Interface())
|
||||||
|
if v.Kind() == reflect.Ptr {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
n := New(val.Interface())
|
||||||
|
n.TagName = s.TagName
|
||||||
|
m := n.Map()
|
||||||
|
|
||||||
|
// do not add the converted value if there are no exported fields, ie:
|
||||||
|
// time.Time
|
||||||
|
if len(m) == 0 {
|
||||||
|
finalVal = val.Interface()
|
||||||
|
} else {
|
||||||
|
finalVal = m
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
// get the element type of the map
|
||||||
|
mapElem := val.Type()
|
||||||
|
switch val.Type().Kind() {
|
||||||
|
case reflect.Ptr, reflect.Array, reflect.Map,
|
||||||
|
reflect.Slice, reflect.Chan:
|
||||||
|
mapElem = val.Type().Elem()
|
||||||
|
if mapElem.Kind() == reflect.Ptr {
|
||||||
|
mapElem = mapElem.Elem()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// only iterate over struct types, ie: map[string]StructType,
|
||||||
|
// map[string][]StructType,
|
||||||
|
if mapElem.Kind() == reflect.Struct ||
|
||||||
|
(mapElem.Kind() == reflect.Slice &&
|
||||||
|
mapElem.Elem().Kind() == reflect.Struct) {
|
||||||
|
m := make(map[string]interface{}, val.Len())
|
||||||
|
for _, k := range val.MapKeys() {
|
||||||
|
m[k.String()] = s.nested(val.MapIndex(k))
|
||||||
|
}
|
||||||
|
finalVal = m
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(arslan): should this be optional?
|
||||||
|
finalVal = val.Interface()
|
||||||
|
case reflect.Slice, reflect.Array:
|
||||||
|
if val.Type().Kind() == reflect.Interface {
|
||||||
|
finalVal = val.Interface()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(arslan): should this be optional?
|
||||||
|
// do not iterate of non struct types, just pass the value. Ie: []int,
|
||||||
|
// []string, co... We only iterate further if it's a struct.
|
||||||
|
// i.e []foo or []*foo
|
||||||
|
if val.Type().Elem().Kind() != reflect.Struct &&
|
||||||
|
!(val.Type().Elem().Kind() == reflect.Ptr &&
|
||||||
|
val.Type().Elem().Elem().Kind() == reflect.Struct) {
|
||||||
|
finalVal = val.Interface()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
slices := make([]interface{}, val.Len())
|
||||||
|
for x := 0; x < val.Len(); x++ {
|
||||||
|
slices[x] = s.nested(val.Index(x))
|
||||||
|
}
|
||||||
|
finalVal = slices
|
||||||
|
default:
|
||||||
|
finalVal = val.Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
return finalVal
|
||||||
|
}
|
32
vendor/github.com/fatih/structs/tags.go
generated
vendored
Normal file
32
vendor/github.com/fatih/structs/tags.go
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package structs
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// tagOptions contains a slice of tag options
|
||||||
|
type tagOptions []string
|
||||||
|
|
||||||
|
// Has returns true if the given option is available in tagOptions
|
||||||
|
func (t tagOptions) Has(opt string) bool {
|
||||||
|
for _, tagOpt := range t {
|
||||||
|
if tagOpt == opt {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseTag splits a struct field's tag into its name and a list of options
|
||||||
|
// which comes after a name. A tag is in the form of: "name,option1,option2".
|
||||||
|
// The name can be neglectected.
|
||||||
|
func parseTag(tag string) (string, tagOptions) {
|
||||||
|
// tag is one of followings:
|
||||||
|
// ""
|
||||||
|
// "name"
|
||||||
|
// "name,opt"
|
||||||
|
// "name,opt,opt2"
|
||||||
|
// ",opt"
|
||||||
|
|
||||||
|
res := strings.Split(tag, ",")
|
||||||
|
return res[0], res[1:]
|
||||||
|
}
|
19
vendor/github.com/kolo/xmlrpc/LICENSE
generated
vendored
Normal file
19
vendor/github.com/kolo/xmlrpc/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
Copyright (C) 2012 Dmitry Maksimov
|
||||||
|
|
||||||
|
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.
|
169
vendor/github.com/kolo/xmlrpc/client.go
generated
vendored
Normal file
169
vendor/github.com/kolo/xmlrpc/client.go
generated
vendored
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
package xmlrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/http/cookiejar"
|
||||||
|
"net/rpc"
|
||||||
|
"net/url"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
*rpc.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// clientCodec is rpc.ClientCodec interface implementation.
|
||||||
|
type clientCodec struct {
|
||||||
|
// url presents url of xmlrpc service
|
||||||
|
url *url.URL
|
||||||
|
|
||||||
|
// httpClient works with HTTP protocol
|
||||||
|
httpClient *http.Client
|
||||||
|
|
||||||
|
// cookies stores cookies received on last request
|
||||||
|
cookies http.CookieJar
|
||||||
|
|
||||||
|
// responses presents map of active requests. It is required to return request id, that
|
||||||
|
// rpc.Client can mark them as done.
|
||||||
|
responses map[uint64]*http.Response
|
||||||
|
mutex sync.Mutex
|
||||||
|
|
||||||
|
response *Response
|
||||||
|
|
||||||
|
// ready presents channel, that is used to link request and it`s response.
|
||||||
|
ready chan uint64
|
||||||
|
|
||||||
|
// close notifies codec is closed.
|
||||||
|
close chan uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (codec *clientCodec) WriteRequest(request *rpc.Request, args interface{}) (err error) {
|
||||||
|
httpRequest, err := NewRequest(codec.url.String(), request.ServiceMethod, args)
|
||||||
|
|
||||||
|
if codec.cookies != nil {
|
||||||
|
for _, cookie := range codec.cookies.Cookies(codec.url) {
|
||||||
|
httpRequest.AddCookie(cookie)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var httpResponse *http.Response
|
||||||
|
httpResponse, err = codec.httpClient.Do(httpRequest)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if codec.cookies != nil {
|
||||||
|
codec.cookies.SetCookies(codec.url, httpResponse.Cookies())
|
||||||
|
}
|
||||||
|
|
||||||
|
codec.mutex.Lock()
|
||||||
|
codec.responses[request.Seq] = httpResponse
|
||||||
|
codec.mutex.Unlock()
|
||||||
|
|
||||||
|
codec.ready <- request.Seq
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (codec *clientCodec) ReadResponseHeader(response *rpc.Response) (err error) {
|
||||||
|
var seq uint64
|
||||||
|
|
||||||
|
select {
|
||||||
|
case seq = <-codec.ready:
|
||||||
|
case <-codec.close:
|
||||||
|
return errors.New("codec is closed")
|
||||||
|
}
|
||||||
|
|
||||||
|
codec.mutex.Lock()
|
||||||
|
httpResponse := codec.responses[seq]
|
||||||
|
codec.mutex.Unlock()
|
||||||
|
|
||||||
|
if httpResponse.StatusCode < 200 || httpResponse.StatusCode >= 300 {
|
||||||
|
return fmt.Errorf("request error: bad status code - %d", httpResponse.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
respData, err := ioutil.ReadAll(httpResponse.Body)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
httpResponse.Body.Close()
|
||||||
|
|
||||||
|
resp := NewResponse(respData)
|
||||||
|
|
||||||
|
if resp.Failed() {
|
||||||
|
response.Error = fmt.Sprintf("%v", resp.Err())
|
||||||
|
}
|
||||||
|
|
||||||
|
codec.response = resp
|
||||||
|
|
||||||
|
response.Seq = seq
|
||||||
|
|
||||||
|
codec.mutex.Lock()
|
||||||
|
delete(codec.responses, seq)
|
||||||
|
codec.mutex.Unlock()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (codec *clientCodec) ReadResponseBody(v interface{}) (err error) {
|
||||||
|
if v == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = codec.response.Unmarshal(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (codec *clientCodec) Close() error {
|
||||||
|
transport := codec.httpClient.Transport.(*http.Transport)
|
||||||
|
transport.CloseIdleConnections()
|
||||||
|
|
||||||
|
close(codec.close)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient returns instance of rpc.Client object, that is used to send request to xmlrpc service.
|
||||||
|
func NewClient(requrl string, transport http.RoundTripper) (*Client, error) {
|
||||||
|
if transport == nil {
|
||||||
|
transport = http.DefaultTransport
|
||||||
|
}
|
||||||
|
|
||||||
|
httpClient := &http.Client{Transport: transport}
|
||||||
|
|
||||||
|
jar, err := cookiejar.New(nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := url.Parse(requrl)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
codec := clientCodec{
|
||||||
|
url: u,
|
||||||
|
httpClient: httpClient,
|
||||||
|
close: make(chan uint64),
|
||||||
|
ready: make(chan uint64),
|
||||||
|
responses: make(map[uint64]*http.Response),
|
||||||
|
cookies: jar,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Client{rpc.NewClientWithCodec(&codec)}, nil
|
||||||
|
}
|
463
vendor/github.com/kolo/xmlrpc/decoder.go
generated
vendored
Normal file
463
vendor/github.com/kolo/xmlrpc/decoder.go
generated
vendored
Normal file
|
@ -0,0 +1,463 @@
|
||||||
|
package xmlrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
iso8601 = "20060102T15:04:05"
|
||||||
|
iso8601Z = "20060102T15:04:05Z07:00"
|
||||||
|
iso8601Hyphen = "2006-01-02T15:04:05"
|
||||||
|
iso8601HyphenZ = "2006-01-02T15:04:05Z07:00"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// CharsetReader is a function to generate reader which converts a non UTF-8
|
||||||
|
// charset into UTF-8.
|
||||||
|
CharsetReader func(string, io.Reader) (io.Reader, error)
|
||||||
|
|
||||||
|
timeLayouts = []string{iso8601, iso8601Z, iso8601Hyphen, iso8601HyphenZ}
|
||||||
|
invalidXmlError = errors.New("invalid xml")
|
||||||
|
)
|
||||||
|
|
||||||
|
type TypeMismatchError string
|
||||||
|
|
||||||
|
func (e TypeMismatchError) Error() string { return string(e) }
|
||||||
|
|
||||||
|
type decoder struct {
|
||||||
|
*xml.Decoder
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshal(data []byte, v interface{}) (err error) {
|
||||||
|
dec := &decoder{xml.NewDecoder(bytes.NewBuffer(data))}
|
||||||
|
|
||||||
|
if CharsetReader != nil {
|
||||||
|
dec.CharsetReader = CharsetReader
|
||||||
|
}
|
||||||
|
|
||||||
|
var tok xml.Token
|
||||||
|
for {
|
||||||
|
if tok, err = dec.Token(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, ok := tok.(xml.StartElement); ok {
|
||||||
|
if t.Name.Local == "value" {
|
||||||
|
val := reflect.ValueOf(v)
|
||||||
|
if val.Kind() != reflect.Ptr {
|
||||||
|
return errors.New("non-pointer value passed to unmarshal")
|
||||||
|
}
|
||||||
|
if err = dec.decodeValue(val.Elem()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// read until end of document
|
||||||
|
err = dec.Skip()
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dec *decoder) decodeValue(val reflect.Value) error {
|
||||||
|
var tok xml.Token
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if val.Kind() == reflect.Ptr {
|
||||||
|
if val.IsNil() {
|
||||||
|
val.Set(reflect.New(val.Type().Elem()))
|
||||||
|
}
|
||||||
|
val = val.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
var typeName string
|
||||||
|
for {
|
||||||
|
if tok, err = dec.Token(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, ok := tok.(xml.EndElement); ok {
|
||||||
|
if t.Name.Local == "value" {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return invalidXmlError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, ok := tok.(xml.StartElement); ok {
|
||||||
|
typeName = t.Name.Local
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Treat value data without type identifier as string
|
||||||
|
if t, ok := tok.(xml.CharData); ok {
|
||||||
|
if value := strings.TrimSpace(string(t)); value != "" {
|
||||||
|
if err = checkType(val, reflect.String); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
val.SetString(value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch typeName {
|
||||||
|
case "struct":
|
||||||
|
ismap := false
|
||||||
|
pmap := val
|
||||||
|
valType := val.Type()
|
||||||
|
|
||||||
|
if err = checkType(val, reflect.Struct); err != nil {
|
||||||
|
if checkType(val, reflect.Map) == nil {
|
||||||
|
if valType.Key().Kind() != reflect.String {
|
||||||
|
return fmt.Errorf("only maps with string key type can be unmarshalled")
|
||||||
|
}
|
||||||
|
ismap = true
|
||||||
|
} else if checkType(val, reflect.Interface) == nil && val.IsNil() {
|
||||||
|
var dummy map[string]interface{}
|
||||||
|
pmap = reflect.New(reflect.TypeOf(dummy)).Elem()
|
||||||
|
valType = pmap.Type()
|
||||||
|
ismap = true
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var fields map[string]reflect.Value
|
||||||
|
|
||||||
|
if !ismap {
|
||||||
|
fields = make(map[string]reflect.Value)
|
||||||
|
|
||||||
|
for i := 0; i < valType.NumField(); i++ {
|
||||||
|
field := valType.Field(i)
|
||||||
|
fieldVal := val.FieldByName(field.Name)
|
||||||
|
|
||||||
|
if fieldVal.CanSet() {
|
||||||
|
if fn := field.Tag.Get("xmlrpc"); fn != "" {
|
||||||
|
fields[fn] = fieldVal
|
||||||
|
} else {
|
||||||
|
fields[field.Name] = fieldVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Create initial empty map
|
||||||
|
pmap.Set(reflect.MakeMap(valType))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process struct members.
|
||||||
|
StructLoop:
|
||||||
|
for {
|
||||||
|
if tok, err = dec.Token(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch t := tok.(type) {
|
||||||
|
case xml.StartElement:
|
||||||
|
if t.Name.Local != "member" {
|
||||||
|
return invalidXmlError
|
||||||
|
}
|
||||||
|
|
||||||
|
tagName, fieldName, err := dec.readTag()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if tagName != "name" {
|
||||||
|
return invalidXmlError
|
||||||
|
}
|
||||||
|
|
||||||
|
var fv reflect.Value
|
||||||
|
ok := true
|
||||||
|
|
||||||
|
if !ismap {
|
||||||
|
fv, ok = fields[string(fieldName)]
|
||||||
|
} else {
|
||||||
|
fv = reflect.New(valType.Elem())
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
for {
|
||||||
|
if tok, err = dec.Token(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if t, ok := tok.(xml.StartElement); ok && t.Name.Local == "value" {
|
||||||
|
if err = dec.decodeValue(fv); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// </value>
|
||||||
|
if err = dec.Skip(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// </member>
|
||||||
|
if err = dec.Skip(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ismap {
|
||||||
|
pmap.SetMapIndex(reflect.ValueOf(string(fieldName)), reflect.Indirect(fv))
|
||||||
|
val.Set(pmap)
|
||||||
|
}
|
||||||
|
case xml.EndElement:
|
||||||
|
break StructLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "array":
|
||||||
|
pslice := val
|
||||||
|
if checkType(val, reflect.Interface) == nil && val.IsNil() {
|
||||||
|
var dummy []interface{}
|
||||||
|
pslice = reflect.New(reflect.TypeOf(dummy)).Elem()
|
||||||
|
} else if err = checkType(val, reflect.Slice); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayLoop:
|
||||||
|
for {
|
||||||
|
if tok, err = dec.Token(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t := tok.(type) {
|
||||||
|
case xml.StartElement:
|
||||||
|
if t.Name.Local != "data" {
|
||||||
|
return invalidXmlError
|
||||||
|
}
|
||||||
|
|
||||||
|
slice := reflect.MakeSlice(pslice.Type(), 0, 0)
|
||||||
|
|
||||||
|
DataLoop:
|
||||||
|
for {
|
||||||
|
if tok, err = dec.Token(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tt := tok.(type) {
|
||||||
|
case xml.StartElement:
|
||||||
|
if tt.Name.Local != "value" {
|
||||||
|
return invalidXmlError
|
||||||
|
}
|
||||||
|
|
||||||
|
v := reflect.New(pslice.Type().Elem())
|
||||||
|
if err = dec.decodeValue(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
slice = reflect.Append(slice, v.Elem())
|
||||||
|
|
||||||
|
// </value>
|
||||||
|
if err = dec.Skip(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case xml.EndElement:
|
||||||
|
pslice.Set(slice)
|
||||||
|
val.Set(pslice)
|
||||||
|
break DataLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case xml.EndElement:
|
||||||
|
break ArrayLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if tok, err = dec.Token(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var data []byte
|
||||||
|
|
||||||
|
switch t := tok.(type) {
|
||||||
|
case xml.EndElement:
|
||||||
|
return nil
|
||||||
|
case xml.CharData:
|
||||||
|
data = []byte(t.Copy())
|
||||||
|
default:
|
||||||
|
return invalidXmlError
|
||||||
|
}
|
||||||
|
|
||||||
|
switch typeName {
|
||||||
|
case "int", "i4", "i8":
|
||||||
|
if checkType(val, reflect.Interface) == nil && val.IsNil() {
|
||||||
|
i, err := strconv.ParseInt(string(data), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pi := reflect.New(reflect.TypeOf(i)).Elem()
|
||||||
|
pi.SetInt(i)
|
||||||
|
val.Set(pi)
|
||||||
|
} else if err = checkType(val, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
i, err := strconv.ParseInt(string(data), 10, val.Type().Bits())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
val.SetInt(i)
|
||||||
|
}
|
||||||
|
case "string", "base64":
|
||||||
|
str := string(data)
|
||||||
|
if checkType(val, reflect.Interface) == nil && val.IsNil() {
|
||||||
|
pstr := reflect.New(reflect.TypeOf(str)).Elem()
|
||||||
|
pstr.SetString(str)
|
||||||
|
val.Set(pstr)
|
||||||
|
} else if err = checkType(val, reflect.String); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
val.SetString(str)
|
||||||
|
}
|
||||||
|
case "dateTime.iso8601":
|
||||||
|
var t time.Time
|
||||||
|
var err error
|
||||||
|
|
||||||
|
for _, layout := range timeLayouts {
|
||||||
|
t, err = time.Parse(layout, string(data))
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if checkType(val, reflect.Interface) == nil && val.IsNil() {
|
||||||
|
ptime := reflect.New(reflect.TypeOf(t)).Elem()
|
||||||
|
ptime.Set(reflect.ValueOf(t))
|
||||||
|
val.Set(ptime)
|
||||||
|
} else if _, ok := val.Interface().(time.Time); !ok {
|
||||||
|
return TypeMismatchError(fmt.Sprintf("error: type mismatch error - can't decode %v to time", val.Kind()))
|
||||||
|
} else {
|
||||||
|
val.Set(reflect.ValueOf(t))
|
||||||
|
}
|
||||||
|
case "boolean":
|
||||||
|
v, err := strconv.ParseBool(string(data))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if checkType(val, reflect.Interface) == nil && val.IsNil() {
|
||||||
|
pv := reflect.New(reflect.TypeOf(v)).Elem()
|
||||||
|
pv.SetBool(v)
|
||||||
|
val.Set(pv)
|
||||||
|
} else if err = checkType(val, reflect.Bool); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
val.SetBool(v)
|
||||||
|
}
|
||||||
|
case "double":
|
||||||
|
if checkType(val, reflect.Interface) == nil && val.IsNil() {
|
||||||
|
i, err := strconv.ParseFloat(string(data), 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pdouble := reflect.New(reflect.TypeOf(i)).Elem()
|
||||||
|
pdouble.SetFloat(i)
|
||||||
|
val.Set(pdouble)
|
||||||
|
} else if err = checkType(val, reflect.Float32, reflect.Float64); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
i, err := strconv.ParseFloat(string(data), val.Type().Bits())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
val.SetFloat(i)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return errors.New("unsupported type")
|
||||||
|
}
|
||||||
|
|
||||||
|
// </type>
|
||||||
|
if err = dec.Skip(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dec *decoder) readTag() (string, []byte, error) {
|
||||||
|
var tok xml.Token
|
||||||
|
var err error
|
||||||
|
|
||||||
|
var name string
|
||||||
|
for {
|
||||||
|
if tok, err = dec.Token(); err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, ok := tok.(xml.StartElement); ok {
|
||||||
|
name = t.Name.Local
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := dec.readCharData()
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return name, value, dec.Skip()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dec *decoder) readCharData() ([]byte, error) {
|
||||||
|
var tok xml.Token
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if tok, err = dec.Token(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, ok := tok.(xml.CharData); ok {
|
||||||
|
return []byte(t.Copy()), nil
|
||||||
|
} else {
|
||||||
|
return nil, invalidXmlError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkType(val reflect.Value, kinds ...reflect.Kind) error {
|
||||||
|
if len(kinds) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if val.Kind() == reflect.Ptr {
|
||||||
|
val = val.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
match := false
|
||||||
|
|
||||||
|
for _, kind := range kinds {
|
||||||
|
if val.Kind() == kind {
|
||||||
|
match = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !match {
|
||||||
|
return TypeMismatchError(fmt.Sprintf("error: type mismatch - can't unmarshal %v to %v",
|
||||||
|
val.Kind(), kinds[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
164
vendor/github.com/kolo/xmlrpc/encoder.go
generated
vendored
Normal file
164
vendor/github.com/kolo/xmlrpc/encoder.go
generated
vendored
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
package xmlrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type encodeFunc func(reflect.Value) ([]byte, error)
|
||||||
|
|
||||||
|
func marshal(v interface{}) ([]byte, error) {
|
||||||
|
if v == nil {
|
||||||
|
return []byte{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
val := reflect.ValueOf(v)
|
||||||
|
return encodeValue(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeValue(val reflect.Value) ([]byte, error) {
|
||||||
|
var b []byte
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if val.Kind() == reflect.Ptr || val.Kind() == reflect.Interface {
|
||||||
|
if val.IsNil() {
|
||||||
|
return []byte("<value/>"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
val = val.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch val.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
switch val.Interface().(type) {
|
||||||
|
case time.Time:
|
||||||
|
t := val.Interface().(time.Time)
|
||||||
|
b = []byte(fmt.Sprintf("<dateTime.iso8601>%s</dateTime.iso8601>", t.Format(iso8601)))
|
||||||
|
default:
|
||||||
|
b, err = encodeStruct(val)
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
b, err = encodeMap(val)
|
||||||
|
case reflect.Slice:
|
||||||
|
b, err = encodeSlice(val)
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
b = []byte(fmt.Sprintf("<int>%s</int>", strconv.FormatInt(val.Int(), 10)))
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
b = []byte(fmt.Sprintf("<i4>%s</i4>", strconv.FormatUint(val.Uint(), 10)))
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
b = []byte(fmt.Sprintf("<double>%s</double>",
|
||||||
|
strconv.FormatFloat(val.Float(), 'g', -1, val.Type().Bits())))
|
||||||
|
case reflect.Bool:
|
||||||
|
if val.Bool() {
|
||||||
|
b = []byte("<boolean>1</boolean>")
|
||||||
|
} else {
|
||||||
|
b = []byte("<boolean>0</boolean>")
|
||||||
|
}
|
||||||
|
case reflect.String:
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
xml.Escape(&buf, []byte(val.String()))
|
||||||
|
|
||||||
|
if _, ok := val.Interface().(Base64); ok {
|
||||||
|
b = []byte(fmt.Sprintf("<base64>%s</base64>", buf.String()))
|
||||||
|
} else {
|
||||||
|
b = []byte(fmt.Sprintf("<string>%s</string>", buf.String()))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("xmlrpc encode error: unsupported type")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte(fmt.Sprintf("<value>%s</value>", string(b))), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeStruct(val reflect.Value) ([]byte, error) {
|
||||||
|
var b bytes.Buffer
|
||||||
|
|
||||||
|
b.WriteString("<struct>")
|
||||||
|
|
||||||
|
t := val.Type()
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
b.WriteString("<member>")
|
||||||
|
f := t.Field(i)
|
||||||
|
|
||||||
|
name := f.Tag.Get("xmlrpc")
|
||||||
|
if name == "" {
|
||||||
|
name = f.Name
|
||||||
|
}
|
||||||
|
b.WriteString(fmt.Sprintf("<name>%s</name>", name))
|
||||||
|
|
||||||
|
p, err := encodeValue(val.FieldByName(f.Name))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b.Write(p)
|
||||||
|
|
||||||
|
b.WriteString("</member>")
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteString("</struct>")
|
||||||
|
|
||||||
|
return b.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeMap(val reflect.Value) ([]byte, error) {
|
||||||
|
var t = val.Type()
|
||||||
|
|
||||||
|
if t.Key().Kind() != reflect.String {
|
||||||
|
return nil, fmt.Errorf("xmlrpc encode error: only maps with string keys are supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
|
||||||
|
b.WriteString("<struct>")
|
||||||
|
|
||||||
|
keys := val.MapKeys()
|
||||||
|
|
||||||
|
for i := 0; i < val.Len(); i++ {
|
||||||
|
key := keys[i]
|
||||||
|
kval := val.MapIndex(key)
|
||||||
|
|
||||||
|
b.WriteString("<member>")
|
||||||
|
b.WriteString(fmt.Sprintf("<name>%s</name>", key.String()))
|
||||||
|
|
||||||
|
p, err := encodeValue(kval)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
b.Write(p)
|
||||||
|
b.WriteString("</member>")
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteString("</struct>")
|
||||||
|
|
||||||
|
return b.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeSlice(val reflect.Value) ([]byte, error) {
|
||||||
|
var b bytes.Buffer
|
||||||
|
|
||||||
|
b.WriteString("<array><data>")
|
||||||
|
|
||||||
|
for i := 0; i < val.Len(); i++ {
|
||||||
|
p, err := encodeValue(val.Index(i))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
b.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteString("</data></array>")
|
||||||
|
|
||||||
|
return b.Bytes(), nil
|
||||||
|
}
|
57
vendor/github.com/kolo/xmlrpc/request.go
generated
vendored
Normal file
57
vendor/github.com/kolo/xmlrpc/request.go
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
package xmlrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewRequest(url string, method string, args interface{}) (*http.Request, error) {
|
||||||
|
var t []interface{}
|
||||||
|
var ok bool
|
||||||
|
if t, ok = args.([]interface{}); !ok {
|
||||||
|
if args != nil {
|
||||||
|
t = []interface{}{args}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := EncodeMethodCall(method, t...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
request, err := http.NewRequest("POST", url, bytes.NewReader(body))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
request.Header.Set("Content-Type", "text/xml")
|
||||||
|
request.Header.Set("Content-Length", fmt.Sprintf("%d", len(body)))
|
||||||
|
|
||||||
|
return request, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func EncodeMethodCall(method string, args ...interface{}) ([]byte, error) {
|
||||||
|
var b bytes.Buffer
|
||||||
|
b.WriteString(`<?xml version="1.0" encoding="UTF-8"?>`)
|
||||||
|
b.WriteString(fmt.Sprintf("<methodCall><methodName>%s</methodName>", method))
|
||||||
|
|
||||||
|
if args != nil {
|
||||||
|
b.WriteString("<params>")
|
||||||
|
|
||||||
|
for _, arg := range args {
|
||||||
|
p, err := marshal(arg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteString(fmt.Sprintf("<param>%s</param>", string(p)))
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteString("</params>")
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteString("</methodCall>")
|
||||||
|
|
||||||
|
return b.Bytes(), nil
|
||||||
|
}
|
52
vendor/github.com/kolo/xmlrpc/response.go
generated
vendored
Normal file
52
vendor/github.com/kolo/xmlrpc/response.go
generated
vendored
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
package xmlrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
faultRx = regexp.MustCompile(`<fault>(\s|\S)+</fault>`)
|
||||||
|
)
|
||||||
|
|
||||||
|
type failedResponse struct {
|
||||||
|
Code int `xmlrpc:"faultCode"`
|
||||||
|
Error string `xmlrpc:"faultString"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *failedResponse) err() error {
|
||||||
|
return &xmlrpcError{
|
||||||
|
code: r.Code,
|
||||||
|
err: r.Error,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewResponse(data []byte) *Response {
|
||||||
|
return &Response{
|
||||||
|
data: data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Response) Failed() bool {
|
||||||
|
return faultRx.Match(r.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Response) Err() error {
|
||||||
|
failedResp := new(failedResponse)
|
||||||
|
if err := unmarshal(r.data, failedResp); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return failedResp.err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Response) Unmarshal(v interface{}) error {
|
||||||
|
if err := unmarshal(r.data, v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
19
vendor/github.com/kolo/xmlrpc/xmlrpc.go
generated
vendored
Normal file
19
vendor/github.com/kolo/xmlrpc/xmlrpc.go
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package xmlrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// xmlrpcError represents errors returned on xmlrpc request.
|
||||||
|
type xmlrpcError struct {
|
||||||
|
code int
|
||||||
|
err string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error() method implements Error interface
|
||||||
|
func (e *xmlrpcError) Error() string {
|
||||||
|
return fmt.Sprintf("error: \"%s\" code: %d", e.err, e.code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Base64 represents value in base64 encoding
|
||||||
|
type Base64 string
|
21
vendor/github.com/mitchellh/mapstructure/LICENSE
generated
vendored
Normal file
21
vendor/github.com/mitchellh/mapstructure/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013 Mitchell Hashimoto
|
||||||
|
|
||||||
|
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.
|
217
vendor/github.com/mitchellh/mapstructure/decode_hooks.go
generated
vendored
Normal file
217
vendor/github.com/mitchellh/mapstructure/decode_hooks.go
generated
vendored
Normal file
|
@ -0,0 +1,217 @@
|
||||||
|
package mapstructure
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// typedDecodeHook takes a raw DecodeHookFunc (an interface{}) and turns
|
||||||
|
// it into the proper DecodeHookFunc type, such as DecodeHookFuncType.
|
||||||
|
func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc {
|
||||||
|
// Create variables here so we can reference them with the reflect pkg
|
||||||
|
var f1 DecodeHookFuncType
|
||||||
|
var f2 DecodeHookFuncKind
|
||||||
|
|
||||||
|
// Fill in the variables into this interface and the rest is done
|
||||||
|
// automatically using the reflect package.
|
||||||
|
potential := []interface{}{f1, f2}
|
||||||
|
|
||||||
|
v := reflect.ValueOf(h)
|
||||||
|
vt := v.Type()
|
||||||
|
for _, raw := range potential {
|
||||||
|
pt := reflect.ValueOf(raw).Type()
|
||||||
|
if vt.ConvertibleTo(pt) {
|
||||||
|
return v.Convert(pt).Interface()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeHookExec executes the given decode hook. This should be used
|
||||||
|
// since it'll naturally degrade to the older backwards compatible DecodeHookFunc
|
||||||
|
// that took reflect.Kind instead of reflect.Type.
|
||||||
|
func DecodeHookExec(
|
||||||
|
raw DecodeHookFunc,
|
||||||
|
from reflect.Type, to reflect.Type,
|
||||||
|
data interface{}) (interface{}, error) {
|
||||||
|
switch f := typedDecodeHook(raw).(type) {
|
||||||
|
case DecodeHookFuncType:
|
||||||
|
return f(from, to, data)
|
||||||
|
case DecodeHookFuncKind:
|
||||||
|
return f(from.Kind(), to.Kind(), data)
|
||||||
|
default:
|
||||||
|
return nil, errors.New("invalid decode hook signature")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ComposeDecodeHookFunc creates a single DecodeHookFunc that
|
||||||
|
// automatically composes multiple DecodeHookFuncs.
|
||||||
|
//
|
||||||
|
// The composed funcs are called in order, with the result of the
|
||||||
|
// previous transformation.
|
||||||
|
func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc {
|
||||||
|
return func(
|
||||||
|
f reflect.Type,
|
||||||
|
t reflect.Type,
|
||||||
|
data interface{}) (interface{}, error) {
|
||||||
|
var err error
|
||||||
|
for _, f1 := range fs {
|
||||||
|
data, err = DecodeHookExec(f1, f, t, data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modify the from kind to be correct with the new data
|
||||||
|
f = nil
|
||||||
|
if val := reflect.ValueOf(data); val.IsValid() {
|
||||||
|
f = val.Type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToSliceHookFunc returns a DecodeHookFunc that converts
|
||||||
|
// string to []string by splitting on the given sep.
|
||||||
|
func StringToSliceHookFunc(sep string) DecodeHookFunc {
|
||||||
|
return func(
|
||||||
|
f reflect.Kind,
|
||||||
|
t reflect.Kind,
|
||||||
|
data interface{}) (interface{}, error) {
|
||||||
|
if f != reflect.String || t != reflect.Slice {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
raw := data.(string)
|
||||||
|
if raw == "" {
|
||||||
|
return []string{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Split(raw, sep), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToTimeDurationHookFunc returns a DecodeHookFunc that converts
|
||||||
|
// strings to time.Duration.
|
||||||
|
func StringToTimeDurationHookFunc() DecodeHookFunc {
|
||||||
|
return func(
|
||||||
|
f reflect.Type,
|
||||||
|
t reflect.Type,
|
||||||
|
data interface{}) (interface{}, error) {
|
||||||
|
if f.Kind() != reflect.String {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
if t != reflect.TypeOf(time.Duration(5)) {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert it by parsing
|
||||||
|
return time.ParseDuration(data.(string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToIPHookFunc returns a DecodeHookFunc that converts
|
||||||
|
// strings to net.IP
|
||||||
|
func StringToIPHookFunc() DecodeHookFunc {
|
||||||
|
return func(
|
||||||
|
f reflect.Type,
|
||||||
|
t reflect.Type,
|
||||||
|
data interface{}) (interface{}, error) {
|
||||||
|
if f.Kind() != reflect.String {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
if t != reflect.TypeOf(net.IP{}) {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert it by parsing
|
||||||
|
ip := net.ParseIP(data.(string))
|
||||||
|
if ip == nil {
|
||||||
|
return net.IP{}, fmt.Errorf("failed parsing ip %v", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ip, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToIPNetHookFunc returns a DecodeHookFunc that converts
|
||||||
|
// strings to net.IPNet
|
||||||
|
func StringToIPNetHookFunc() DecodeHookFunc {
|
||||||
|
return func(
|
||||||
|
f reflect.Type,
|
||||||
|
t reflect.Type,
|
||||||
|
data interface{}) (interface{}, error) {
|
||||||
|
if f.Kind() != reflect.String {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
if t != reflect.TypeOf(net.IPNet{}) {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert it by parsing
|
||||||
|
_, net, err := net.ParseCIDR(data.(string))
|
||||||
|
return net, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToTimeHookFunc returns a DecodeHookFunc that converts
|
||||||
|
// strings to time.Time.
|
||||||
|
func StringToTimeHookFunc(layout string) DecodeHookFunc {
|
||||||
|
return func(
|
||||||
|
f reflect.Type,
|
||||||
|
t reflect.Type,
|
||||||
|
data interface{}) (interface{}, error) {
|
||||||
|
if f.Kind() != reflect.String {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
if t != reflect.TypeOf(time.Time{}) {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert it by parsing
|
||||||
|
return time.Parse(layout, data.(string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WeaklyTypedHook is a DecodeHookFunc which adds support for weak typing to
|
||||||
|
// the decoder.
|
||||||
|
//
|
||||||
|
// Note that this is significantly different from the WeaklyTypedInput option
|
||||||
|
// of the DecoderConfig.
|
||||||
|
func WeaklyTypedHook(
|
||||||
|
f reflect.Kind,
|
||||||
|
t reflect.Kind,
|
||||||
|
data interface{}) (interface{}, error) {
|
||||||
|
dataVal := reflect.ValueOf(data)
|
||||||
|
switch t {
|
||||||
|
case reflect.String:
|
||||||
|
switch f {
|
||||||
|
case reflect.Bool:
|
||||||
|
if dataVal.Bool() {
|
||||||
|
return "1", nil
|
||||||
|
}
|
||||||
|
return "0", nil
|
||||||
|
case reflect.Float32:
|
||||||
|
return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil
|
||||||
|
case reflect.Int:
|
||||||
|
return strconv.FormatInt(dataVal.Int(), 10), nil
|
||||||
|
case reflect.Slice:
|
||||||
|
dataType := dataVal.Type()
|
||||||
|
elemKind := dataType.Elem().Kind()
|
||||||
|
if elemKind == reflect.Uint8 {
|
||||||
|
return string(dataVal.Interface().([]uint8)), nil
|
||||||
|
}
|
||||||
|
case reflect.Uint:
|
||||||
|
return strconv.FormatUint(dataVal.Uint(), 10), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data, nil
|
||||||
|
}
|
50
vendor/github.com/mitchellh/mapstructure/error.go
generated
vendored
Normal file
50
vendor/github.com/mitchellh/mapstructure/error.go
generated
vendored
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
package mapstructure
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Error implements the error interface and can represents multiple
|
||||||
|
// errors that occur in the course of a single decode.
|
||||||
|
type Error struct {
|
||||||
|
Errors []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Error() string {
|
||||||
|
points := make([]string, len(e.Errors))
|
||||||
|
for i, err := range e.Errors {
|
||||||
|
points[i] = fmt.Sprintf("* %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(points)
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"%d error(s) decoding:\n\n%s",
|
||||||
|
len(e.Errors), strings.Join(points, "\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrappedErrors implements the errwrap.Wrapper interface to make this
|
||||||
|
// return value more useful with the errwrap and go-multierror libraries.
|
||||||
|
func (e *Error) WrappedErrors() []error {
|
||||||
|
if e == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]error, len(e.Errors))
|
||||||
|
for i, e := range e.Errors {
|
||||||
|
result[i] = errors.New(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendErrors(errors []string, err error) []string {
|
||||||
|
switch e := err.(type) {
|
||||||
|
case *Error:
|
||||||
|
return append(errors, e.Errors...)
|
||||||
|
default:
|
||||||
|
return append(errors, e.Error())
|
||||||
|
}
|
||||||
|
}
|
1149
vendor/github.com/mitchellh/mapstructure/mapstructure.go
generated
vendored
Normal file
1149
vendor/github.com/mitchellh/mapstructure/mapstructure.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
21
vendor/github.com/smueller18/goinwx/LICENSE
generated
vendored
Normal file
21
vendor/github.com/smueller18/goinwx/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2017 Andrew
|
||||||
|
|
||||||
|
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.
|
54
vendor/github.com/smueller18/goinwx/account.go
generated
vendored
Normal file
54
vendor/github.com/smueller18/goinwx/account.go
generated
vendored
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
package goinwx
|
||||||
|
|
||||||
|
const (
|
||||||
|
methodAccountLogin = "account.login"
|
||||||
|
methodAccountLogout = "account.logout"
|
||||||
|
methodAccountLock = "account.lock"
|
||||||
|
methodAccountUnlock = "account.unlock"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AccountService interface {
|
||||||
|
Login() error
|
||||||
|
Logout() error
|
||||||
|
Lock() error
|
||||||
|
Unlock(tan string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccountServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ AccountService = &AccountServiceOp{}
|
||||||
|
|
||||||
|
func (s *AccountServiceOp) Login() error {
|
||||||
|
req := s.client.NewRequest(methodAccountLogin, map[string]interface{}{
|
||||||
|
"user": s.client.Username,
|
||||||
|
"pass": s.client.Password,
|
||||||
|
})
|
||||||
|
|
||||||
|
_, err := s.client.Do(*req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AccountServiceOp) Logout() error {
|
||||||
|
req := s.client.NewRequest(methodAccountLogout, nil)
|
||||||
|
|
||||||
|
_, err := s.client.Do(*req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AccountServiceOp) Lock() error {
|
||||||
|
req := s.client.NewRequest(methodAccountLock, nil)
|
||||||
|
|
||||||
|
_, err := s.client.Do(*req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AccountServiceOp) Unlock(tan string) error {
|
||||||
|
req := s.client.NewRequest(methodAccountUnlock, map[string]interface{}{
|
||||||
|
"tan": tan,
|
||||||
|
})
|
||||||
|
|
||||||
|
_, err := s.client.Do(*req)
|
||||||
|
return err
|
||||||
|
}
|
150
vendor/github.com/smueller18/goinwx/contact.go
generated
vendored
Normal file
150
vendor/github.com/smueller18/goinwx/contact.go
generated
vendored
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
package goinwx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fatih/structs"
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
methodContactInfo = "contact.info"
|
||||||
|
methodContactList = "contact.list"
|
||||||
|
methodContactCreate = "contact.create"
|
||||||
|
methodContactDelete = "contact.delete"
|
||||||
|
methodContactUpdate = "contact.update"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ContactService interface {
|
||||||
|
Create(*ContactCreateRequest) (int, error)
|
||||||
|
Update(*ContactUpdateRequest) error
|
||||||
|
Delete(int) error
|
||||||
|
Info(int) (*ContactInfoResponse, error)
|
||||||
|
List(string) (*ContactListResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContactServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ContactService = &ContactServiceOp{}
|
||||||
|
|
||||||
|
type ContactCreateRequest struct {
|
||||||
|
Type string `structs:"type"`
|
||||||
|
Name string `structs:"name"`
|
||||||
|
Org string `structs:"org,omitempty"`
|
||||||
|
Street string `structs:"street"`
|
||||||
|
City string `structs:"city"`
|
||||||
|
PostalCode string `structs:"pc"`
|
||||||
|
StateProvince string `structs:"sp,omitempty"`
|
||||||
|
CountryCode string `structs:"cc"`
|
||||||
|
Voice string `structs:"voice"`
|
||||||
|
Fax string `structs:"fax,omitempty"`
|
||||||
|
Email string `structs:"email"`
|
||||||
|
Remarks string `structs:"remarks,omitempty"`
|
||||||
|
Protection bool `structs:"protection,omitempty"`
|
||||||
|
Testing bool `structs:"testing,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContactUpdateRequest struct {
|
||||||
|
Id int `structs:"id"`
|
||||||
|
Name string `structs:"name,omitempty"`
|
||||||
|
Org string `structs:"org,omitempty"`
|
||||||
|
Street string `structs:"street,omitempty"`
|
||||||
|
City string `structs:"city,omitempty"`
|
||||||
|
PostalCode string `structs:"pc,omitempty"`
|
||||||
|
StateProvince string `structs:"sp,omitempty"`
|
||||||
|
CountryCode string `structs:"cc,omitempty"`
|
||||||
|
Voice string `structs:"voice,omitempty"`
|
||||||
|
Fax string `structs:"fax,omitempty"`
|
||||||
|
Email string `structs:"email,omitempty"`
|
||||||
|
Remarks string `structs:"remarks,omitempty"`
|
||||||
|
Protection bool `structs:"protection,omitempty"`
|
||||||
|
Testing bool `structs:"testing,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContactInfoResponse struct {
|
||||||
|
Contact Contact `mapstructure:"contact"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContactListResponse struct {
|
||||||
|
Count int
|
||||||
|
Contacts []Contact `mapstructure:"contact"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ContactServiceOp) Create(request *ContactCreateRequest) (int, error) {
|
||||||
|
req := s.client.NewRequest(methodContactCreate, structs.Map(request))
|
||||||
|
|
||||||
|
resp, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result map[string]int
|
||||||
|
err = mapstructure.Decode(*resp, &result)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result["id"], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ContactServiceOp) Delete(roId int) error {
|
||||||
|
req := s.client.NewRequest(methodContactDelete, map[string]interface{}{
|
||||||
|
"id": roId,
|
||||||
|
})
|
||||||
|
|
||||||
|
_, err := s.client.Do(*req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ContactServiceOp) Update(request *ContactUpdateRequest) error {
|
||||||
|
req := s.client.NewRequest(methodContactUpdate, structs.Map(request))
|
||||||
|
|
||||||
|
_, err := s.client.Do(*req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ContactServiceOp) Info(contactId int) (*ContactInfoResponse, error) {
|
||||||
|
var requestMap = make(map[string]interface{})
|
||||||
|
requestMap["wide"] = 1
|
||||||
|
|
||||||
|
if contactId != 0 {
|
||||||
|
requestMap["id"] = contactId
|
||||||
|
}
|
||||||
|
|
||||||
|
req := s.client.NewRequest(methodContactInfo, requestMap)
|
||||||
|
|
||||||
|
resp, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result ContactInfoResponse
|
||||||
|
err = mapstructure.Decode(*resp, &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ContactServiceOp) List(search string) (*ContactListResponse, error) {
|
||||||
|
var requestMap = make(map[string]interface{})
|
||||||
|
|
||||||
|
if search != "" {
|
||||||
|
requestMap["search"] = search
|
||||||
|
}
|
||||||
|
req := s.client.NewRequest(methodContactList, requestMap)
|
||||||
|
|
||||||
|
resp, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result ContactListResponse
|
||||||
|
err = mapstructure.Decode(*resp, &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
303
vendor/github.com/smueller18/goinwx/domain.go
generated
vendored
Normal file
303
vendor/github.com/smueller18/goinwx/domain.go
generated
vendored
Normal file
|
@ -0,0 +1,303 @@
|
||||||
|
package goinwx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fatih/structs"
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
methodDomainCheck = "domain.check"
|
||||||
|
methodDomainCreate = "domain.create"
|
||||||
|
methodDomainDelete = "domain.delete"
|
||||||
|
methodDomainGetPrices = "domain.getPrices"
|
||||||
|
methodDomainGetRules = "domain.getRules"
|
||||||
|
methodDomainInfo = "domain.info"
|
||||||
|
methodDomainList = "domain.list"
|
||||||
|
methodDomainLog = "domain.log"
|
||||||
|
methodDomainPush = "domain.push"
|
||||||
|
methodDomainRenew = "domain.renew"
|
||||||
|
methodDomainRestore = "domain.restore"
|
||||||
|
methodDomainStats = "domain.stats"
|
||||||
|
methodDomainTrade = "domain.trade"
|
||||||
|
methodDomainTransfer = "domain.transfer"
|
||||||
|
methodDomainTransferOut = "domain.transferOut"
|
||||||
|
methodDomainUpdate = "domain.update"
|
||||||
|
methodDomainWhois = "domain.whois"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DomainService interface {
|
||||||
|
Check(domains []string) ([]DomainCheckResponse, error)
|
||||||
|
Register(request *DomainRegisterRequest) (*DomainRegisterResponse, error)
|
||||||
|
Delete(domain string, scheduledDate time.Time) error
|
||||||
|
Info(domain string, roId int) (*DomainInfoResponse, error)
|
||||||
|
GetPrices(tlds []string) ([]DomainPriceResponse, error)
|
||||||
|
List(*DomainListRequest) (*DomainList, error)
|
||||||
|
Whois(domain string) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type DomainServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ DomainService = &DomainServiceOp{}
|
||||||
|
|
||||||
|
type domainCheckResponseRoot struct {
|
||||||
|
Domains []DomainCheckResponse `mapstructure:"domain"`
|
||||||
|
}
|
||||||
|
type DomainCheckResponse struct {
|
||||||
|
Available int `mapstructure:"avail"`
|
||||||
|
Status string `mapstructure:"status"`
|
||||||
|
Name string `mapstructure:"name"`
|
||||||
|
Domain string `mapstructure:"domain"`
|
||||||
|
TLD string `mapstructure:"tld"`
|
||||||
|
CheckMethod string `mapstructure:"checkmethod"`
|
||||||
|
Price float32 `mapstructure:"price"`
|
||||||
|
CheckTime float32 `mapstructure:"checktime"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type domainPriceResponseRoot struct {
|
||||||
|
Prices []DomainPriceResponse `mapstructure:"price"`
|
||||||
|
}
|
||||||
|
type DomainPriceResponse struct {
|
||||||
|
Tld string `mapstructure:"tld"`
|
||||||
|
Currency string `mapstructure:"currency"`
|
||||||
|
CreatePrice float32 `mapstructure:"createPrice"`
|
||||||
|
MonthlyCreatePrice float32 `mapstructure:"monthlyCreatePrice"`
|
||||||
|
TransferPrice float32 `mapstructure:"transferPrice"`
|
||||||
|
RenewalPrice float32 `mapstructure:"renewalPrice"`
|
||||||
|
MonthlyRenewalPrice float32 `mapstructure:"monthlyRenewalPrice"`
|
||||||
|
UpdatePrice float32 `mapstructure:"updatePrice"`
|
||||||
|
TradePrice float32 `mapstructure:"tradePrice"`
|
||||||
|
TrusteePrice float32 `mapstructure:"trusteePrice"`
|
||||||
|
MonthlyTrusteePrice float32 `mapstructure:"monthlyTrusteePrice"`
|
||||||
|
CreatePeriod int `mapstructure:"createPeriod"`
|
||||||
|
TransferPeriod int `mapstructure:"transferPeriod"`
|
||||||
|
RenewalPeriod int `mapstructure:"renewalPeriod"`
|
||||||
|
TradePeriod int `mapstructure:"tradePeriod"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DomainRegisterRequest struct {
|
||||||
|
Domain string `structs:"domain"`
|
||||||
|
Period string `structs:"period,omitempty"`
|
||||||
|
Registrant int `structs:"registrant"`
|
||||||
|
Admin int `structs:"admin"`
|
||||||
|
Tech int `structs:"tech"`
|
||||||
|
Billing int `structs:"billing"`
|
||||||
|
Nameservers []string `structs:"ns,omitempty"`
|
||||||
|
TransferLock string `structs:"transferLock,omitempty"`
|
||||||
|
RenewalMode string `structs:"renewalMode,omitempty"`
|
||||||
|
WhoisProvider string `structs:"whoisProvider,omitempty"`
|
||||||
|
WhoisUrl string `structs:"whoisUrl,omitempty"`
|
||||||
|
ScDate string `structs:"scDate,omitempty"`
|
||||||
|
ExtDate string `structs:"extDate,omitempty"`
|
||||||
|
Asynchron string `structs:"asynchron,omitempty"`
|
||||||
|
Voucher string `structs:"voucher,omitempty"`
|
||||||
|
Testing string `structs:"testing,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DomainRegisterResponse struct {
|
||||||
|
RoId int
|
||||||
|
Price float32
|
||||||
|
Currency string
|
||||||
|
}
|
||||||
|
|
||||||
|
type DomainInfoResponse struct {
|
||||||
|
RoId int `mapstructure:"roId"`
|
||||||
|
Domain string `mapstructure:"domain"`
|
||||||
|
DomainAce string `mapstructure:"domainAce"`
|
||||||
|
Period string `mapstructure:"period"`
|
||||||
|
CrDate time.Time `mapstructure:"crDate"`
|
||||||
|
ExDate time.Time `mapstructure:"exDate"`
|
||||||
|
UpDate time.Time `mapstructure:"upDate"`
|
||||||
|
ReDate time.Time `mapstructure:"reDate"`
|
||||||
|
ScDate time.Time `mapstructure:"scDate"`
|
||||||
|
TransferLock int `mapstructure:"transferLock"`
|
||||||
|
Status string `mapstructure:"status"`
|
||||||
|
AuthCode string `mapstructure:"authCode"`
|
||||||
|
RenewalMode string `mapstructure:"renewalMode"`
|
||||||
|
TransferMode string `mapstructure:"transferMode"`
|
||||||
|
Registrant int `mapstructure:"registrant"`
|
||||||
|
Admin int `mapstructure:"admin"`
|
||||||
|
Tech int `mapstructure:"tech"`
|
||||||
|
Billing int `mapstructure:"billing"`
|
||||||
|
Nameservers []string `mapstructure:"ns"`
|
||||||
|
NoDelegation string `mapstructure:"noDelegation"`
|
||||||
|
Contacts map[string]Contact `mapstructure:"contact"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Contact struct {
|
||||||
|
RoId int
|
||||||
|
Id string
|
||||||
|
Type string
|
||||||
|
Name string
|
||||||
|
Org string
|
||||||
|
Street string
|
||||||
|
City string
|
||||||
|
PostalCode string `mapstructure:"pc"`
|
||||||
|
StateProvince string `mapstructure:"sp"`
|
||||||
|
Country string `mapstructure:"cc"`
|
||||||
|
Phone string `mapstructure:"voice"`
|
||||||
|
Fax string
|
||||||
|
Email string
|
||||||
|
Remarks string
|
||||||
|
Protection string
|
||||||
|
}
|
||||||
|
|
||||||
|
type DomainListRequest struct {
|
||||||
|
Domain string `structs:"domain,omitempty"`
|
||||||
|
RoId int `structs:"roId,omitempty"`
|
||||||
|
Status int `structs:"status,omitempty"`
|
||||||
|
Registrant int `structs:"registrant,omitempty"`
|
||||||
|
Admin int `structs:"admin,omitempty"`
|
||||||
|
Tech int `structs:"tech,omitempty"`
|
||||||
|
Billing int `structs:"billing,omitempty"`
|
||||||
|
RenewalMode int `structs:"renewalMode,omitempty"`
|
||||||
|
TransferLock int `structs:"transferLock,omitempty"`
|
||||||
|
NoDelegation int `structs:"noDelegation,omitempty"`
|
||||||
|
Tag int `structs:"tag,omitempty"`
|
||||||
|
Order int `structs:"order,omitempty"`
|
||||||
|
Page int `structs:"page,omitempty"`
|
||||||
|
Pagelimit int `structs:"pagelimit,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DomainList struct {
|
||||||
|
Count int
|
||||||
|
Domains []DomainInfoResponse `mapstructure:"domain"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DomainServiceOp) Check(domains []string) ([]DomainCheckResponse, error) {
|
||||||
|
req := s.client.NewRequest(methodDomainCheck, map[string]interface{}{
|
||||||
|
"domain": domains,
|
||||||
|
"wide": "2",
|
||||||
|
})
|
||||||
|
|
||||||
|
resp, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(domainCheckResponseRoot)
|
||||||
|
err = mapstructure.Decode(*resp, &root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Domains, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DomainServiceOp) GetPrices(tlds []string) ([]DomainPriceResponse, error) {
|
||||||
|
req := s.client.NewRequest(methodDomainGetPrices, map[string]interface{}{
|
||||||
|
"tld": tlds,
|
||||||
|
"vat": false,
|
||||||
|
})
|
||||||
|
|
||||||
|
resp, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(domainPriceResponseRoot)
|
||||||
|
err = mapstructure.Decode(*resp, &root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Prices, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DomainServiceOp) Register(request *DomainRegisterRequest) (*DomainRegisterResponse, error) {
|
||||||
|
req := s.client.NewRequest(methodDomainCreate, structs.Map(request))
|
||||||
|
|
||||||
|
resp, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result DomainRegisterResponse
|
||||||
|
err = mapstructure.Decode(*resp, &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DomainServiceOp) Delete(domain string, scheduledDate time.Time) error {
|
||||||
|
req := s.client.NewRequest(methodDomainDelete, map[string]interface{}{
|
||||||
|
"domain": domain,
|
||||||
|
"scDate": scheduledDate.Format(time.RFC3339),
|
||||||
|
})
|
||||||
|
|
||||||
|
_, err := s.client.Do(*req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DomainServiceOp) Info(domain string, roId int) (*DomainInfoResponse, error) {
|
||||||
|
req := s.client.NewRequest(methodDomainInfo, map[string]interface{}{
|
||||||
|
"domain": domain,
|
||||||
|
"wide": "2",
|
||||||
|
})
|
||||||
|
if roId != 0 {
|
||||||
|
req.Args["roId"] = roId
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result DomainInfoResponse
|
||||||
|
err = mapstructure.Decode(*resp, &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
fmt.Println("Response", result)
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DomainServiceOp) List(request *DomainListRequest) (*DomainList, error) {
|
||||||
|
if request == nil {
|
||||||
|
return nil, errors.New("Request can't be nil")
|
||||||
|
}
|
||||||
|
requestMap := structs.Map(request)
|
||||||
|
requestMap["wide"] = "2"
|
||||||
|
|
||||||
|
req := s.client.NewRequest(methodDomainList, requestMap)
|
||||||
|
|
||||||
|
resp, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result DomainList
|
||||||
|
err = mapstructure.Decode(*resp, &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DomainServiceOp) Whois(domain string) (string, error) {
|
||||||
|
req := s.client.NewRequest(methodDomainWhois, map[string]interface{}{
|
||||||
|
"domain": domain,
|
||||||
|
})
|
||||||
|
|
||||||
|
resp, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result map[string]string
|
||||||
|
err = mapstructure.Decode(*resp, &result)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result["whois"], nil
|
||||||
|
}
|
133
vendor/github.com/smueller18/goinwx/goinwx.go
generated
vendored
Normal file
133
vendor/github.com/smueller18/goinwx/goinwx.go
generated
vendored
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
package goinwx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/kolo/xmlrpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
libraryVersion = "0.4.0"
|
||||||
|
APIBaseUrl = "https://api.domrobot.com/xmlrpc/"
|
||||||
|
APISandboxBaseUrl = "https://api.ote.domrobot.com/xmlrpc/"
|
||||||
|
APILanguage = "eng"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client manages communication with INWX API.
|
||||||
|
type Client struct {
|
||||||
|
// HTTP client used to communicate with the INWX API.
|
||||||
|
RPCClient *xmlrpc.Client
|
||||||
|
|
||||||
|
// Base URL for API requests.
|
||||||
|
BaseURL *url.URL
|
||||||
|
|
||||||
|
// API username
|
||||||
|
Username string
|
||||||
|
|
||||||
|
// API password
|
||||||
|
Password string
|
||||||
|
|
||||||
|
// User agent for client
|
||||||
|
APILanguage string
|
||||||
|
|
||||||
|
// Services used for communicating with the API
|
||||||
|
Account AccountService
|
||||||
|
Domains DomainService
|
||||||
|
Nameservers NameserverService
|
||||||
|
Contacts ContactService
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientOptions struct {
|
||||||
|
Sandbox bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Request struct {
|
||||||
|
ServiceMethod string
|
||||||
|
Args map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response is a INWX API response. This wraps the standard http.Response returned from INWX.
|
||||||
|
type Response struct {
|
||||||
|
Code int `xmlrpc:"code"`
|
||||||
|
Message string `xmlrpc:"msg"`
|
||||||
|
ReasonCode string `xmlrpc:"reasonCode"`
|
||||||
|
Reason string `xmlrpc:"reason"`
|
||||||
|
ResponseData map[string]interface{} `xmlrpc:"resData"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// An ErrorResponse reports the error caused by an API request
|
||||||
|
type ErrorResponse struct {
|
||||||
|
Code int `xmlrpc:"code"`
|
||||||
|
Message string `xmlrpc:"msg"`
|
||||||
|
ReasonCode string `xmlrpc:"reasonCode"`
|
||||||
|
Reason string `xmlrpc:"reason"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient returns a new INWX API client.
|
||||||
|
func NewClient(username, password string, opts *ClientOptions) *Client {
|
||||||
|
var useSandbox bool
|
||||||
|
if opts != nil {
|
||||||
|
useSandbox = opts.Sandbox
|
||||||
|
}
|
||||||
|
|
||||||
|
var baseURL *url.URL
|
||||||
|
|
||||||
|
if useSandbox {
|
||||||
|
baseURL, _ = url.Parse(APISandboxBaseUrl)
|
||||||
|
} else {
|
||||||
|
baseURL, _ = url.Parse(APIBaseUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
rpcClient, _ := xmlrpc.NewClient(baseURL.String(), nil)
|
||||||
|
|
||||||
|
client := &Client{RPCClient: rpcClient,
|
||||||
|
BaseURL: baseURL,
|
||||||
|
Username: username,
|
||||||
|
Password: password,
|
||||||
|
}
|
||||||
|
|
||||||
|
client.Account = &AccountServiceOp{client: client}
|
||||||
|
client.Domains = &DomainServiceOp{client: client}
|
||||||
|
client.Nameservers = &NameserverServiceOp{client: client}
|
||||||
|
client.Contacts = &ContactServiceOp{client: client}
|
||||||
|
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRequest creates an API request.
|
||||||
|
func (c *Client) NewRequest(serviceMethod string, args map[string]interface{}) *Request {
|
||||||
|
if args != nil {
|
||||||
|
args["lang"] = APILanguage
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Request{ServiceMethod: serviceMethod, Args: args}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do sends an API request and returns the API response.
|
||||||
|
func (c *Client) Do(req Request) (*map[string]interface{}, error) {
|
||||||
|
var resp Response
|
||||||
|
err := c.RPCClient.Call(req.ServiceMethod, req.Args, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &resp.ResponseData, CheckResponse(&resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ErrorResponse) Error() string {
|
||||||
|
if r.Reason != "" {
|
||||||
|
return fmt.Sprintf("(%d) %s. Reason: (%s) %s",
|
||||||
|
r.Code, r.Message, r.ReasonCode, r.Reason)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("(%d) %s", r.Code, r.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckResponse checks the API response for errors, and returns them if present.
|
||||||
|
func CheckResponse(r *Response) error {
|
||||||
|
if c := r.Code; c >= 1000 && c <= 1500 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ErrorResponse{Code: r.Code, Message: r.Message, Reason: r.Reason, ReasonCode: r.ReasonCode}
|
||||||
|
}
|
275
vendor/github.com/smueller18/goinwx/nameserver.go
generated
vendored
Normal file
275
vendor/github.com/smueller18/goinwx/nameserver.go
generated
vendored
Normal file
|
@ -0,0 +1,275 @@
|
||||||
|
package goinwx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fatih/structs"
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
methodNameserverCheck = "nameserver.check"
|
||||||
|
methodNameserverCreate = "nameserver.create"
|
||||||
|
methodNameserverCreateRecord = "nameserver.createRecord"
|
||||||
|
methodNameserverDelete = "nameserver.delete"
|
||||||
|
methodNameserverDeleteRecord = "nameserver.deleteRecord"
|
||||||
|
methodNameserverInfo = "nameserver.info"
|
||||||
|
methodNameserverList = "nameserver.list"
|
||||||
|
methodNameserverUpdate = "nameserver.update"
|
||||||
|
methodNameserverUpdateRecord = "nameserver.updateRecord"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NameserverService interface {
|
||||||
|
Check(domain string, nameservers []string) (*NameserverCheckResponse, error)
|
||||||
|
Create(*NameserverCreateRequest) (int, error)
|
||||||
|
Info(*NameserverInfoRequest) (*NamserverInfoResponse, error)
|
||||||
|
List(domain string) (*NamserverListResponse, error)
|
||||||
|
CreateRecord(*NameserverRecordRequest) (int, error)
|
||||||
|
UpdateRecord(recId int, request *NameserverRecordRequest) error
|
||||||
|
DeleteRecord(recId int) error
|
||||||
|
FindRecordById(recId int) (*NameserverRecord, *NameserverDomain, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type NameserverServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ NameserverService = &NameserverServiceOp{}
|
||||||
|
|
||||||
|
type NameserverCheckResponse struct {
|
||||||
|
Details []string
|
||||||
|
Status string
|
||||||
|
}
|
||||||
|
|
||||||
|
type NameserverRecordRequest struct {
|
||||||
|
RoId int `structs:"roId,omitempty"`
|
||||||
|
Domain string `structs:"domain,omitempty"`
|
||||||
|
Type string `structs:"type"`
|
||||||
|
Content string `structs:"content"`
|
||||||
|
Name string `structs:"name,omitempty"`
|
||||||
|
Ttl int `structs:"ttl,omitempty"`
|
||||||
|
Priority int `structs:"prio,omitempty"`
|
||||||
|
UrlRedirectType string `structs:"urlRedirectType,omitempty"`
|
||||||
|
UrlRedirectTitle string `structs:"urlRedirectTitle,omitempty"`
|
||||||
|
UrlRedirectDescription string `structs:"urlRedirectDescription,omitempty"`
|
||||||
|
UrlRedirectFavIcon string `structs:"urlRedirectFavIcon,omitempty"`
|
||||||
|
UrlRedirectKeywords string `structs:"urlRedirectKeywords,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NameserverCreateRequest struct {
|
||||||
|
Domain string `structs:"domain"`
|
||||||
|
Type string `structs:"type"`
|
||||||
|
Nameservers []string `structs:"ns,omitempty"`
|
||||||
|
MasterIp string `structs:"masterIp,omitempty"`
|
||||||
|
Web string `structs:"web,omitempty"`
|
||||||
|
Mail string `structs:"mail,omitempty"`
|
||||||
|
SoaEmail string `structs:"soaEmail,omitempty"`
|
||||||
|
UrlRedirectType string `structs:"urlRedirectType,omitempty"`
|
||||||
|
UrlRedirectTitle string `structs:"urlRedirectTitle,omitempty"`
|
||||||
|
UrlRedirectDescription string `structs:"urlRedirectDescription,omitempty"`
|
||||||
|
UrlRedirectFavIcon string `structs:"urlRedirectFavIcon,omitempty"`
|
||||||
|
UrlRedirectKeywords string `structs:"urlRedirectKeywords,omitempty"`
|
||||||
|
Testing bool `structs:"testing,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NameserverInfoRequest struct {
|
||||||
|
Domain string `structs:"domain,omitempty"`
|
||||||
|
RoId int `structs:"roId,omitempty"`
|
||||||
|
RecordId int `structs:"recordId,omitempty"`
|
||||||
|
Type string `structs:"type,omitempty"`
|
||||||
|
Name string `structs:"name,omitempty"`
|
||||||
|
Content string `structs:"content,omitempty"`
|
||||||
|
Ttl int `structs:"ttl,omitempty"`
|
||||||
|
Prio int `structs:"prio,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NamserverInfoResponse struct {
|
||||||
|
RoId int
|
||||||
|
Domain string
|
||||||
|
Type string
|
||||||
|
MasterIp string
|
||||||
|
LastZoneCheck time.Time
|
||||||
|
SlaveDns interface{}
|
||||||
|
SOAserial string
|
||||||
|
Count int
|
||||||
|
Records []NameserverRecord `mapstructure:"record"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NameserverRecord struct {
|
||||||
|
Id int
|
||||||
|
Name string
|
||||||
|
Type string
|
||||||
|
Content string
|
||||||
|
Ttl int
|
||||||
|
Prio int
|
||||||
|
UrlRedirectType string
|
||||||
|
UrlRedirectTitle string
|
||||||
|
UrlRedirectDescription string
|
||||||
|
UrlRedirectKeywords string
|
||||||
|
UrlRedirectFavIcon string
|
||||||
|
}
|
||||||
|
|
||||||
|
type NamserverListResponse struct {
|
||||||
|
Count int
|
||||||
|
Domains []NameserverDomain `mapstructure:"domains"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NameserverDomain struct {
|
||||||
|
RoId int `mapstructure:"roId"`
|
||||||
|
Domain string `mapstructure:"domain"`
|
||||||
|
Type string `mapstructure:"type"`
|
||||||
|
MasterIp string `mapstructure:"masterIp"`
|
||||||
|
Mail string `mapstructure:"mail"`
|
||||||
|
Web string `mapstructure:"web"`
|
||||||
|
Url string `mapstructure:"url"`
|
||||||
|
Ipv4 string `mapstructure:"ipv4"`
|
||||||
|
Ipv6 string `mapstructure:"ipv6"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NameserverServiceOp) Check(domain string, nameservers []string) (*NameserverCheckResponse, error) {
|
||||||
|
req := s.client.NewRequest(methodNameserverCheck, map[string]interface{}{
|
||||||
|
"domain": domain,
|
||||||
|
"ns": nameservers,
|
||||||
|
})
|
||||||
|
|
||||||
|
resp, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result NameserverCheckResponse
|
||||||
|
err = mapstructure.Decode(*resp, &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NameserverServiceOp) Info(request *NameserverInfoRequest) (*NamserverInfoResponse, error) {
|
||||||
|
req := s.client.NewRequest(methodNameserverInfo, structs.Map(request))
|
||||||
|
|
||||||
|
resp, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var result NamserverInfoResponse
|
||||||
|
err = mapstructure.Decode(*resp, &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NameserverServiceOp) List(domain string) (*NamserverListResponse, error) {
|
||||||
|
requestMap := map[string]interface{}{
|
||||||
|
"domain": "*",
|
||||||
|
"wide": 2,
|
||||||
|
}
|
||||||
|
if domain != "" {
|
||||||
|
requestMap["domain"] = domain
|
||||||
|
}
|
||||||
|
req := s.client.NewRequest(methodNameserverList, requestMap)
|
||||||
|
|
||||||
|
resp, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var result NamserverListResponse
|
||||||
|
err = mapstructure.Decode(*resp, &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NameserverServiceOp) Create(request *NameserverCreateRequest) (int, error) {
|
||||||
|
req := s.client.NewRequest(methodNameserverCreate, structs.Map(request))
|
||||||
|
|
||||||
|
resp, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result map[string]int
|
||||||
|
err = mapstructure.Decode(*resp, &result)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result["roId"], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NameserverServiceOp) CreateRecord(request *NameserverRecordRequest) (int, error) {
|
||||||
|
req := s.client.NewRequest(methodNameserverCreateRecord, structs.Map(request))
|
||||||
|
|
||||||
|
resp, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result map[string]int
|
||||||
|
err = mapstructure.Decode(*resp, &result)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result["id"], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NameserverServiceOp) UpdateRecord(recId int, request *NameserverRecordRequest) error {
|
||||||
|
if request == nil {
|
||||||
|
return errors.New("Request can't be nil")
|
||||||
|
}
|
||||||
|
requestMap := structs.Map(request)
|
||||||
|
requestMap["id"] = recId
|
||||||
|
|
||||||
|
req := s.client.NewRequest(methodNameserverUpdateRecord, requestMap)
|
||||||
|
|
||||||
|
_, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NameserverServiceOp) DeleteRecord(recId int) error {
|
||||||
|
req := s.client.NewRequest(methodNameserverDeleteRecord, map[string]interface{}{
|
||||||
|
"id": recId,
|
||||||
|
})
|
||||||
|
|
||||||
|
_, err := s.client.Do(*req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NameserverServiceOp) FindRecordById(recId int) (*NameserverRecord, *NameserverDomain, error) {
|
||||||
|
listResp, err := s.client.Nameservers.List("")
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, domainItem := range listResp.Domains {
|
||||||
|
resp, err := s.client.Nameservers.Info(&NameserverInfoRequest{RoId: domainItem.RoId})
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, record := range resp.Records {
|
||||||
|
if record.Id == recId {
|
||||||
|
return &record, &domainItem, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil, fmt.Errorf("couldn't find INWX Record for id %d", recId)
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue