diff --git a/ecs.go b/ecs.go
new file mode 100644
index 0000000..060177b
--- /dev/null
+++ b/ecs.go
@@ -0,0 +1,189 @@
+// Copyright (C) 2018-2020 CornierKhan1
+//
+// WiiSOAP is SOAP Server Software, designed specifically to handle Wii Shop Channel SOAP.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/.
+
+package main
+
+import (
+ "encoding/xml"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "strconv"
+ "time"
+)
+
+func ecsHandler(w http.ResponseWriter, r *http.Request) {
+ // Figure out the action to handle via header.
+ action := r.Header.Get("SOAPAction")
+ action = parseAction(action, "ecs")
+
+ // Get a sexy new timestamp to use.
+ timestampNano := strconv.FormatInt(time.Now().UTC().Unix(), 10)
+ timestamp := timestampNano + "000"
+
+ fmt.Println("[!] Incoming ECS request.")
+ body, err := ioutil.ReadAll(r.Body)
+ if err != nil {
+ http.Error(w, "Error reading request body...", http.StatusInternalServerError)
+ }
+
+ // The switch converts the HTTP Body of the request into a string. There is no need to convert the cases to byte format.
+ switch action {
+ // 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).
+
+ case "CheckDeviceStatus":
+ fmt.Println("CDS.")
+ CDS := CDS{}
+ if err = xml.Unmarshal(body, &CDS); err != nil {
+ fmt.Println("...or not. Bad or incomplete request. (End processing.)")
+ fmt.Fprint(w, "You need to POST some SOAP from WSC if you wanna get some, honey. ;3")
+ return
+ }
+ fmt.Println(CDS)
+ fmt.Println("The request is valid! Responding...")
+ fmt.Fprintf(w, `
+
+
+
+ %s
+ %s
+ %s
+ %s
+ 0
+ false
+
+ 2018
+ POINTS
+
+ 0
+ %s
+ %s
+
+
+`, CDS.Version, CDS.DeviceID, CDS.MessageID, timestamp, timestamp, timestamp)
+
+ case "NotifiedETicketsSynced":
+ fmt.Println("NETS")
+ NETS := NETS{}
+ if err = xml.Unmarshal(body, &NETS); err != nil {
+ fmt.Println("...or not. Bad or incomplete request. (End processing.)")
+ fmt.Fprint(w, "This is a disgusting request, but 20 dollars is 20 dollars. ;3")
+ fmt.Printf("Error: %v", err)
+ return
+ }
+ 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)
+
+ case "ListETickets":
+ fmt.Println("LET")
+ LET := LET{}
+ if err = xml.Unmarshal(body, &LET); err != nil {
+ fmt.Println("...or not. Bad or incomplete request. (End processing.)")
+ fmt.Fprint(w, "that's all you got for me? ;3")
+ fmt.Printf("Error: %v", err)
+ return
+ }
+ fmt.Println(LET)
+ fmt.Println("The request is valid! Responding...")
+ fmt.Fprintf(w, `
+
+
+
+ %s
+ %s
+ %s
+ %s
+ 0
+ false
+ 0
+ %s
+ %s
+
+
+`, LET.Version, LET.DeviceID, LET.MessageID, timestamp, timestamp, timestamp)
+
+ case "PurchaseTitle":
+ fmt.Println("PT")
+ PT := PT{}
+ if err = xml.Unmarshal(body, &PT); err != nil {
+ fmt.Println("...or not. Bad or incomplete request. (End processing.)")
+ fmt.Fprint(w, "if you wanna fun time, its gonna cost ya extra sweetie. ;3")
+ fmt.Printf("Error: %v", err)
+ return
+ }
+ fmt.Println(PT)
+ fmt.Println("The request is valid! Responding...")
+ fmt.Fprintf(w, `
+
+
+
+ %s
+ %s
+ %s
+ %s
+ 0
+ false
+
+ 2018
+ POINTS
+
+
+ 00000000
+ %s
+ PURCHGAME
+
+ %s
+ 00000000
+ 00000000
+ 00000000
+ 00000000
+
+
+`, PT.Version, PT.DeviceID, PT.MessageID, timestamp, timestamp, timestamp)
+
+ default:
+ fmt.Fprintf(w, "WiiSOAP can't handle this. Try again later or actually use a Wii instead of a computer.")
+ return
+ }
+
+ fmt.Println("Delivered response!")
+
+ // TODO: Add NUS and CAS SOAP to the case list.
+ fmt.Println("[!] End of ECS Request.\n")
+}
diff --git a/ias.go b/ias.go
new file mode 100644
index 0000000..9cd9cb9
--- /dev/null
+++ b/ias.go
@@ -0,0 +1,179 @@
+// Copyright (C) 2018-2020 CornierKhan1
+//
+// WiiSOAP is SOAP Server Software, designed specifically to handle Wii Shop Channel SOAP.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/.
+
+package main
+
+import (
+ "encoding/xml"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "strconv"
+ "time"
+)
+
+func iasHandler(w http.ResponseWriter, r *http.Request) {
+ // Figure out the action to handle via header.
+ 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 {
+ http.Error(w, "Error reading request body...", http.StatusInternalServerError)
+ }
+
+ // The switch converts the HTTP Body of the request into a string. There is no need to convert the cases to byte format.
+ switch action {
+ // 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).
+
+ case "CheckRegistration":
+ fmt.Println("CR.")
+ CR := CR{}
+ if err = xml.Unmarshal(body, &CR); err != nil {
+ fmt.Println("...or not. Bad or incomplete request. (End processing.)")
+ fmt.Fprint(w, "not good enough for me. ;3")
+ fmt.Printf("Error: %v", err)
+ return
+ }
+ 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)
+
+ case "GetRegistrationInfo":
+ fmt.Println("GRI.")
+ GRI := GRI{}
+ if err = xml.Unmarshal(body, &GRI); err != nil {
+ fmt.Println("...or not. Bad or incomplete request. (End processing.)")
+ fmt.Fprint(w, "how dirty. ;3")
+ fmt.Printf("Error: %v", err)
+ return
+ }
+ fmt.Println(GRI)
+ fmt.Println("The request is valid! Responding...")
+ fmt.Fprintf(w, `
+
+
+
+ %s
+ %s
+ %s
+ %s
+ 0
+ false
+ %s
+ 00000000
+ false
+ %s
+
+ 0000000000000000
+ R
+ POINTS
+
+
+`, GRI.Version, GRI.DeviceID, GRI.MessageID, timestamp, GRI.AccountID, GRI.Country)
+
+ case "Register":
+ fmt.Println("REG.")
+ REG := REG{}
+ if err = xml.Unmarshal(body, ®); err != nil {
+ fmt.Println("...or not. Bad or incomplete request. (End processing.)")
+ fmt.Fprint(w, "disgustingly invalid. ;3")
+ fmt.Printf("Error: %v", err)
+ return
+ }
+ fmt.Println(REG)
+ fmt.Println("The request is valid! Responding...")
+ fmt.Fprintf(w, `
+
+
+
+ %s
+ %s
+ %s
+ %s
+ 0
+ false
+ %s
+ 00000000
+ %s
+
+ 00000000
+
+
+`, REG.Version, REG.DeviceID, REG.MessageID, timestamp, REG.AccountID, REG.Country)
+
+ case "Unregister":
+ fmt.Println("UNR.")
+ UNR := UNR{}
+ if err = xml.Unmarshal(body, &UNR); err != nil {
+ fmt.Println("...or not. Bad or incomplete request. (End processing.)")
+ fmt.Fprint(w, "how abnormal... ;3")
+ fmt.Printf("Error: %v", err)
+ return
+ }
+ 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)
+
+ default:
+ fmt.Fprintf(w, "WiiSOAP can't handle this. Try again later or actually use a Wii instead of a computer.")
+ return
+ }
+
+ fmt.Println("Delivered response!")
+
+ // TODO: Add NUS and CAS SOAP to the case list.
+ fmt.Println("[!] End of IAS Request.\n")
+}
diff --git a/main.go b/main.go
index 2ea68cc..2908ec3 100644
--- a/main.go
+++ b/main.go
@@ -24,9 +24,6 @@ import (
"io/ioutil"
"log"
"net/http"
- "strconv"
- "strings"
- "time"
_ "github.com/go-sql-driver/mysql"
)
@@ -41,7 +38,7 @@ const (
// checkError makes error handling not as ugly and inefficient.
func checkError(err error) {
if err != nil {
- log.Fatalf("WiiSOAP forgot how to drive and suddenly crashed! Reason: %s\n", err.Error())
+ log.Fatalf("WiiSOAP forgot how to drive and suddenly crashed! Reason: %v\n", err)
}
}
@@ -69,313 +66,12 @@ func main() {
// Start the HTTP server.
fmt.Printf("Starting HTTP connection (%s)...\nNot using the usual port for HTTP?\nBe sure to use a proxy, otherwise the Wii can't connect!\n", CON.Address)
- http.HandleFunc("/ecs/services/ECommerceSOAP", handler) // each request calls handler
+
+ // These following endpoints don't have to match what the official WSC have.
+ // However, semantically, it feels proper.
+ http.HandleFunc("/ecs/services/ECommerceSOAP", ecsHandler) // For ECS operations
+ http.HandleFunc("/ias/services/IdentityAuthenticationSOAP", iasHandler) // For IAS operations
log.Fatal(http.ListenAndServe(CON.Address, nil))
- // From here on out, all special cool things should go into the handler function.
-}
-
-func handler(w http.ResponseWriter, r *http.Request) {
- // Figure out the action to handle via header.
- action := r.Header.Get("SOAPAction")
- action = parseAction(action, "ecs")
-
- // Get a sexy new timestamp to use.
- timestampnano := strconv.FormatInt(time.Now().UTC().Unix(), 10)
- timestamp := timestampnano + "000"
-
- fmt.Println("[!] Incoming request.")
- body, err := ioutil.ReadAll(r.Body)
- if err != nil {
- http.Error(w, "Error reading request body...", http.StatusInternalServerError)
- }
-
- // The switch converts the HTTP Body of the request into a string. There is no need to convert the cases to byte format.
- switch action {
- // 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).
-
- case "CheckDeviceStatus":
- fmt.Println("CDS.")
- CDS := CDS{}
- if err = xml.Unmarshal(body, &CDS); err != nil {
- fmt.Println("...or not. Bad or incomplete request. (End processing.)")
- fmt.Fprint(w, "You need to POST some SOAP from WSC if you wanna get some, honey. ;3")
- return
- }
- fmt.Println(CDS)
- fmt.Println("The request is valid! Responding...")
- fmt.Fprintf(w, `
-
-
-
- %s
- %s
- %s
- %s
- 0
- false
-
- 2018
- POINTS
-
- 0
- %s
- %s
-
-
-`, CDS.Version, CDS.DeviceID, CDS.MessageID, timestamp, timestamp, timestamp)
- fmt.Println("Delivered response!")
-
- case "NotifiedETicketsSynced":
- fmt.Println("NETS")
- NETS := NETS{}
- if err = xml.Unmarshal(body, &NETS); err != nil {
- fmt.Println("...or not. Bad or incomplete request. (End processing.)")
- fmt.Fprint(w, "This is a disgusting request, but 20 dollars is 20 dollars. ;3")
- fmt.Printf("error: %v", err)
- return
- }
- 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.Println("Delivered response!")
-
- case "ListETickets":
- fmt.Println("LET")
- LET := LET{}
- if err = xml.Unmarshal(body, &LET); err != nil {
- fmt.Println("...or not. Bad or incomplete request. (End processing.)")
- fmt.Fprint(w, "This is a disgusting request, but 20 dollars is 20 dollars. ;3")
- fmt.Printf("error: %v", err)
- return
- }
- fmt.Println(LET)
- fmt.Println("The request is valid! Responding...")
- fmt.Fprintf(w, `
-
-
-
- %s
- %s
- %s
- %s
- 0
- false
- 0
- %s
- %s
-
-
-`, LET.Version, LET.DeviceID, LET.MessageID, timestamp, timestamp, timestamp)
- fmt.Println("Delivered response!")
-
- case "PurchaseTitle":
- fmt.Println("PT")
- PT := PT{}
- if err = xml.Unmarshal(body, &PT); err != nil {
- fmt.Println("...or not. Bad or incomplete request. (End processing.)")
- fmt.Fprint(w, "if you wanna fun time, its gonna cost ya extra sweetie. ;3")
- fmt.Printf("Error: %s", err.Error())
- return
- }
- fmt.Println(PT)
- fmt.Println("The request is valid! Responding...")
- fmt.Fprintf(w, `
-
-
-
- %s
- %s
- %s
- %s
- 0
- false
-
- 2018
- POINTS
-
-
- 00000000
- %s
- PURCHGAME
-
- %s
- 00000000
- 00000000
- 00000000
- 00000000
-
-
-`, PT.Version, PT.DeviceID, PT.MessageID, timestamp, timestamp, timestamp)
- fmt.Println("Delivered response!")
-
- case "CheckRegistration":
- fmt.Println("CR.")
- CR := CR{}
- if err = xml.Unmarshal(body, &CR); err != nil {
- fmt.Println("...or not. Bad or incomplete request. (End processing.)")
- fmt.Fprint(w, "not good enough for me. ;3")
- fmt.Printf("Error: %s", err.Error())
- return
- }
- 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)
- fmt.Println("[i] Delivered response!")
-
- case "GetRegistrationInfo":
- fmt.Println("GRI.")
- GRI := GRI{}
- if err = xml.Unmarshal(body, &GRI); err != nil {
- fmt.Println("...or not. Bad or incomplete request. (End processing.)")
- fmt.Fprint(w, "how dirty. ;3")
- fmt.Printf("Error: %s", err.Error())
- return
- }
- fmt.Println(GRI)
- fmt.Println("The request is valid! Responding...")
- fmt.Fprintf(w, `
-
-
-
- %s
- %s
- %s
- %s
- 0
- false
- %s
- 00000000
- false
- %s
-
- 0000000000000000
- R
- POINTS
-
-
-`, GRI.Version, GRI.DeviceID, GRI.MessageID, timestamp, GRI.AccountID, GRI.Country)
- fmt.Println("Delivered response!")
-
- case "Register":
- fmt.Println("REG.")
- REG := REG{}
- if err = xml.Unmarshal(body, ®); err != nil {
- fmt.Println("...or not. Bad or incomplete request. (End processing.)")
- fmt.Fprint(w, "disgustingly invalid. ;3")
- fmt.Printf("Error: %s", err.Error())
- return
- }
- fmt.Println(REG)
- fmt.Println("The request is valid! Responding...")
- fmt.Fprintf(w, `
-
-
-
- %s
- %s
- %s
- %s
- 0
- false
- %s
- 00000000
- %s
-
- 00000000
-
-
-`, REG.Version, REG.DeviceID, REG.MessageID, timestamp, REG.AccountID, REG.Country)
- fmt.Println("Delivered response!")
-
- case "Unregister":
- fmt.Println("UNR.")
- UNR := UNR{}
- if err = xml.Unmarshal(body, &UNR); err != nil {
- fmt.Println("...or not. Bad or incomplete request. (End processing.)")
- fmt.Fprint(w, "how abnormal... ;3")
- fmt.Printf("Error: %s", err.Error())
- return
- }
- 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.Println("Delivered response!")
-
- default:
- fmt.Fprintf(w, "WiiSOAP can't handle this. Try again later or actually use a Wii instead of a computer.")
- }
-
- // TODO: Add NUS and CAS SOAP to the case list.
- fmt.Println("[!] End of Request." + "\n")
-}
-
-func namespaceForType(service string) string {
- return "urn:" + service + ".wsapi.broadon.com"
-}
-
-// Expected contents are 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)
-
- if stripped == original {
- // This doesn't appear valid.
- return ""
- } else {
- return stripped
- }
+ // From here on out, all special cool things should go into their respective handler function.
}
diff --git a/utils.go b/utils.go
new file mode 100644
index 0000000..3adb2ca
--- /dev/null
+++ b/utils.go
@@ -0,0 +1,37 @@
+// Copyright (C) 2018-2020 CornierKhan1
+//
+// WiiSOAP is SOAP Server Software, designed specifically to handle Wii Shop Channel SOAP.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/.
+
+package main
+
+import "strings"
+
+func namespaceForType(service string) string {
+ return "urn:" + service + ".wsapi.broadon.com"
+}
+
+// Expected contents are 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)
+
+ if stripped == original {
+ // This doesn't appear valid.
+ return ""
+ } else {
+ return stripped
+ }
+}