mirror of
https://wiilab.wiimart.org/wiimart/WiiSOAP
synced 2025-09-03 20:11:14 +02:00
Properly check registration status
This commit is contained in:
parent
943f475bba
commit
3337ebb475
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
.idea
|
||||
config.xml
|
||||
WiiSOAP
|
||||
|
14
README.md
14
README.md
@ -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
44
constants.go
Normal 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"
|
||||
)
|
22
database.sql
22
database.sql
@ -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
64
ias.go
@ -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
|
||||
}
|
||||
|
||||
|
29
structure.go
29
structure.go
@ -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"`
|
||||
|
Loading…
x
Reference in New Issue
Block a user