mirror of
https://wiilab.wiimart.org/wiimart/WiiSOAP
synced 2025-09-05 21:11:02 +02:00
Convert sprintf-based XML handling to struct-based marshalling
This allows the opportunity to have various handler methods for inserting XML data, instead of a jungle between properly parsing and string-based prayer.
This commit is contained in:
parent
3427d96df4
commit
f32e2a5f2e
63
ecs.go
63
ecs.go
@ -22,11 +22,9 @@ import (
|
|||||||
"github.com/antchfx/xmlquery"
|
"github.com/antchfx/xmlquery"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ecsHandler(common map[string]string, doc *xmlquery.Node) (bool, string) {
|
func ecsHandler(e Envelope, doc *xmlquery.Node) (bool, string) {
|
||||||
timestamp := common["Timestamp"]
|
|
||||||
|
|
||||||
// All actions below are for ECS-related functions.
|
// All actions below are for ECS-related functions.
|
||||||
switch common["Action"] {
|
switch e.Action() {
|
||||||
// TODO: Make the case functions cleaner. (e.g. Should the response be a variable?)
|
// TODO: Make the case functions cleaner. (e.g. Should the response be a variable?)
|
||||||
// TODO: Update the responses so that they query the SQL Database for the proper information (e.g. Device Code, Token, etc).
|
// TODO: Update the responses so that they query the SQL Database for the proper information (e.g. Device Code, Token, etc).
|
||||||
|
|
||||||
@ -34,51 +32,52 @@ func ecsHandler(common map[string]string, doc *xmlquery.Node) (bool, string) {
|
|||||||
//You need to POST some SOAP from WSC if you wanna get some, honey. ;3
|
//You need to POST some SOAP from WSC if you wanna get some, honey. ;3
|
||||||
|
|
||||||
fmt.Println("The request is valid! Responding...")
|
fmt.Println("The request is valid! Responding...")
|
||||||
custom := fmt.Sprintf(`<Balance>
|
e.AddCustomType(Balance{
|
||||||
<Amount>2018</Amount>
|
Amount: 2018,
|
||||||
<Currency>POINTS</Currency>
|
Currency: "POINTS",
|
||||||
</Balance>
|
})
|
||||||
<ForceSyncTime>0</ForceSyncTime>
|
e.AddKVNode("ForceSyncTime", "0")
|
||||||
<ExtTicketTime>%s</ExtTicketTime>
|
e.AddKVNode("ExtTicketTime", e.Timestamp())
|
||||||
<SyncTime>%s</SyncTime>`, timestamp, timestamp)
|
e.AddKVNode("SyncTime", e.Timestamp())
|
||||||
return formatSuccess(common, custom)
|
break
|
||||||
|
|
||||||
case "NotifyETicketsSynced":
|
case "NotifyETicketsSynced":
|
||||||
// This is a disgusting request, but 20 dollars is 20 dollars. ;3
|
// This is a disgusting request, but 20 dollars is 20 dollars. ;3
|
||||||
|
|
||||||
fmt.Println("The request is valid! Responding...")
|
fmt.Println("The request is valid! Responding...")
|
||||||
return formatSuccess(common, "")
|
break
|
||||||
|
|
||||||
case "ListETickets":
|
case "ListETickets":
|
||||||
// that's all you've got for me? ;3
|
// that's all you've got for me? ;3
|
||||||
|
|
||||||
fmt.Println("The request is valid! Responding...")
|
fmt.Println("The request is valid! Responding...")
|
||||||
custom := fmt.Sprintf(`<ForceSyncTime>0</ForceSyncTime>
|
e.AddKVNode("ForceSyncTime", "0")
|
||||||
<ExtTicketTime>%s</ExtTicketTime>
|
e.AddKVNode("ExtTicketTime", e.Timestamp())
|
||||||
<SyncTime>%s</SyncTime>`, timestamp, timestamp)
|
e.AddKVNode("SyncTime", e.Timestamp())
|
||||||
return formatSuccess(common, custom)
|
break
|
||||||
|
|
||||||
case "PurchaseTitle":
|
case "PurchaseTitle":
|
||||||
// If you wanna fun time, it's gonna cost ya extra sweetie... ;3
|
// If you wanna fun time, it's gonna cost ya extra sweetie... ;3
|
||||||
|
|
||||||
fmt.Println("The request is valid! Responding...")
|
fmt.Println("The request is valid! Responding...")
|
||||||
custom := fmt.Sprintf(`<Balance>
|
e.AddCustomType(Balance{
|
||||||
<Amount>2018</Amount>
|
Amount: 2018,
|
||||||
<Currency>POINTS</Currency>
|
Currency: "POINTS",
|
||||||
</Balance>
|
})
|
||||||
<Transactions>
|
e.AddCustomType(Transactions{
|
||||||
<TransactionId>00000000</TransactionId>
|
TransactionId: "00000000",
|
||||||
<Date>%s</Date>
|
Date: e.Timestamp(),
|
||||||
<Type>PURCHGAME</Type>
|
Type: "PURCHGAME",
|
||||||
</Transactions>
|
})
|
||||||
<SyncTime>%s</SyncTime>
|
e.AddKVNode("SyncTime", e.Timestamp())
|
||||||
<ETickets>00000000</ETickets>
|
e.AddKVNode("Certs", "00000000")
|
||||||
<Certs>00000000</Certs>
|
e.AddKVNode("TitleId", "00000000")
|
||||||
<Certs>00000000</Certs>
|
e.AddKVNode("ETickets", "00000000")
|
||||||
<TitleId>00000000</TitleId>`, timestamp, timestamp)
|
break
|
||||||
return formatSuccess(common, custom)
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false, "WiiSOAP can't handle this. Try again later or actually use a Wii instead of a computer."
|
return false, "WiiSOAP can't handle this. Try again later or actually use a Wii instead of a computer."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return e.ReturnSuccess()
|
||||||
}
|
}
|
||||||
|
60
ias.go
60
ias.go
@ -22,72 +22,78 @@ import (
|
|||||||
"github.com/antchfx/xmlquery"
|
"github.com/antchfx/xmlquery"
|
||||||
)
|
)
|
||||||
|
|
||||||
func iasHandler(common map[string]string, doc *xmlquery.Node) (bool, string) {
|
func iasHandler(e Envelope, doc *xmlquery.Node) (bool, string) {
|
||||||
|
|
||||||
// All actions below are for IAS-related functions.
|
// All actions below are for IAS-related functions.
|
||||||
switch common["Action"] {
|
switch e.Action() {
|
||||||
// TODO: Make the case functions cleaner. (e.g. Should the response be a variable?)
|
// TODO: Make the case functions cleaner. (e.g. Should the response be a variable?)
|
||||||
// TODO: Update the responses so that they query the SQL Database for the proper information (e.g. Device Code, Token, etc).
|
// TODO: Update the responses so that they query the SQL Database for the proper information (e.g. Device Code, Token, etc).
|
||||||
|
|
||||||
case "CheckRegistration":
|
case "CheckRegistration":
|
||||||
serialNo, err := getKey(doc, "SerialNumber")
|
serialNo, err := getKey(doc, "SerialNumber")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return formatError(common, 5, "not good enough for me. ;3", err)
|
return e.ReturnError(5, "not good enough for me. ;3", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("The request is valid! Responding...")
|
fmt.Println("The request is valid! Responding...")
|
||||||
custom := fmt.Sprintf(`<OriginalSerialNumber>%s</OriginalSerialNumber>
|
e.AddKVNode("OriginalSerialNumber", serialNo)
|
||||||
<DeviceStatus>R</DeviceStatus>`, serialNo)
|
e.AddKVNode("DeviceStatus", "R")
|
||||||
return formatSuccess(common, custom)
|
break
|
||||||
|
|
||||||
|
case "GetChallenge":
|
||||||
|
fmt.Println("The request is valid! Responding...")
|
||||||
|
break
|
||||||
|
|
||||||
case "GetRegistrationInfo":
|
case "GetRegistrationInfo":
|
||||||
accountId, err := getKey(doc, "AccountId")
|
accountId, err := getKey(doc, "AccountId")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return formatError(common, 6, "how dirty. ;3", err)
|
return e.ReturnError(7, "how dirty. ;3", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
country, err := getKey(doc, "Country")
|
country, err := getKey(doc, "Country")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return formatError(common, 6, "how dirty. ;3", err)
|
return e.ReturnError(7, "how dirty. ;3", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("The request is valid! Responding...")
|
fmt.Println("The request is valid! Responding...")
|
||||||
custom := fmt.Sprintf(`<AccountId>%s</AccountId>
|
e.AddKVNode("AccountId", accountId)
|
||||||
<DeviceToken>00000000</DeviceToken>
|
e.AddKVNode("DeviceToken", "00000000")
|
||||||
<DeviceTokenExpired>false</DeviceTokenExpired>
|
e.AddKVNode("DeviceTokenExpired", "false")
|
||||||
<Country>%s</Country>
|
e.AddKVNode("Country", country)
|
||||||
<ExtAccountId></ExtAccountId>
|
e.AddKVNode("ExtAccountId", "")
|
||||||
<DeviceCode>0000000000000000</DeviceCode>
|
e.AddKVNode("DeviceCode", "0000000000000000")
|
||||||
<DeviceStatus>R</DeviceStatus>
|
e.AddKVNode("DeviceStatus", "R")
|
||||||
<Currency>POINTS</Currency>`, accountId, country)
|
// This _must_ be POINTS.
|
||||||
return formatSuccess(common, custom)
|
e.AddKVNode("Currency", "POINTS")
|
||||||
|
break
|
||||||
|
|
||||||
case "Register":
|
case "Register":
|
||||||
accountId, err := getKey(doc, "AccountId")
|
accountId, err := getKey(doc, "AccountId")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return formatError(common, 7, "disgustingly invalid. ;3", err)
|
return e.ReturnError(8, "disgustingly invalid. ;3", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
country, err := getKey(doc, "Country")
|
country, err := getKey(doc, "Country")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return formatError(common, 7, "disgustingly invalid. ;3", err)
|
return e.ReturnError(8, "disgustingly invalid. ;3", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("The request is valid! Responding...")
|
fmt.Println("The request is valid! Responding...")
|
||||||
custom := fmt.Sprintf(`<AccountId>%s</AccountId>
|
e.AddKVNode("AccountId", accountId)
|
||||||
<DeviceToken>00000000</DeviceToken>
|
e.AddKVNode("DeviceToken", "00000000")
|
||||||
<Country>%s</Country>
|
e.AddKVNode("Country", country)
|
||||||
<ExtAccountId></ExtAccountId>
|
e.AddKVNode("ExtAccountId", "")
|
||||||
<DeviceCode>00000000</DeviceCode>`, accountId, country)
|
e.AddKVNode("DeviceCode", "0000000000000000")
|
||||||
return formatSuccess(common, custom)
|
break
|
||||||
|
|
||||||
case "Unregister":
|
case "Unregister":
|
||||||
// how abnormal... ;3
|
// how abnormal... ;3
|
||||||
|
|
||||||
fmt.Println("The request is valid! Responding...")
|
fmt.Println("The request is valid! Responding...")
|
||||||
return formatSuccess(common, "")
|
break
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false, "WiiSOAP can't handle this. Try again later or actually use a Wii instead of a computer."
|
return false, "WiiSOAP can't handle this. Try again later or actually use a Wii instead of a computer."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return e.ReturnSuccess()
|
||||||
}
|
}
|
||||||
|
41
main.go
41
main.go
@ -28,28 +28,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
// Header is the base format of a SOAP response with string substitutions available.
|
|
||||||
// All XML constants must be treated as temporary until a proper XPath solution is investigated.
|
|
||||||
Header = `<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
|
||||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
||||||
<soapenv:Body>
|
|
||||||
<%sResponse xmlns="%s">` + "\n"
|
|
||||||
// Template describes common fields across all requests, for easy replication.
|
|
||||||
Template = ` <Version>%s</Version>
|
|
||||||
<DeviceId>%s</DeviceId>
|
|
||||||
<MessageId>%s</MessageId>
|
|
||||||
<TimeStamp>%s</TimeStamp>
|
|
||||||
<ErrorCode>%d</ErrorCode>
|
|
||||||
<ServiceStandbyMode>false</ServiceStandbyMode>` + "\n"
|
|
||||||
// Footer is the base format of a closing envelope in SOAP.
|
|
||||||
Footer = `</%sResponse>
|
|
||||||
</soapenv:Body>
|
|
||||||
</soapenv:Envelope>`
|
|
||||||
)
|
|
||||||
|
|
||||||
// checkError makes error handling not as ugly and inefficient.
|
// checkError makes error handling not as ugly and inefficient.
|
||||||
func checkError(err error) {
|
func checkError(err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -123,23 +101,24 @@ func commonHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Println("Received:", string(body))
|
||||||
|
|
||||||
|
// Insert the current action being performed.
|
||||||
|
envelope := NewEnvelope(service, action)
|
||||||
|
|
||||||
// Extract shared values from this request.
|
// Extract shared values from this request.
|
||||||
common, err := obtainCommon(doc)
|
err = envelope.ObtainCommon(doc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
printError(w, "Error handling request body: "+err.Error())
|
printError(w, "Error handling request body: "+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert the current action being performed.
|
|
||||||
common["Service"] = service
|
|
||||||
common["Action"] = action
|
|
||||||
|
|
||||||
var result string
|
|
||||||
var successful bool
|
var successful bool
|
||||||
|
var result string
|
||||||
if service == "ias" {
|
if service == "ias" {
|
||||||
successful, result = iasHandler(common, doc)
|
successful, result = iasHandler(envelope, doc)
|
||||||
} else if service == "ecs" {
|
} else if service == "ecs" {
|
||||||
successful, result = ecsHandler(common, doc)
|
successful, result = ecsHandler(envelope, doc)
|
||||||
}
|
}
|
||||||
|
|
||||||
if successful {
|
if successful {
|
||||||
@ -155,5 +134,5 @@ func commonHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func printError(w http.ResponseWriter, reason string) {
|
func printError(w http.ResponseWriter, reason string) {
|
||||||
http.Error(w, reason, http.StatusInternalServerError)
|
http.Error(w, reason, http.StatusInternalServerError)
|
||||||
log.Println("Failed to handle request: " + reason)
|
fmt.Println("Failed to handle request: " + reason)
|
||||||
}
|
}
|
||||||
|
63
structure.go
63
structure.go
@ -35,3 +35,66 @@ type Config struct {
|
|||||||
SQLPass string `xml:"SQLPass"`
|
SQLPass string `xml:"SQLPass"`
|
||||||
SQLDB string `xml:"SQLDB"`
|
SQLDB string `xml:"SQLDB"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Envelope represents the root element of any response, soapenv:Envelope.
|
||||||
|
type Envelope struct {
|
||||||
|
XMLName string `xml:"soapenv:Envelope"`
|
||||||
|
SOAPEnv string `xml:"xmlns:soapenv,attr"`
|
||||||
|
XSD string `xml:"xmlns:xsd,attr"`
|
||||||
|
XSI string `xml:"xmlns:xsi,attr"`
|
||||||
|
|
||||||
|
// Represents a soapenv:Body within.
|
||||||
|
Body Body
|
||||||
|
|
||||||
|
// Used for internal state tracking.
|
||||||
|
action string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Body represents the nested soapenv:Body element as a child on the root element,
|
||||||
|
// containing the response intended for the action being handled.
|
||||||
|
type Body struct {
|
||||||
|
XMLName string `xml:"soapenv:Body"`
|
||||||
|
|
||||||
|
// Represents the actual response inside
|
||||||
|
Response Response
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response describes the inner response format, along with common fields across requests.
|
||||||
|
type Response struct {
|
||||||
|
XMLName xml.Name
|
||||||
|
XMLNS string `xml:"xmlns,attr"`
|
||||||
|
|
||||||
|
// These common fields are persistent across all requests.
|
||||||
|
Version string `xml:"Version"`
|
||||||
|
DeviceId string `xml:"DeviceId"`
|
||||||
|
MessageId string `xml:"MessageId"`
|
||||||
|
TimeStamp string `xml:"TimeStamp"`
|
||||||
|
ErrorCode int
|
||||||
|
ServiceStandbyMode bool `xml:"ServiceStandbyMode"`
|
||||||
|
|
||||||
|
// Allows a simple <name>value</name> node to be inserted.
|
||||||
|
KVFields []KVField
|
||||||
|
// Allows for <name>[dynamic content]</name> situations.
|
||||||
|
CustomFields []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// KVField represents an individual node in form of <XMLName>Contents</XMLName>.
|
||||||
|
type KVField struct {
|
||||||
|
XMLName xml.Name
|
||||||
|
Value string `xml:",chardata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Balance represents a common XML structure.
|
||||||
|
type Balance struct {
|
||||||
|
XMLName xml.Name `xml:"Balance"`
|
||||||
|
Amount int `xml:"Amount"`
|
||||||
|
Currency string `xml"Currency"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transactions represents a common XML structure.
|
||||||
|
type Transactions struct {
|
||||||
|
XMLName xml.Name `xml:"Transactions"`
|
||||||
|
TransactionId string `xml:"TransactionId"`
|
||||||
|
Date string `xml:"Date"`
|
||||||
|
Type string `xml:"Type"`
|
||||||
|
}
|
||||||
|
138
utils.go
138
utils.go
@ -18,8 +18,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/xml"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"github.com/antchfx/xmlquery"
|
"github.com/antchfx/xmlquery"
|
||||||
"io"
|
"io"
|
||||||
"regexp"
|
"regexp"
|
||||||
@ -44,41 +44,104 @@ func parseAction(original string) (string, string) {
|
|||||||
return service, action
|
return service, action
|
||||||
}
|
}
|
||||||
|
|
||||||
// formatHeader formats a response type and the proper service.
|
// NewEnvelope returns a new Envelope with proper attributes initialized.
|
||||||
func formatHeader(responseType string, service string) string {
|
func NewEnvelope(service string, action string) Envelope {
|
||||||
return fmt.Sprintf(Header, responseType, "urn:"+service+".wsapi.broadon.com")
|
// Get a sexy new timestamp to use.
|
||||||
|
timestampNano := strconv.FormatInt(time.Now().UTC().Unix(), 10) + "000"
|
||||||
|
|
||||||
|
return Envelope{
|
||||||
|
SOAPEnv: "http://schemas.xmlsoap.org/soap/envelope/",
|
||||||
|
XSD: "http://www.w3.org/2001/XMLSchema",
|
||||||
|
XSI: "http://www.w3.org/2001/XMLSchema-instance",
|
||||||
|
Body: Body{
|
||||||
|
Response: Response{
|
||||||
|
XMLName: xml.Name{Local: action + "Response"},
|
||||||
|
XMLNS: "urn:" + service + ".wsapi.broadon.com",
|
||||||
|
|
||||||
|
TimeStamp: timestampNano,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
action: action,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// formatTemplate inserts common, cross-requests values into every request.
|
// Action returns the action for this service.
|
||||||
func formatTemplate(common map[string]string, errorCode int) string {
|
func (e *Envelope) Action() string {
|
||||||
return fmt.Sprintf(Template, common["Version"], common["DeviceID"], common["MessageId"], common["Timestamp"], errorCode)
|
return e.action
|
||||||
}
|
}
|
||||||
|
|
||||||
// formatFooter formats the closing tags of any SOAP request per previous response type.
|
// Timestamp returns a shared timestamp for this request.
|
||||||
func formatFooter(responseType string) string {
|
func (e *Envelope) Timestamp() string {
|
||||||
return fmt.Sprintf(Footer, responseType)
|
return e.Body.Response.TimeStamp
|
||||||
}
|
}
|
||||||
|
|
||||||
// formatForNamespace mangles together several variables throughout a SOAP request.
|
// obtainCommon interprets a given node, and updates the envelope with common key values.
|
||||||
func formatForNamespace(common map[string]string, errorCode int, extraContents string) string {
|
func (e *Envelope) ObtainCommon(doc *xmlquery.Node) error {
|
||||||
return fmt.Sprintf("%s%s%s%s",
|
var err error
|
||||||
formatHeader(common["Action"], common["Service"]),
|
|
||||||
formatTemplate(common, errorCode),
|
// These fields are common across all requests.
|
||||||
"\t"+extraContents+"\n",
|
e.Body.Response.Version, err = getKey(doc, "Version")
|
||||||
formatFooter(common["Action"]),
|
if err != nil {
|
||||||
)
|
return err
|
||||||
|
}
|
||||||
|
e.Body.Response.DeviceId, err = getKey(doc, "DeviceId")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
e.Body.Response.MessageId, err = getKey(doc, "MessageId")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// formatSuccess returns a standard SOAP response with a positive error code, and additional contents.
|
// AddKVNode adds a given key by name to a specified value.
|
||||||
func formatSuccess(common map[string]string, extraContents string) (bool, string) {
|
func (e *Envelope) AddKVNode(key string, value string) {
|
||||||
return true, formatForNamespace(common, 0, extraContents)
|
e.Body.Response.KVFields = append(e.Body.Response.KVFields, KVField{
|
||||||
|
XMLName: xml.Name{Local: key},
|
||||||
|
Value: value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddCustomType adds a given key by name to a specified structure.
|
||||||
|
func (e *Envelope) AddCustomType(customType interface{}) {
|
||||||
|
e.Body.Response.CustomFields = append(e.Body.Response.CustomFields, customType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// becomeXML marshals the Envelope object, returning the intended boolean state on success.
|
||||||
|
// ..there has to be a better way to do this, TODO.
|
||||||
|
func (e *Envelope) becomeXML(intendedStatus bool) (bool, string) {
|
||||||
|
contents, err := xml.Marshal(e)
|
||||||
|
if err != nil {
|
||||||
|
return false, "an error occurred marshalling XML: " + err.Error()
|
||||||
|
} else {
|
||||||
|
// Add XML header on top of existing contents.
|
||||||
|
result := xml.Header + string(contents)
|
||||||
|
return intendedStatus, result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReturnSuccess returns a standard SOAP response with a positive error code.
|
||||||
|
func (e *Envelope) ReturnSuccess() (bool, string) {
|
||||||
|
// Ensure the error code is 0.
|
||||||
|
e.Body.Response.ErrorCode = 0
|
||||||
|
|
||||||
|
return e.becomeXML(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// formatError returns a standard SOAP response with an error code.
|
// formatError returns a standard SOAP response with an error code.
|
||||||
func formatError(common map[string]string, errorCode int, reason string, err error) (bool, string) {
|
func (e *Envelope) ReturnError(errorCode int, reason string, err error) (bool, string) {
|
||||||
extra := "<UserReason>" + reason + "</UserReason>\n" +
|
e.Body.Response.ErrorCode = errorCode
|
||||||
"\t<ServerReason>" + err.Error() + "</ServerReason>\n"
|
|
||||||
return false, formatForNamespace(common, errorCode, extra)
|
// Ensure all additional fields are empty to avoid conflict.
|
||||||
|
e.Body.Response.KVFields = []KVField{}
|
||||||
|
e.Body.Response.CustomFields = nil
|
||||||
|
|
||||||
|
e.AddKVNode("UserReason", reason)
|
||||||
|
e.AddKVNode("ServerReason", err.Error())
|
||||||
|
|
||||||
|
return e.becomeXML(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// normalise parses a document, returning a document with only the request type's child nodes, stripped of prefix.
|
// normalise parses a document, returning a document with only the request type's child nodes, stripped of prefix.
|
||||||
@ -108,31 +171,6 @@ func stripNamespace(node *xmlquery.Node) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// obtainCommon interprets a given node, and from its children finds common keys and respective values across all requests.
|
|
||||||
func obtainCommon(doc *xmlquery.Node) (map[string]string, error) {
|
|
||||||
info := make(map[string]string)
|
|
||||||
|
|
||||||
// Get a sexy new timestamp to use.
|
|
||||||
timestampNano := strconv.FormatInt(time.Now().UTC().Unix(), 10)
|
|
||||||
info["Timestamp"] = timestampNano + "000"
|
|
||||||
|
|
||||||
// These fields are common across all requests.
|
|
||||||
// Looping through all...
|
|
||||||
shared := []string{"Version", "DeviceId", "MessageId"}
|
|
||||||
for _, key := range shared {
|
|
||||||
// select their node by name...
|
|
||||||
value, err := getKey(doc, key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// and insert their value to our map.
|
|
||||||
info[key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
return info, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getKey returns the value for a child key from a node, if documented.
|
// getKey returns the value for a child key from a node, if documented.
|
||||||
func getKey(doc *xmlquery.Node, key string) (string, error) {
|
func getKey(doc *xmlquery.Node, key string) (string, error) {
|
||||||
node := xmlquery.FindOne(doc, "//"+key)
|
node := xmlquery.FindOne(doc, "//"+key)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user