mirror of
https://wiilab.wiimart.org/wiimart/WiiSOAP
synced 2025-09-05 21:11:02 +02:00
Generate our own tickets rather than store them
This commit is contained in:
parent
7fdbe32e78
commit
9b0fc390c2
120
ecs.go
120
ecs.go
@ -18,6 +18,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -31,31 +33,34 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// TODO (Sketch): Once v3 API has proper versions, remove query to tickets table.
|
||||||
QueryOwnedTitles = `SELECT owned_titles.title_id, tickets.version
|
QueryOwnedTitles = `SELECT owned_titles.title_id, tickets.version
|
||||||
FROM owned_titles, tickets
|
FROM owned_titles, tickets
|
||||||
WHERE owned_titles.title_id = tickets.title_id
|
WHERE owned_titles.title_id = tickets.title_id
|
||||||
AND owned_titles.account_id = $1`
|
AND owned_titles.account_id = $1`
|
||||||
|
|
||||||
QueryOwnedServiceTitles = `SELECT titles.reference_id, owned_titles.date_purchased, titles.item_id, titles.price
|
QueryOwnedServiceTitles = `SELECT titles.reference_id, owned_titles.date_purchased, titles.item_id
|
||||||
FROM titles, owned_titles
|
FROM titles, owned_titles
|
||||||
WHERE titles.item_id = owned_titles.item_id
|
WHERE titles.item_id = owned_titles.item_id
|
||||||
AND owned_titles.account_id = $1`
|
AND titles.title_id = $1
|
||||||
|
AND owned_titles.account_id = $2`
|
||||||
|
|
||||||
QueryTicketStatement = `SELECT ticket, version FROM tickets WHERE title_id = $1`
|
AssociateTicketStatement = `INSERT INTO owned_titles (account_id, title_id, version, item_id, date_purchased)
|
||||||
|
VALUES ($1, $2, $3, $4, $5)`
|
||||||
AssociateTicketStatement = `INSERT INTO owned_titles (account_id, title_id, version, item_id)
|
|
||||||
VALUES ($1, $2, $3, $4)`
|
|
||||||
|
|
||||||
// SharedBalanceAmount describes the maximum signed 32-bit integer value.
|
// SharedBalanceAmount describes the maximum signed 32-bit integer value.
|
||||||
// It is not an actual tracked points value, but exists to permit reuse.
|
// It is not an actual tracked points value, but exists to permit reuse.
|
||||||
SharedBalanceAmount = math.MaxInt32
|
SharedBalanceAmount = math.MaxInt32
|
||||||
|
|
||||||
// WiinoMaApplicationID is the title ID for the Japanese channel Wii no Ma.
|
// WiinoMaApplicationID is the title ID for the Japanese channel Wii no Ma.
|
||||||
WiinoMaApplicationID = "000100014843494A"
|
WiinoMaApplicationID = "000100014843494a"
|
||||||
// WiinoMaServiceTitleID is the service ID used by Wii no Ma's theatre.
|
// WiinoMaServiceTitleID is the service ID used by Wii no Ma's theatre.
|
||||||
WiinoMaServiceTitleID = "000101006843494a"
|
WiinoMaServiceTitleID = "000101006843494a"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// content_aes_key is the AES key that is used to encrypt title contents.
|
||||||
|
var content_aes_key = [16]byte{0x72, 0x95, 0xDB, 0xC0, 0x47, 0x3C, 0x90, 0x0B, 0xB5, 0x94, 0x19, 0x9C, 0xB5, 0xBC, 0xD3, 0xDC}
|
||||||
|
|
||||||
func getBalance() Balance {
|
func getBalance() Balance {
|
||||||
return Balance{
|
return Balance{
|
||||||
Amount: SharedBalanceAmount,
|
Amount: SharedBalanceAmount,
|
||||||
@ -146,29 +151,38 @@ func purchaseTitle(e *Envelope) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// We store title IDs in lowercase.
|
ticket := new(bytes.Buffer)
|
||||||
titleId = strings.ToLower(titleId)
|
var ticketStruct wadlib.Ticket
|
||||||
|
err = binary.Read(bytes.NewReader(wadlib.TicketTemplate), binary.BigEndian, &ticketStruct)
|
||||||
// Query the ticket and current version for this title.
|
|
||||||
var ticket []byte
|
|
||||||
var version int
|
|
||||||
row := pool.QueryRow(ctx, QueryTicketStatement, titleId)
|
|
||||||
|
|
||||||
err = row.Scan(&ticket, &version)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("unexpected error purchasing: %v", err)
|
// Should never happen but report
|
||||||
// TODO(spotlightishere): Can we more elegantly return an error when a title may not exist here?
|
e.Error(2, "error reading ticket template", err)
|
||||||
e.Error(2, "error purchasing", nil)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
price := 0
|
// We will now formulate the ticket for this title.
|
||||||
// Wii no Ma needs the ticket to be in the v1 ticket format.
|
intTitleId, err := strconv.ParseUint(titleId, 16, 64)
|
||||||
|
if err != nil {
|
||||||
|
e.Error(2, "invalid title id", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ticketStruct.TitleID = intTitleId
|
||||||
|
|
||||||
|
// Title key is encrypted with the common key and current title ID
|
||||||
|
ticketStruct.UpdateTitleKey(content_aes_key)
|
||||||
|
|
||||||
|
titleId = strings.ToLower(titleId)
|
||||||
if titleId == WiinoMaServiceTitleID {
|
if titleId == WiinoMaServiceTitleID {
|
||||||
// Firstly we need to change our title ID to the Wii no Ma application ID.
|
// Wii no Ma needs the ticket to be in the v1 ticket format.
|
||||||
titleId, err = e.getKey("ApplicationId")
|
// Update the ticket to reflect that.
|
||||||
|
ticketStruct.FileVersion = 1
|
||||||
|
ticketStruct.AccessTitleMask = math.MaxUint32
|
||||||
|
ticketStruct.LicenseType = 5
|
||||||
|
|
||||||
|
err = binary.Write(ticket, binary.BigEndian, ticketStruct)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.Error(2, "missing application ID", err)
|
e.Error(2, "failed to create ticket", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,8 +210,8 @@ func purchaseTitle(e *Envelope) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query the database for other purchased items
|
// Query the database for other purchased items of the same title id.
|
||||||
rows, err := pool.Query(ctx, QueryOwnedServiceTitles, accountId)
|
rows, err := pool.Query(ctx, QueryOwnedServiceTitles, titleId, accountId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("unexpected error purchasing: %v", err)
|
log.Printf("unexpected error purchasing: %v", err)
|
||||||
e.Error(2, "error purchasing", nil)
|
e.Error(2, "error purchasing", nil)
|
||||||
@ -208,7 +222,7 @@ func purchaseTitle(e *Envelope) {
|
|||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var currentRefIdString string
|
var currentRefIdString string
|
||||||
var purchasedTime time.Time
|
var purchasedTime time.Time
|
||||||
err = rows.Scan(¤tRefIdString, &purchasedTime, nil, &price)
|
err = rows.Scan(¤tRefIdString, &purchasedTime, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("unexpected error purchasing: %v", err)
|
log.Printf("unexpected error purchasing: %v", err)
|
||||||
e.Error(2, "error purchasing", nil)
|
e.Error(2, "error purchasing", nil)
|
||||||
@ -230,32 +244,48 @@ func purchaseTitle(e *Envelope) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
ticket, err = v1Ticket.CreateV1Ticket(ticket, subscriptions)
|
newTicket, err := v1Ticket.CreateV1Ticket(ticket.Bytes(), subscriptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("unexpected error creating v1Ticket: %v", err)
|
log.Printf("unexpected error creating v1Ticket: %v", err)
|
||||||
e.Error(2, "error creating ticket", nil)
|
e.Error(2, "error creating ticket", nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ticket = bytes.NewBuffer(newTicket)
|
||||||
|
} else {
|
||||||
|
// TODO: (Sketch) Validate if this title actually exists using the v3 API.
|
||||||
|
err = binary.Write(ticket, binary.BigEndian, ticketStruct)
|
||||||
|
if err != nil {
|
||||||
|
e.Error(2, "failed to create ticket", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Associate the given title ID with the user.
|
// Associate the given title ID with the user.
|
||||||
_, err = pool.Exec(ctx, AssociateTicketStatement, accountId, titleId, version, itemId)
|
// TODO (Sketch): Once v3 API has proper versions, use.
|
||||||
|
_, err = pool.Exec(ctx, AssociateTicketStatement, accountId, titleId, 0, itemId, time.Now().UTC())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("unexpected error purchasing: %v", err)
|
log.Printf("unexpected error purchasing: %v", err)
|
||||||
e.Error(2, "error purchasing", nil)
|
e.Error(2, "error purchasing", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The returned ticket is expected to have two other certificates associated.
|
// The returned ticket is expected to have two other certificates associated.
|
||||||
ticketString := b64(append(ticket, wadlib.CertChainTemplate...))
|
ticketString := b64(append(ticket.Bytes(), wadlib.CertChainTemplate...))
|
||||||
|
|
||||||
e.AddCustomType(getBalance())
|
e.AddCustomType(getBalance())
|
||||||
e.AddCustomType(Transactions{
|
e.AddCustomType(Transactions{
|
||||||
TransactionId: "00000000",
|
TransactionId: "00000000",
|
||||||
Date: e.Timestamp(),
|
Date: e.Timestamp(),
|
||||||
Type: "PURCHGAME",
|
Type: "PURCHGAME",
|
||||||
TotalPaid: price,
|
TotalPaid: 0,
|
||||||
Currency: "POINTS",
|
Currency: "POINTS",
|
||||||
ItemId: itemId,
|
ItemId: itemId,
|
||||||
|
ItemPricing: Prices{
|
||||||
|
ItemId: itemId,
|
||||||
|
Price: Price{Amount: 0, Currency: "POINTS"},
|
||||||
|
Limits: LimitStruct(PR),
|
||||||
|
LicenseKind: PERMANENT,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
e.AddKVNode("SyncTime", e.Timestamp())
|
e.AddKVNode("SyncTime", e.Timestamp())
|
||||||
|
|
||||||
@ -267,7 +297,6 @@ func purchaseTitle(e *Envelope) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func listPurchaseHistory(e *Envelope) {
|
func listPurchaseHistory(e *Envelope) {
|
||||||
// TODO(SketchMaster2001) Query database for transactions
|
|
||||||
accountId, err := e.AccountId()
|
accountId, err := e.AccountId()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.Error(2, "missing account ID", err)
|
e.Error(2, "missing account ID", err)
|
||||||
@ -280,14 +309,15 @@ func listPurchaseHistory(e *Envelope) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
titleId = strings.ToLower(titleId)
|
||||||
var transactions []Transactions
|
var transactions []Transactions
|
||||||
|
|
||||||
// We will query the database differently for Wii no Ma.
|
// We will query the database differently for Wii no Ma.
|
||||||
if titleId == WiinoMaApplicationID {
|
if titleId == WiinoMaApplicationID {
|
||||||
// Query the database for other purchased items
|
// Query the database for other purchased items
|
||||||
rows, err := pool.Query(ctx, QueryOwnedServiceTitles, accountId)
|
rows, err := pool.Query(ctx, QueryOwnedServiceTitles, WiinoMaServiceTitleID, accountId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("unexpected error purchasing: %v", err)
|
log.Printf("unexpected error querying owned service titles: %v", err)
|
||||||
e.Error(2, "error purchasing", nil)
|
e.Error(2, "error purchasing", nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -297,8 +327,7 @@ func listPurchaseHistory(e *Envelope) {
|
|||||||
var refId string
|
var refId string
|
||||||
var purchasedTime time.Time
|
var purchasedTime time.Time
|
||||||
var itemId int
|
var itemId int
|
||||||
var price int
|
err = rows.Scan(&refId, &purchasedTime, &itemId)
|
||||||
err = rows.Scan(&refId, &purchasedTime, &itemId, &price)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("unexpected error purchasing: %v", err)
|
log.Printf("unexpected error purchasing: %v", err)
|
||||||
e.Error(2, "error purchasing", nil)
|
e.Error(2, "error purchasing", nil)
|
||||||
@ -307,21 +336,23 @@ func listPurchaseHistory(e *Envelope) {
|
|||||||
|
|
||||||
transaction := Transactions{
|
transaction := Transactions{
|
||||||
TransactionId: "00000000",
|
TransactionId: "00000000",
|
||||||
Date: strconv.Itoa(int(purchasedTime.UnixMilli())),
|
// (Sketch) I don't know why but Wii no Ma won't acknowledge the entry if it isn't past a day from
|
||||||
Type: "PURCHGAME",
|
// purchase.
|
||||||
TotalPaid: price,
|
Date: strconv.Itoa(int(purchasedTime.AddDate(0, 0, -1).UnixMilli())),
|
||||||
Currency: "POINTS",
|
Type: "PURCHGAME",
|
||||||
ItemId: itemId,
|
TotalPaid: 0,
|
||||||
|
Currency: "POINTS",
|
||||||
|
ItemId: itemId,
|
||||||
ItemPricing: Prices{
|
ItemPricing: Prices{
|
||||||
ItemId: itemId,
|
ItemId: itemId,
|
||||||
Price: Price{
|
Price: Price{
|
||||||
Amount: price,
|
Amount: 0,
|
||||||
Currency: "POINTS",
|
Currency: "POINTS",
|
||||||
},
|
},
|
||||||
Limits: LimitStruct(PR),
|
Limits: LimitStruct(PR),
|
||||||
LicenseKind: SERVICE,
|
LicenseKind: SERVICE,
|
||||||
},
|
},
|
||||||
TitleId: "000101006843494A",
|
TitleId: WiinoMaServiceTitleID,
|
||||||
ItemCode: itemId,
|
ItemCode: itemId,
|
||||||
ReferenceId: refId,
|
ReferenceId: refId,
|
||||||
}
|
}
|
||||||
@ -346,8 +377,7 @@ func listPurchaseHistory(e *Envelope) {
|
|||||||
Limits: LimitStruct(PR),
|
Limits: LimitStruct(PR),
|
||||||
LicenseKind: PERMANENT,
|
LicenseKind: PERMANENT,
|
||||||
},
|
},
|
||||||
TitleId: "000101006843494A",
|
TitleId: "000101006843494A",
|
||||||
ReferenceId: "01234567890123456789012345678912",
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user