diff --git a/docs/_includes/matrix.html b/docs/_includes/matrix.html
index ad17c409b..ea5c265a1 100644
--- a/docs/_includes/matrix.html
+++ b/docs/_includes/matrix.html
@@ -19,6 +19,7 @@
NAMEDOTCOM |
NS1 |
OCTODNS |
+ OPENSRS |
OVH |
ROUTE53 |
SOFTLAYER |
@@ -70,6 +71,9 @@
|
+
+
+ |
|
@@ -121,6 +125,9 @@
|
+
+
+ |
|
@@ -181,6 +188,9 @@
|
+
+
+ |
|
@@ -213,6 +223,7 @@
|
|
+ |
|
@@ -255,6 +266,7 @@
|
|
|
+ |
|
@@ -299,6 +311,7 @@
|
+ |
|
@@ -349,6 +362,7 @@
|
+ |
|
@@ -383,6 +397,7 @@
|
|
|
+ |
|
@@ -412,6 +427,7 @@
|
|
|
+ |
|
@@ -434,6 +450,7 @@
|
|
|
+ |
|
@@ -475,6 +492,7 @@
|
+ |
|
@@ -525,6 +543,9 @@
|
+
+
+ |
|
@@ -591,6 +612,9 @@
|
+
+
+ |
diff --git a/providers/_all/all.go b/providers/_all/all.go
index 9f32c1880..16aa27b8a 100644
--- a/providers/_all/all.go
+++ b/providers/_all/all.go
@@ -15,6 +15,7 @@ import (
_ "github.com/StackExchange/dnscontrol/providers/namedotcom"
_ "github.com/StackExchange/dnscontrol/providers/ns1"
_ "github.com/StackExchange/dnscontrol/providers/octodns"
+ _ "github.com/StackExchange/dnscontrol/providers/opensrs"
_ "github.com/StackExchange/dnscontrol/providers/ovh"
_ "github.com/StackExchange/dnscontrol/providers/route53"
_ "github.com/StackExchange/dnscontrol/providers/softlayer"
diff --git a/providers/opensrs/opensrsProvider.go b/providers/opensrs/opensrsProvider.go
new file mode 100644
index 000000000..ca0d1ea4f
--- /dev/null
+++ b/providers/opensrs/opensrsProvider.go
@@ -0,0 +1,144 @@
+package opensrs
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "sort"
+ "strings"
+
+ "github.com/StackExchange/dnscontrol/models"
+ "github.com/StackExchange/dnscontrol/providers"
+
+ opensrs "github.com/philhug/opensrs-go/opensrs"
+)
+
+var docNotes = providers.DocumentationNotes{
+ providers.DocCreateDomains: providers.Cannot(),
+ providers.DocOfficiallySupported: providers.Cannot(),
+ providers.CanUseTLSA: providers.Cannot(),
+}
+
+func init() {
+ providers.RegisterRegistrarType("OPENSRS", newReg)
+}
+
+var defaultNameServerNames = []string{
+ "ns1.systemdns.com",
+ "ns2.systemdns.com",
+ "ns3.systemdns.com",
+}
+
+type OpenSRSApi struct {
+ UserName string // reseller user name
+ ApiKey string // API Key
+
+ BaseURL string // An alternate base URI
+ client *opensrs.Client // Client
+}
+
+func (c *OpenSRSApi) GetNameservers(domainName string) ([]*models.Nameserver, error) {
+ return models.StringsToNameservers(defaultNameServerNames), nil
+}
+
+func (c *OpenSRSApi) GetRegistrarCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
+ corrections := []*models.Correction{}
+
+ nameServers, err := c.getNameservers(dc.Name)
+ if err != nil {
+ return nil, err
+ }
+
+ sort.Strings(nameServers)
+ actual := strings.Join(nameServers, ",")
+
+ expectedSet := []string{}
+ for _, ns := range dc.Nameservers {
+ expectedSet = append(expectedSet, ns.Name)
+ }
+ sort.Strings(expectedSet)
+ expected := strings.Join(expectedSet, ",")
+
+ if actual != expected {
+ return []*models.Correction{
+ {
+ Msg: fmt.Sprintf("Update nameservers %s -> %s", actual, expected),
+ F: c.updateNameserversFunc(expectedSet, dc.Name),
+ },
+ }, nil
+ }
+
+ return corrections, nil
+}
+
+// OpenSRS calls
+
+func (c *OpenSRSApi) getClient() *opensrs.Client {
+ return c.client
+}
+
+// Returns the name server names that should be used. If the domain is registered
+// then this method will return the delegation name servers. If this domain
+// is hosted only, then it will return the default OpenSRS name servers.
+func (c *OpenSRSApi) getNameservers(domainName string) ([]string, error) {
+ client := c.getClient()
+
+ status, err := client.Domains.GetDomain(domainName, "status", 1)
+ if err != nil {
+ return nil, err
+ }
+
+ if status.Attributes.LockState == "0" {
+ dom, err := client.Domains.GetDomain(domainName, "nameservers", 1)
+ if err != nil {
+ return nil, err
+ }
+ return dom.Attributes.NameserverList.ToString(), nil
+ } else {
+ return nil, errors.New("Domain is locked")
+ }
+}
+
+// Returns a function that can be invoked to change the delegation of the domain to the given name server names.
+func (c *OpenSRSApi) updateNameserversFunc(nameServerNames []string, domainName string) func() error {
+ return func() error {
+ client := c.getClient()
+
+ _, err := client.Domains.UpdateDomainNameservers(domainName, nameServerNames)
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+}
+
+// constructors
+
+func newReg(conf map[string]string) (providers.Registrar, error) {
+ return newProvider(conf, nil)
+}
+
+func newProvider(m map[string]string, metadata json.RawMessage) (*OpenSRSApi, error) {
+ api := &OpenSRSApi{}
+ api.ApiKey = m["apikey"]
+
+ if api.ApiKey == "" {
+ return nil, fmt.Errorf("OpenSRS apikey must be provided.")
+ }
+
+ api.UserName = m["username"]
+ if api.UserName == "" {
+ return nil, fmt.Errorf("OpenSRS username key must be provided.")
+ }
+
+ if m["baseurl"] != "" {
+ api.BaseURL = m["baseurl"]
+ }
+
+ api.client = opensrs.NewClient(opensrs.NewApiKeyMD5Credentials(api.UserName, api.ApiKey))
+ if api.BaseURL != "" {
+ api.client.BaseURL = api.BaseURL
+ }
+
+ return api, nil
+}
diff --git a/vendor/github.com/philhug/opensrs-go/LICENSE b/vendor/github.com/philhug/opensrs-go/LICENSE
new file mode 100644
index 000000000..88a2305cf
--- /dev/null
+++ b/vendor/github.com/philhug/opensrs-go/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Philipp Hug
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/philhug/opensrs-go/opensrs/authentication.go b/vendor/github.com/philhug/opensrs-go/opensrs/authentication.go
new file mode 100644
index 000000000..eace8c91a
--- /dev/null
+++ b/vendor/github.com/philhug/opensrs-go/opensrs/authentication.go
@@ -0,0 +1,44 @@
+package opensrs
+
+import (
+ "crypto/md5"
+ "encoding/hex"
+)
+
+const (
+ httpHeaderUserName = "X-UserName"
+ httpHeaderSignature = "X-Signature"
+)
+
+// Provides credentials that can be used for authenticating with OpenSRS.
+//
+type Credentials interface {
+ // Returns the HTTP headers that should be set
+ // to authenticate the HTTP Request.
+ Headers(xml []byte) map[string]string
+}
+
+// API key MD5 authentication
+type apiKeyMD5Credentials struct {
+ userName string
+ apiKey string
+}
+
+// NewApiKeyMD5Credentials construct Credentials using the OpenSRS MD5 Api Key method.
+func NewApiKeyMD5Credentials(userName string, apiKey string) Credentials {
+ return &apiKeyMD5Credentials{userName: userName, apiKey: apiKey}
+}
+
+func (c *apiKeyMD5Credentials) Headers(xml []byte) map[string]string {
+ h := md5.New()
+ h.Write(xml)
+ h.Write([]byte(c.apiKey))
+ m := hex.EncodeToString(h.Sum(nil))
+
+ h = md5.New()
+ h.Write([]byte(m))
+ h.Write([]byte(c.apiKey))
+ m = hex.EncodeToString(h.Sum(nil))
+
+ return map[string]string{httpHeaderUserName: c.userName, httpHeaderSignature: m}
+}
diff --git a/vendor/github.com/philhug/opensrs-go/opensrs/domains.go b/vendor/github.com/philhug/opensrs-go/opensrs/domains.go
new file mode 100644
index 000000000..ea073ec41
--- /dev/null
+++ b/vendor/github.com/philhug/opensrs-go/opensrs/domains.go
@@ -0,0 +1,56 @@
+package opensrs
+
+import (
+ "strconv"
+)
+
+// DomainsService handles communication with the domain related
+// methods of the OpenSRS API.
+//
+type DomainsService struct {
+ client *Client
+}
+
+// GetDomain fetches a domain.
+//
+func (s *DomainsService) GetDomain(domainIdentifier string, domainType string, limit int) (*OpsResponse, error) {
+ opsResponse := OpsResponse{}
+ opsRequestAttributes := OpsRequestAttributes{Domain: domainIdentifier, Limit: strconv.Itoa(limit), Type: domainType}
+
+ resp, err := s.client.post("GET", "DOMAIN", opsRequestAttributes, &opsResponse)
+ if err != nil {
+ return nil, err
+ }
+ _ = resp
+ return &opsResponse, nil
+}
+
+// UpdateDomainNameservers changes domain servers on a domain.
+//
+func (s *DomainsService) UpdateDomainNameservers(domainIdentifier string, newDs []string) (*OpsResponse, error) {
+ opsResponse := OpsResponse{}
+
+ opsRequestAttributes := OpsRequestAttributes{Domain: domainIdentifier, AssignNs: newDs, OpType: "assign"}
+
+ resp, err := s.client.post("ADVANCED_UPDATE_NAMESERVERS", "DOMAIN", opsRequestAttributes, &opsResponse)
+ if err != nil {
+ return nil, err
+ }
+ _ = resp
+ return &opsResponse, nil
+}
+
+// GetDNSZone fetches zone info for a domain.
+//
+func (s *DomainsService) GetDNSZone(domainIdentifier string) (*OpsResponse, error) {
+ opsResponse := OpsResponse{}
+ opsRequestAttributes := OpsRequestAttributes{Domain: domainIdentifier}
+
+ resp, err := s.client.post("GET_DNS_ZONE", "DOMAIN", opsRequestAttributes, &opsResponse)
+ if err != nil {
+ return nil, err
+ }
+ _ = resp
+ return &opsResponse, nil
+}
+
diff --git a/vendor/github.com/philhug/opensrs-go/opensrs/opensrs.go b/vendor/github.com/philhug/opensrs-go/opensrs/opensrs.go
new file mode 100644
index 000000000..def3ba761
--- /dev/null
+++ b/vendor/github.com/philhug/opensrs-go/opensrs/opensrs.go
@@ -0,0 +1,231 @@
+// Package opensrs provides a client for the OpenSRS API.
+// In order to use this package you will need a OpenSRS account.
+package opensrs
+
+import (
+ "bytes"
+ "crypto/tls"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "net/url"
+)
+
+const (
+ // Version identifies the current library version.
+ // This is a pro-forma convention given that Go dependencies
+ // tends to be fetched directly from the repo.
+ // It is also used in the user-agent identify the client.
+ Version = "0.0.1"
+
+ // defaultBaseURL to the OpenSRS production API.
+ //defaultBaseURL = "https://rr-n1-tor.opensrs.net:55443"
+ defaultBaseURL = "https://horizon.opensrs.net:55443"
+
+ // userAgent represents the default user agent used
+ // when no other user agent is set.
+ defaultUserAgent = "opensrs-go/" + Version
+)
+
+// Client represents a client to the OpenSRS API.
+type Client struct {
+ // HttpClient is the underlying HTTP client
+ // used to communicate with the API.
+ HttpClient *http.Client
+
+ // Credentials used for accessing the OpenSRS API
+ Credentials Credentials
+
+ // BaseURL for API requests.
+ // Defaults to the public OpenSRS API, but can be set to a different endpoint (e.g. the sandbox).
+ BaseURL string
+
+ // UserAgent used when communicating with the OpenSRS API.
+ UserAgent string
+
+ // Services used for talking to different parts of the OpenSRS API.
+ Domains *DomainsService
+
+ // Set to true to output debugging logs during API calls
+ Debug bool
+}
+
+// NewClient returns a new OpenSRS API client using the given credentials.
+func NewClient(credentials Credentials) *Client {
+ proxyUrl, _ := url.Parse("http://127.0.0.1:8080")
+
+ tr := &http.Transport{
+ TLSClientConfig: &tls.Config{InsecureSkipVerify : true},
+ Proxy: http.ProxyURL(proxyUrl),
+ }
+ c := &Client{Credentials: credentials, HttpClient: &http.Client{Transport: tr}, BaseURL: defaultBaseURL}
+ c.Domains = &DomainsService{client: c}
+ return c
+}
+
+// NewRequest creates an API request.
+// The path is expected to be a relative path and will be resolved
+// according to the BaseURL of the Client. Paths should always be specified without a preceding slash.
+func (c *Client) NewRequest(method, path string, payload interface{}) (*http.Request, error) {
+ url := c.BaseURL + path
+
+ body := new(bytes.Buffer)
+ if payload != nil {
+ xml, err := ToXml(payload)
+ if err != nil {
+ return nil, err
+ }
+ body = bytes.NewBuffer(xml)
+ }
+
+ req, err := http.NewRequest(method, url, body)
+ if err != nil {
+ return nil, err
+ }
+
+ req.Header.Set("Content-Type", "text/xml")
+ req.Header.Add("Accept", "text/xml")
+ req.Header.Add("User-Agent", formatUserAgent(c.UserAgent))
+ for key, value := range c.Credentials.Headers(body.Bytes()) {
+ req.Header.Add(key, value)
+ }
+
+ return req, nil
+}
+
+// formatUserAgent builds the final user agent to use for HTTP requests.
+//
+// If no custom user agent is provided, the default user agent is used.
+//
+// opensrs-go/1.0
+//
+// If a custom user agent is provided, the final user agent is the combination of the custom user agent
+// prepended by the default user agent.
+//
+// opensrs-go/1.0 customAgentFlag
+//
+func formatUserAgent(customUserAgent string) string {
+ if customUserAgent == "" {
+ return defaultUserAgent
+ }
+
+ return fmt.Sprintf("%s %s", defaultUserAgent, customUserAgent)
+}
+
+func (c *Client) post(action string, object string, attributes OpsRequestAttributes, obj *OpsResponse) (*http.Response, error) {
+ payload := OpsRequest{Action: action, Object: object, Protocol: "XCP", Attributes: attributes}
+ req, err := c.NewRequest("POST", "", payload)
+ if err != nil {
+ return nil, err
+ }
+
+ return c.Do(req, obj)
+}
+
+// Do sends an API request and returns the API response.
+//
+// The API response is JSON decoded and stored in the value pointed by obj,
+// or returned as an error if an API error has occurred.
+// If obj implements the io.Writer interface, the raw response body will be written to obj,
+// without attempting to decode it.
+func (c *Client) Do(req *http.Request, obj *OpsResponse) (*http.Response, error) {
+ if c.Debug {
+ log.Printf("Executing request (%v): %#v", req.URL, req)
+ }
+
+ resp, err := c.HttpClient.Do(req)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ if c.Debug {
+ log.Printf("Response received: %#v", resp)
+ }
+
+ err = CheckResponse(resp)
+ if err != nil {
+ return resp, err
+ }
+
+ // If obj implements the io.Writer,
+ // the response body is decoded into v.
+ if obj != nil {
+ b, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return nil, err
+ }
+ err = FromXml(b, obj)
+ if err != nil {
+ return resp, err
+ }
+
+ err = CheckOpsResponse(resp, obj)
+ if err != nil {
+ return resp, err
+ }
+ }
+
+ return resp, err
+}
+
+// An ErrorResponse represents an API response that generated an error.
+type ErrorResponse struct {
+ HttpResponse *http.Response
+ OpsResponse *OpsResponse
+
+ // human-readable message
+ Message string `json:"message"`
+}
+
+// Error implements the error interface.
+func (r *ErrorResponse) Error() string {
+ s := fmt.Sprintf("%v %v: ",
+ r.HttpResponse.Request.Method, r.HttpResponse.Request.URL)
+ if r.OpsResponse != nil {
+ s = s + fmt.Sprintf("%v %v",
+ r.OpsResponse.ResponseCode,
+ r.OpsResponse.ResponseText)
+ } else {
+ s = s + fmt.Sprintf("%v %v",
+ r.HttpResponse.StatusCode, r.Message)
+ }
+ return s
+}
+
+// CheckResponse checks the API response for errors, and returns them if present.
+// A response is considered an error if the status code is different than 2xx. Specific requests
+// may have additional requirements, but this is sufficient in most of the cases.
+func CheckResponse(resp *http.Response) error {
+ if code := resp.StatusCode; 200 <= code && code <= 299 {
+ return nil
+ }
+
+ errorResponse := &ErrorResponse{}
+ errorResponse.HttpResponse = resp
+
+ b, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return err
+ }
+
+ err = FromXml(b, errorResponse)
+ if err != nil {
+ return err
+ }
+
+ return errorResponse
+}
+
+func CheckOpsResponse(resp *http.Response, or *OpsResponse) error {
+ if or.IsSuccess == "1" {
+ return nil
+ }
+
+ errorResponse := &ErrorResponse{}
+ errorResponse.HttpResponse = resp
+ errorResponse.OpsResponse = or
+
+ return errorResponse
+}
diff --git a/vendor/github.com/philhug/opensrs-go/opensrs/structs.go b/vendor/github.com/philhug/opensrs-go/opensrs/structs.go
new file mode 100644
index 000000000..9a07f2d08
--- /dev/null
+++ b/vendor/github.com/philhug/opensrs-go/opensrs/structs.go
@@ -0,0 +1,92 @@
+package opensrs
+
+type NameserverList []struct {
+ Name string `json:"name"`
+ IpAddress string `json:"ipaddress,omitempty"`
+ Ipv6 string `json:"ipv6,omitempty"`
+ SortOrder string `json:"sortorder,omitempty"`
+}
+
+type ARecord struct {
+ IpAddress string `json:"ipaddress,omitempty"`
+ SubDomain string `json:"subdomain,omitempty"`
+}
+
+type AAAARecord struct {
+ Ipv6Address string `json:"ipv6_address,omitempty"`
+ SubDomain string `json:"subdomain,omitempty"`
+}
+
+type CNAMERecord struct {
+ HostName string `json:"hostname,omitempty"`
+ SubDomain string `json:"subdomain,omitempty"`
+}
+
+type MXRecord struct {
+ Priority string `json:"priority,omitempty"`
+ SubDomain string `json:"subdomain,omitempty"`
+ HostName string `json:"hostname,omitempty"`
+}
+
+type SRVRecord struct {
+ Priority string `json:"priority,omitempty"`
+ Weight string `json:"weight,omitempty"`
+ SubDomain string `json:"subdomain,omitempty"`
+ HostName string `json:"hostname,omitempty"`
+ Port string `json:"port,omitempty"`
+}
+
+type TXTRecord struct {
+ SubDomain string `json:"subdomain,omitempty"`
+ Text string `json:"text,omitempty"`
+}
+
+type DnsRecords struct {
+ A []ARecord `json:"A,omitempty"`
+ AAAA []AAAARecord `json:"AAAA,omitempty"`
+ CNAME []CNAMERecord `json:"CNAME,omitempty"`
+ MX []MXRecord `json:"MX,omitempty"`
+ SRV []SRVRecord `json:"SRV,omitempty"`
+ TXT []TXTRecord `json:"TXT,omitempty"`
+}
+
+func (n NameserverList) ToString() []string {
+ domains := make([]string, len(n))
+ for i, ns := range n {
+ domains[i] = ns.Name
+ }
+ return domains
+}
+
+type OpsRequestAttributes struct {
+ Domain string `json:"domain"`
+ Limit string `json:"limit,omitempty"`
+ Type string `json:"type,omitempty"`
+ Data string `json:"data,omitempty"`
+ AffectDomains string `json:"affect_domains,omitempty"`
+ NameserverList NameserverList `json:"nameserver_list,omitempty"`
+ OpType string `json:"op_type,omitempty"`
+ AssignNs []string `json:"assign_ns,omitempty"`
+}
+
+type OpsResponse struct {
+ Action string `json:"action"`
+ Object string `json:"object"`
+ Protocol string `json:"protocol"`
+ IsSuccess string `json:"is_success"`
+ ResponseCode string `json:"response_code"`
+ ResponseText string `json:"response_text"`
+ Attributes struct {
+ NameserverList NameserverList `json:"nameserver_list,omitempty"`
+ Type string `json:"type,omitempty"`
+ LockState string `json:"lock_state,omitempty"`
+ Records DnsRecords `json:"records,omitempty"`
+ } `json:"attributes"`
+}
+
+type OpsRequest struct {
+ Action string `json:"action"`
+ Object string `json:"object"`
+ Protocol string `json:"protocol"`
+ Attributes OpsRequestAttributes `json:"attributes"`
+}
diff --git a/vendor/github.com/philhug/opensrs-go/opensrs/xml.go b/vendor/github.com/philhug/opensrs-go/opensrs/xml.go
new file mode 100644
index 000000000..4613e4d29
--- /dev/null
+++ b/vendor/github.com/philhug/opensrs-go/opensrs/xml.go
@@ -0,0 +1,176 @@
+package opensrs
+
+import (
+ "encoding/json"
+ "encoding/xml"
+ "errors"
+ "log"
+ "reflect"
+ "strconv"
+ "strings"
+)
+
+type Header struct {
+ XMLName xml.Name `xml:"header"`
+ Version string `xml:"version"`
+}
+
+type Item struct {
+ XMLName xml.Name `xml:"item"`
+ Key string `xml:"key,attr"`
+ DtArray *DtArray `xml:"dt_array,omitempty"`
+ DtAssoc *DtAssoc `xml:"dt_assoc,omitempty"`
+ Value string `xml:",chardata"`
+}
+
+func (i *Item) decode() interface{} {
+ if i.DtAssoc != nil {
+ return i.DtAssoc.decode()
+ }
+ if i.DtArray != nil {
+ return i.DtArray.decode()
+ }
+ return i.Value
+}
+
+type DtArray struct {
+ XMLName xml.Name `xml:"dt_array"`
+ ItemList []Item `xml:"item,omitempty"`
+}
+
+func (d *DtArray) decode() []interface{} {
+ m := make([]interface{}, 0)
+ for _, element := range d.ItemList {
+ m = append(m, element.decode())
+ }
+ return m
+}
+
+type DtAssoc struct {
+ XMLName xml.Name `xml:"dt_assoc"`
+ ItemList []Item `xml:"item,omitempty"`
+}
+
+func (d *DtAssoc) decode() Map {
+ m := make(Map)
+ for _, element := range d.ItemList {
+ m[element.Key] = element.decode()
+ }
+ return m
+}
+
+type DataBlock struct {
+ XMLName xml.Name `xml:"data_block"`
+ DtAssoc *DtAssoc `xml:"dt_assoc,omitempty"`
+ //DtArray DtArray `xml:"dt_array,omitempty"`
+}
+
+type Map map[string]interface{}
+
+func (d *DataBlock) decode() Map {
+ m := make(Map)
+ if d.DtAssoc != nil {
+ return d.DtAssoc.decode()
+ }
+ return m
+}
+
+func encodeItem(key string, value reflect.Value) Item {
+ item := Item{}
+ item.Key = key
+ v := internalEncode(value)
+ s, ok := v.(string)
+ if ok {
+ item.Value = s
+ }
+ dtass, ok := v.(DtAssoc)
+ if ok {
+ item.DtAssoc = &dtass
+ }
+ dtarr, ok := v.(DtArray)
+ if ok {
+ item.DtArray = &dtarr
+ }
+ return item
+}
+
+func internalEncode(v reflect.Value) (p interface{}) {
+ t := v.Type()
+ switch t.Kind() {
+ case reflect.Interface:
+ return internalEncode(v.Elem())
+ case reflect.String:
+ return v.Interface().(string)
+ case reflect.Struct:
+ dt := DtAssoc{}
+
+ for i := 0; i < t.NumField(); i++ {
+ key := strings.ToLower(t.Field(i).Name)
+ value := v.Field(i)
+ item := encodeItem(key, value)
+ dt.ItemList = append(dt.ItemList, item)
+ }
+ return dt
+ case reflect.Map: // DtAssoc
+ dt := DtAssoc{}
+
+ for _, k := range v.MapKeys() {
+ v := v.MapIndex(k)
+ key := k.String()
+ item := encodeItem(key, v)
+ dt.ItemList = append(dt.ItemList, item)
+ }
+ return dt
+ case reflect.Slice: //DtArray
+ dt := DtArray{}
+ for i := 0; i < v.Len(); i++ {
+ key := strconv.Itoa(i)
+ value := v.Index(i)
+ item := encodeItem(key, value)
+ dt.ItemList = append(dt.ItemList, item)
+ }
+ return dt
+ default:
+ log.Println("FAIL, unknown type", t.Kind())
+ }
+ return nil
+}
+
+type Body struct {
+ XMLName xml.Name `xml:"body"`
+ DataBlock DataBlock `xml:"data_block"`
+}
+
+type OPSEnvelope struct {
+ XMLName xml.Name `xml:"OPS_envelope"`
+ Header Header `xml:"header"`
+ Body Body `xml:"body"`
+}
+
+func FromXml(b []byte, v interface{}) error {
+ var q OPSEnvelope
+ err := xml.Unmarshal(b, &q)
+ if err != nil {
+ return err
+ }
+ m := q.Body.DataBlock.decode()
+ jsonString, _ := json.Marshal(m)
+ log.Println(string(jsonString))
+ return json.Unmarshal(jsonString, &v)
+
+}
+
+func ToXml(v interface{}) (b []byte, err error) {
+ jsonString, _ := json.Marshal(v)
+ var m interface{}
+ json.Unmarshal(jsonString, &m)
+
+ q := OPSEnvelope{Header: Header{Version: "0.9"}, Body: Body{}}
+ dtass, ok := internalEncode(reflect.ValueOf(m)).(DtAssoc)
+ if ok {
+ q.Body.DataBlock.DtAssoc = &dtass
+ } else {
+ return nil, errors.New("Encoding failed")
+ }
+ return xml.MarshalIndent(q, "", " ")
+}
diff --git a/vendor/vendor.json b/vendor/vendor.json
index a9f2bc15f..f20203255 100644
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -330,6 +330,12 @@
"revision": "8e652d1a756f2480f1ad28dc211c77a25264e02d",
"revisionTime": "2018-01-09T18:53:19Z"
},
+ {
+ "checksumSHA1": "ln/74dXVPX/i7aDah00ZKqbdpw8=",
+ "path": "github.com/philhug/opensrs-go/opensrs",
+ "revision": "58112e74137c104eb77bdad4b53c51da22b7b6d5",
+ "revisionTime": "2017-11-26T23:10:42Z"
+ },
{
"checksumSHA1": "ynJSWoF6v+3zMnh9R0QmmG6iGV8=",
"path": "github.com/pkg/errors",