diff --git a/ecs.go b/ecs.go index 060177b..e61af9d 100644 --- a/ecs.go +++ b/ecs.go @@ -56,28 +56,14 @@ func ecsHandler(w http.ResponseWriter, r *http.Request) { } fmt.Println(CDS) fmt.Println("The request is valid! Responding...") - fmt.Fprintf(w, ` - - - - %s - %s - %s - %s - 0 - false - + custom := fmt.Sprintf(` 2018 POINTS 0 %s - %s - - -`, CDS.Version, CDS.DeviceID, CDS.MessageID, timestamp, timestamp, timestamp) + %s`, timestamp, timestamp) + fmt.Fprint(w, formatSuccess("ecs", action, CDS.Version, CDS.DeviceID, CDS.MessageID, custom)) case "NotifiedETicketsSynced": fmt.Println("NETS") @@ -90,21 +76,7 @@ func ecsHandler(w http.ResponseWriter, r *http.Request) { } fmt.Println(NETS) fmt.Println("The request is valid! Responding...") - fmt.Fprintf(w, ` - - - - %s - %s - %s - %s - 0 - false - - -`, NETS.Version, NETS.DeviceID, NETS.MessageID, timestamp) + fmt.Fprint(w, formatSuccess("ecs", action, NETS.Version, NETS.DeviceID, NETS.MessageID, "")) case "ListETickets": fmt.Println("LET") @@ -117,24 +89,10 @@ func ecsHandler(w http.ResponseWriter, r *http.Request) { } fmt.Println(LET) fmt.Println("The request is valid! Responding...") - fmt.Fprintf(w, ` - - - - %s - %s - %s - %s - 0 - false - 0 + custom := fmt.Sprintf(`0 %s - %s - - -`, LET.Version, LET.DeviceID, LET.MessageID, timestamp, timestamp, timestamp) + %s`, timestamp, timestamp) + fmt.Fprint(w, formatSuccess("ecs", action, LET.Version, LET.DeviceID, LET.MessageID, custom)) case "PurchaseTitle": fmt.Println("PT") @@ -147,19 +105,7 @@ func ecsHandler(w http.ResponseWriter, r *http.Request) { } fmt.Println(PT) fmt.Println("The request is valid! Responding...") - fmt.Fprintf(w, ` - - - - %s - %s - %s - %s - 0 - false - + custom := fmt.Sprintf(` 2018 POINTS @@ -172,13 +118,11 @@ func ecsHandler(w http.ResponseWriter, r *http.Request) { 00000000 00000000 00000000 - 00000000 - - -`, PT.Version, PT.DeviceID, PT.MessageID, timestamp, timestamp, timestamp) + 00000000`, timestamp, timestamp) + fmt.Fprint(w, formatSuccess("ecs", action, PT.Version, PT.DeviceID, PT.MessageID, custom)) default: - fmt.Fprintf(w, "WiiSOAP can't handle this. Try again later or actually use a Wii instead of a computer.") + fmt.Fprint(w, "WiiSOAP can't handle this. Try again later or actually use a Wii instead of a computer.") return } diff --git a/ias.go b/ias.go index 9cd9cb9..6896f10 100644 --- a/ias.go +++ b/ias.go @@ -22,8 +22,6 @@ import ( "fmt" "io/ioutil" "net/http" - "strconv" - "time" ) func iasHandler(w http.ResponseWriter, r *http.Request) { @@ -31,10 +29,6 @@ func iasHandler(w http.ResponseWriter, r *http.Request) { action := r.Header.Get("SOAPAction") action = parseAction(action, "ias") - // Get a sexy new timestamp to use. - timestampNano := strconv.FormatInt(time.Now().UTC().Unix(), 10) - timestamp := timestampNano + "000" - fmt.Println("[!] Incoming IAS request.") body, err := ioutil.ReadAll(r.Body) if err != nil { @@ -57,23 +51,9 @@ func iasHandler(w http.ResponseWriter, r *http.Request) { } fmt.Println(CR) fmt.Println("The request is valid! Responding...") - fmt.Fprintf(w, ` - - - - %s - %s - %s - %s - 0 - false - %s - R - - -`, CR.Version, CR.DeviceID, CR.DeviceID, timestamp, CR.SerialNo) + custom := fmt.Sprintf(`%s + R`, CR.SerialNo) + fmt.Fprint(w, formatSuccess("ias", action, CR.Version, CR.DeviceID, CR.MessageID, custom)) case "GetRegistrationInfo": fmt.Println("GRI.") @@ -86,29 +66,15 @@ func iasHandler(w http.ResponseWriter, r *http.Request) { } fmt.Println(GRI) fmt.Println("The request is valid! Responding...") - fmt.Fprintf(w, ` - - - - %s - %s - %s - %s - 0 - false - %s + custom := fmt.Sprintf(`%s 00000000 false %s 0000000000000000 R - POINTS - - -`, GRI.Version, GRI.DeviceID, GRI.MessageID, timestamp, GRI.AccountID, GRI.Country) + POINTS`, GRI.AccountID, GRI.Country) + fmt.Fprint(w, formatSuccess("ias", action, GRI.Version, GRI.DeviceID, GRI.MessageID, custom)) case "Register": fmt.Println("REG.") @@ -121,26 +87,12 @@ func iasHandler(w http.ResponseWriter, r *http.Request) { } fmt.Println(REG) fmt.Println("The request is valid! Responding...") - fmt.Fprintf(w, ` - - - - %s - %s - %s - %s - 0 - false - %s + custom := fmt.Sprintf(`%s 00000000 %s - 00000000 - - -`, REG.Version, REG.DeviceID, REG.MessageID, timestamp, REG.AccountID, REG.Country) + 00000000`, REG.AccountID, REG.Country) + fmt.Fprint(w, formatSuccess("ias", action, REG.Version, REG.DeviceID, REG.MessageID, custom)) case "Unregister": fmt.Println("UNR.") @@ -153,22 +105,10 @@ func iasHandler(w http.ResponseWriter, r *http.Request) { } fmt.Println(UNR) fmt.Println("The request is valid! Responding...") - fmt.Fprintf(w, ` - - - - %s - %s - %s - %s - 0 - false - - -`, UNR.Version, UNR.DeviceID, UNR.MessageID, timestamp) + fmt.Fprint(w, formatSuccess("ias", action, UNR.Version, UNR.DeviceID, UNR.MessageID, "")) default: - fmt.Fprintf(w, "WiiSOAP can't handle this. Try again later or actually use a Wii instead of a computer.") + fmt.Fprint(w, "WiiSOAP can't handle this. Try again later or actually use a Wii instead of a computer.") return } diff --git a/main.go b/main.go index 2908ec3..3f20257 100644 --- a/main.go +++ b/main.go @@ -29,10 +29,25 @@ import ( ) const ( - // Header is a generic XML header suitable for use with the output of Marshal. - // This is not automatically added to any output of this package, - // it is provided as a convenience. - Header = `` + "\n" + // 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 = ` + + +<%sResponse xmlns="%s">` + "\n" + // Template describes common fields across all requests, for easy replication. + Template = ` %s + %s + %s + %s + %d + false` + "\n" + // Footer is the base format of a closing envelope in SOAP. + Footer = ` + +` ) // checkError makes error handling not as ugly and inefficient. diff --git a/utils.go b/utils.go index 3adb2ca..376c9c3 100644 --- a/utils.go +++ b/utils.go @@ -17,13 +17,19 @@ package main -import "strings" +import ( + "fmt" + "strconv" + "strings" + "time" +) +// namespaceForType returns the expected XML namespace format for a service. func namespaceForType(service string) string { return "urn:" + service + ".wsapi.broadon.com" } -// Expected contents are along the lines of "urn:ecs.wsapi.broadon.com/CheckDeviceStatus" +// parseAction interprets contents along the lines of "urn:ecs.wsapi.broadon.com/CheckDeviceStatus". func parseAction(original string, service string) string { prefix := namespaceForType(service) + "/" stripped := strings.Replace(original, prefix, "", 1) @@ -35,3 +41,42 @@ func parseAction(original string, service string) string { return stripped } } + +// formatHeader formats a response type and the proper service. +func formatHeader(responseType string, service string) string { + return fmt.Sprintf(Header, responseType, namespaceForType(service)) +} + +// formatTemplate inserts common, cross-requests values into every request. +func formatTemplate(version string, deviceId string, messageId string, errorCode int) string { + // Get a sexy new timestamp to use. + timestampNano := strconv.FormatInt(time.Now().UTC().Unix(), 10) + timestamp := timestampNano + "000" + + return fmt.Sprintf(Template, version, deviceId, messageId, timestamp, errorCode) +} + +// formatFooter formats the closing tags of any SOAP request per previous response type. +func formatFooter(responseType string) string { + return fmt.Sprintf(Footer, responseType) +} + +// formatForNamespace mangles together several variables throughout a SOAP request. +func formatForNamespace(service string, responseType string, version string, deviceId string, messageId string, errorCode int, extraContents string) string { + return fmt.Sprintf("%s%s%s%s", + formatHeader(responseType, service), + formatTemplate(version, deviceId, messageId, errorCode), + "\t\t"+extraContents, + formatFooter(responseType), + ) +} + +// formatSuccess returns a standard SOAP response with a positive error code, and additional contents. +func formatSuccess(service string, responseType string, version string, deviceId string, messageId string, extraContents string) string { + return formatForNamespace(service, responseType, version, deviceId, messageId, 0, extraContents) +} + +// formatError returns a standard SOAP response with an error code. +func formatError(service string, responseType string, version string, deviceId string, messageId string, errorCode int) string { + return formatForNamespace(service, responseType, version, deviceId, messageId, errorCode, "") +}