Properly check registration status

This commit is contained in:
Spotlight 2022-01-01 23:00:50 -06:00
parent 943f475bba
commit 3337ebb475
No known key found for this signature in database
GPG Key ID: 874AA355B3209BDC
6 changed files with 106 additions and 68 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
.idea
config.xml
WiiSOAP

View File

@ -1,16 +1,20 @@
# WiiSOAP
A SOAP Server, designed specifically to handle Wii Shop Channel SOAP. Slowly in development.
> WiiSOAP is being rewritten in the future, therefore Pull Requests may be ignored.
WiiSOAP is a server designed specifically to handle Wii Shop Channel SOAP - more specifically, that of the ECommerce library.
Ideally, one day this will become feature complete enough to handle other titles utilizing EC, such as DLCs or other purchases.
## What's the difference between this repo and that other SOAP repo?
This is the SOAP Server Software. The other repository only has the communication templates between a Wii and WSC's server.
It aims to implement everything necessary to provide title tickets, manage authentication, and everything between.
> Note that this software is still in development. The schema ma
## What's the difference between this repo and [that other SOAP repo](https://github.com/OpenShopChannel/Open-Shop-SOAP)?
This is the SOAP Server Software. The other repository, [Open-Shop-SOAP](https://github.com/OpenShopChannel/Open-Shop-SOAP), holds templates of communication between a Wii and WSC's server.
# Changelog
Versions on this software are based on goals. (e.g 0.2 works towards SQL support. 0.3 works towards NUS support, etc.)
## 0.3.x Tanuki
### 0.3.0
- Migrates to using PostgreSQL
- Migrate to using PostgreSQL
- Add routes and XML niceties
- Implement most routes

44
constants.go Normal file
View File

@ -0,0 +1,44 @@
package main
// LimitKinds represents various limits applied to the current ticket.
type LimitKinds int
const (
// PR is presumably "purchased".
PR LimitKinds = 0
TR = 1
DR = 2
SR = 3
LR = 4
AT = 10000
)
// LimitStruct returns a Limits struct filled for the given kind.
func LimitStruct(kind LimitKinds) Limits {
names := map[LimitKinds]string{
PR: "PR",
TR: "TR",
DR: "DR",
SR: "SR",
LR: "LR",
AT: "AT",
}
return Limits{
Limits: kind,
LimitKind: names[kind],
}
}
// DeviceStatus represents the various statuses a device may have.
//
// These values do not appear to be directly checked by the client within the
// Wii Shop Channel, and are a generic string. We could utilize any value we wish.
// However, titles utilizing DLCs appear to check the raw values.
// For this reason, we mirror values from Nintendo.
type DeviceStatus string
const (
DeviceStatusRegistered = "R"
DeviceStatusUnregistered = "U"
)

View File

@ -2,8 +2,8 @@
-- PostgreSQL database dump
--
-- Dumped from database version 13.2
-- Dumped by pg_dump version 13.2
-- Dumped from database version 14.1
-- Dumped by pg_dump version 14.1
SET statement_timeout = 0;
SET lock_timeout = 0;
@ -64,22 +64,12 @@ CREATE TABLE public.userbase (
device_token_hashed character varying(32) NOT NULL,
account_id integer NOT NULL,
region character varying(3),
country character varying(2),
language character varying(2),
serial_number character varying(11),
device_code bigint
serial_number character varying(11)
);
ALTER TABLE public.userbase OWNER TO wiisoap;
--
-- Name: COLUMN userbase.device_code; Type: COMMENT; Schema: public; Owner: wiisoap
--
COMMENT ON COLUMN public.userbase.device_code IS 'Also known as the console''s friend code.';
--
-- Name: owned_titles owned_titles_pk; Type: CONSTRAINT; Schema: public; Owner: wiisoap
--
@ -126,10 +116,10 @@ CREATE UNIQUE INDEX userbase_account_id_uindex ON public.userbase USING btree (a
--
-- Name: userbase_device_code_uindex; Type: INDEX; Schema: public; Owner: wiisoap
-- Name: userbase_device_id_uindex; Type: INDEX; Schema: public; Owner: wiisoap
--
CREATE UNIQUE INDEX userbase_device_code_uindex ON public.userbase USING btree (device_code);
CREATE UNIQUE INDEX userbase_device_id_uindex ON public.userbase USING btree (device_id);
--
@ -157,4 +147,4 @@ ALTER TABLE ONLY public.owned_titles
--
-- PostgreSQL database dump complete
--
--

64
ias.go
View File

@ -23,25 +23,55 @@ import (
"fmt"
wiino "github.com/RiiConnect24/wiino/golang"
"github.com/jackc/pgconn"
"github.com/jackc/pgx/v4"
"log"
"math/rand"
"strconv"
)
const (
PrepareUserStatement = `INSERT INTO userbase (device_id, device_token, device_token_hashed, account_id, region, country, language, serial_number, device_code) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`
SyncUserStatement = `SELECT account_id, device_code, device_token FROM userbase WHERE language = $1 AND country = $2 AND region = $3 AND device_id = $4`
PrepareUserStatement = `INSERT INTO userbase
(device_id, device_token, device_token_hashed, account_id, region, serial_number)
VALUES ($1, $2, $3, $4, $5, $6)`
SyncUserStatement = `SELECT
account_id, device_token
FROM userbase WHERE
region = $1 AND
device_id = $2`
CheckUserStatement = `SELECT
1
FROM userbase WHERE
device_id = $1 AND
serial_number = $2 AND
region = $3`
)
func checkRegistration(e *Envelope) {
serialNo, err := getKey(e.doc, "SerialNumber")
if err != nil {
e.Error(5, "not good enough for me. ;3", err)
e.Error(5, "missing serial number", err)
return
}
// We'll utilize our sync user statement.
query := pool.QueryRow(ctx, CheckUserStatement, e.DeviceId(), serialNo, e.Region())
err = query.Scan(nil)
// Formulate our response
e.AddKVNode("OriginalSerialNumber", serialNo)
e.AddKVNode("DeviceStatus", "R")
if err != nil {
// We're either unregistered, or a database error occurred.
if err == pgx.ErrNoRows {
e.AddKVNode("DeviceStatus", DeviceStatusUnregistered)
} else {
log.Printf("error executing statement: %v\n", err)
e.Error(5, "server-side error", err)
}
} else {
// No errors! We're safe.
e.AddKVNode("DeviceStatus", DeviceStatusRegistered)
}
}
func getChallenge(e *Envelope) {
@ -64,11 +94,10 @@ func getRegistrationInfo(e *Envelope) {
func syncRegistration(e *Envelope) {
var accountId int64
var deviceCode int
var deviceToken string
user := pool.QueryRow(ctx, SyncUserStatement, e.Language(), e.Country(), e.Region(), e.DeviceId())
err := user.Scan(&accountId, &deviceCode, &deviceToken)
user := pool.QueryRow(ctx, SyncUserStatement, e.Region(), e.DeviceId())
err := user.Scan(&accountId, &deviceToken)
if err != nil {
e.Error(7, "An error occurred querying the database.", err)
}
@ -82,37 +111,36 @@ func syncRegistration(e *Envelope) {
}
func register(e *Envelope) {
reason := "disgustingly invalid. ;3"
deviceCode, err := getKey(e.doc, "DeviceCode")
if err != nil {
e.Error(7, reason, err)
e.Error(7, "missing device code", err)
return
}
registerRegion, err := getKey(e.doc, "RegisterRegion")
if err != nil {
e.Error(7, reason, err)
e.Error(7, "missing registration region", err)
return
}
if registerRegion != e.Region() {
e.Error(7, reason, errors.New("region does not match registration region"))
e.Error(7, "mismatched region", errors.New("region does not match registration region"))
return
}
serialNo, err := getKey(e.doc, "SerialNumber")
if err != nil {
e.Error(7, reason, err)
e.Error(7, "missing serial number", err)
return
}
// Validate given friend code.
userId, err := strconv.ParseUint(deviceCode, 10, 64)
if err != nil {
e.Error(7, reason, err)
e.Error(7, "invalid friend code", err)
return
}
if wiino.NWC24CheckUserID(userId) != 0 {
e.Error(7, reason, err)
e.Error(7, "invalid friend code", err)
return
}
@ -124,18 +152,18 @@ func register(e *Envelope) {
// ...and then its md5, because the Wii sends this for most requests.
md5DeviceToken := fmt.Sprintf("%x", md5.Sum([]byte(deviceToken)))
// Insert all of our obtained values to the database..
_, err = pool.Exec(ctx, PrepareUserStatement, e.DeviceId(), deviceToken, md5DeviceToken, accountId, e.Region(), e.Country(), e.Language(), serialNo, deviceCode)
// Insert all of our obtained values to the database...
_, err = pool.Exec(ctx, PrepareUserStatement, e.DeviceId(), deviceToken, md5DeviceToken, accountId, e.Region(), serialNo)
if err != nil {
// It's okay if this isn't a PostgreSQL error, as perhaps other issues have come in.
if driverErr, ok := err.(*pgconn.PgError); ok {
if driverErr.Code == "23505" {
e.Error(7, reason, errors.New("user already exists"))
e.Error(7, "database error", errors.New("user already exists"))
return
}
}
log.Printf("error executing statement: %v\n", err)
e.Error(7, reason, errors.New("failed to execute db operation"))
e.Error(7, "database error", errors.New("failed to execute db operation"))
return
}

View File

@ -101,35 +101,6 @@ type Balance struct {
Currency string `xml:"Currency"`
}
type LimitKinds int
const (
// PR is presumably "purchased".
PR LimitKinds = 0
TR = 1
DR = 2
SR = 3
LR = 4
AT = 10000
)
// LimitStruct returns a Limits struct filled for the given kind.
func LimitStruct(kind LimitKinds) Limits {
names := map[LimitKinds]string{
PR: "PR",
TR: "TR",
DR: "DR",
SR: "SR",
LR: "LR",
AT: "AT",
}
return Limits{
Limits: kind,
LimitKind: names[kind],
}
}
// Limits represents a common XML structure for transaction information.
type Limits struct {
XMLName xml.Name `xml:"Limits"`