mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2025-01-28 10:34:42 +08:00
161 lines
4.2 KiB
Go
161 lines
4.2 KiB
Go
// Package namecheap implements a client for the Namecheap API.
|
|
//
|
|
// In order to use this package you will need a Namecheap account and your API Token.
|
|
package namecheap
|
|
|
|
import (
|
|
"encoding/xml"
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
)
|
|
|
|
const defaultBaseURL = "https://api.namecheap.com/xml.response"
|
|
|
|
// Client represents a client used to make calls to the Namecheap API.
|
|
type Client struct {
|
|
ApiUser string
|
|
ApiToken string
|
|
UserName string
|
|
HttpClient *http.Client
|
|
|
|
// Base URL for API requests.
|
|
// Defaults to the public Namecheap API,
|
|
// but can be set to a different endpoint (e.g. the sandbox).
|
|
// BaseURL should always be specified with a trailing slash.
|
|
BaseURL string
|
|
|
|
*Registrant
|
|
}
|
|
|
|
type ApiRequest struct {
|
|
method string
|
|
command string
|
|
params url.Values
|
|
}
|
|
|
|
type ApiResponse struct {
|
|
Status string `xml:"Status,attr"`
|
|
Command string `xml:"RequestedCommand"`
|
|
Domains []DomainGetListResult `xml:"CommandResponse>DomainGetListResult>Domain"`
|
|
DomainInfo *DomainInfo `xml:"CommandResponse>DomainGetInfoResult"`
|
|
DomainDNSHosts *DomainDNSGetHostsResult `xml:"CommandResponse>DomainDNSGetHostsResult"`
|
|
DomainDNSSetHosts *DomainDNSSetHostsResult `xml:"CommandResponse>DomainDNSSetHostsResult"`
|
|
DomainCreate *DomainCreateResult `xml:"CommandResponse>DomainCreateResult"`
|
|
DomainsCheck []DomainCheckResult `xml:"CommandResponse>DomainCheckResult"`
|
|
DomainNSInfo *DomainNSInfoResult `xml:"CommandResponse>DomainNSInfoResult"`
|
|
DomainDNSSetCustom *DomainDNSSetCustomResult `xml:"CommandResponse>DomainDNSSetCustomResult"`
|
|
Errors []ApiError `xml:"Errors>Error"`
|
|
}
|
|
|
|
// ApiError is the format of the error returned in the api responses.
|
|
type ApiError struct {
|
|
Number int `xml:"Number,attr"`
|
|
Message string `xml:",innerxml"`
|
|
}
|
|
|
|
func (err *ApiError) Error() string {
|
|
return err.Message
|
|
}
|
|
|
|
func NewClient(apiUser, apiToken, userName string) *Client {
|
|
return &Client{
|
|
ApiUser: apiUser,
|
|
ApiToken: apiToken,
|
|
UserName: userName,
|
|
HttpClient: http.DefaultClient,
|
|
BaseURL: defaultBaseURL,
|
|
}
|
|
}
|
|
|
|
// NewRegistrant associates a new registrant with the
|
|
func (client *Client) NewRegistrant(
|
|
firstName, lastName,
|
|
addr1, addr2,
|
|
city, state, postalCode, country,
|
|
phone, email string,
|
|
) {
|
|
client.Registrant = newRegistrant(
|
|
firstName, lastName,
|
|
addr1, addr2,
|
|
city, state, postalCode, country,
|
|
phone, email,
|
|
)
|
|
}
|
|
|
|
func (client *Client) do(request *ApiRequest) (*ApiResponse, error) {
|
|
if request.method == "" {
|
|
return nil, errors.New("request method cannot be blank")
|
|
}
|
|
|
|
body, _, err := client.sendRequest(request)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resp := new(ApiResponse)
|
|
if err = xml.Unmarshal(body, resp); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if resp.Status == "ERROR" {
|
|
errMsg := ""
|
|
for _, apiError := range resp.Errors {
|
|
errMsg += fmt.Sprintf("Error %d: %s\n", apiError.Number, apiError.Message)
|
|
}
|
|
return nil, errors.New(errMsg)
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
func (client *Client) makeRequest(request *ApiRequest) (*http.Request, error) {
|
|
url, err := url.Parse(client.BaseURL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p := request.params
|
|
p.Set("ApiUser", client.ApiUser)
|
|
p.Set("ApiKey", client.ApiToken)
|
|
p.Set("UserName", client.UserName)
|
|
// This param is required by the API, but not actually used.
|
|
p.Set("ClientIp", "127.0.0.1")
|
|
p.Set("Command", request.command)
|
|
url.RawQuery = p.Encode()
|
|
|
|
// UGH
|
|
//
|
|
// Need this for the domain name part of the domains.check endpoint
|
|
url.RawQuery = strings.Replace(url.RawQuery, "%2C", ",", -1)
|
|
|
|
urlString := fmt.Sprintf("%s?%s", client.BaseURL, url.RawQuery)
|
|
req, err := http.NewRequest(request.method, urlString, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return req, nil
|
|
}
|
|
|
|
func (client *Client) sendRequest(request *ApiRequest) ([]byte, int, error) {
|
|
req, err := client.makeRequest(request)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
resp, err := client.HttpClient.Do(req)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
buf, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
return buf, resp.StatusCode, nil
|
|
}
|