WiiSOAP/ecs.go
Ocarinaoftime c2f231bf25
Update ecs.go
Upload current SOAP files
2025-04-25 11:35:49 -04:00

739 lines
47 KiB
Go

// 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 (
"bytes"
"crypto/md5"
"encoding/binary"
"encoding/hex"
"fmt"
"html"
"log"
"math"
"math/rand"
"os"
"strconv"
"strings"
"time"
v1Ticket "github.com/OpenShopChannel/V1TicketGenerator"
"github.com/antchfx/xmlquery"
"github.com/wii-tools/wadlib"
)
const (
QueryOwnedTitles = `SELECT owned_titles.title_id
FROM owned_titles
WHERE owned_titles.account_id = $1`
QueryOwnedServiceTitles = `SELECT service_titles.reference_id, owned_titles.date_purchased, service_titles.item_id
FROM service_titles, owned_titles
WHERE service_titles.item_id = owned_titles.item_id
AND service_titles.title_id = $1
AND owned_titles.account_id = $2`
AssociateTicketStatement = `INSERT INTO owned_titles (account_id, title_id, version, item_id, date_purchased)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT (accound_id) DO NOTHING`
AssociatePointsStatement = `INSERT INTO public.userbase (device_id, device_token, device_token_hashed, account_id, region, serial_number, points)
VALUES ($1, $2, $3, $4, $5, $6, $7)
ON CONFLICT (device_id) DO UPDATE SET points = $7`
QueryPointsStatement = `SELECT points from public.userbase WHERE device_id = $1`
AssociateGiftedTitleStatement = `INSERT INTO public.gifted_titles (title_id, trans_id, friend_code)
VALUES ($1, $2, $3)`
QueryGiftedTitleStatement = `SELECT trans_id FROM public.gifted_titles`
RemoveGiftedTitleStatement = `DELETE FROM public.gifted_titles
WHERE title_id = $1 AND trans_id = $2`
// SharedBalanceAmount describes the maximum signed 32-bit integer value.
// It is not an actual tracked points value, but exists to permit reuse.
SharedBalanceAmount = 1000
// WiinoMaApplicationID is the title ID for the Japanese channel Wii no Ma.
WiinoMaApplicationID = "000100014843494A"
// WiinoMaServiceTitleID is the service ID used by Wii no Ma's theatre.
WiinoMaServiceTitleID = "000101006843494A"
)
// contentAesKey is the AES key that is used to encrypt title contents.
var contentAesKey = [16]byte{0x72, 0x95, 0xDB, 0xC0, 0x47, 0x3C, 0x90, 0x0B, 0xB5, 0x94, 0x19, 0x9C, 0xB5, 0xBC, 0xD3, 0xDC}
func getBalance(e *Envelope) Balance {
var points string
err := pool.QueryRow(ctx, QueryPointsStatement, e.DeviceId()).Scan(&points)
if err != nil {
e.Error(104, "Could not retrieve points balance.", err)
}
log.Printf("Points balance for device %s: %s", e.DeviceId(), points)
if points == "" {
points = "0"
}
pointsInt, err := strconv.Atoi(points)
if err != nil {
e.Error(114, "Could not convert points balance to integer.", err)
}
log.Printf("Points balance for device %s: %d", e.DeviceId(), pointsInt)
return Balance{
Amount: pointsInt,
Currency: "POINTS",
}
}
func checkDeviceStatus(e *Envelope) {
e.AddCustomType(getBalance(e))
e.AddKVNode("ForceSyncTime", "0")
e.AddKVNode("ExtTicketTime", "0")
e.AddKVNode("SyncTime", e.Timestamp())
}
func notifyETicketsSynced(e *Envelope) {
// TODO: Implement handling of synchronization timing
}
func listETickets(e *Envelope) {
/* var titleIds [5]string
var ticketIds [5]string
titleIds[0] = "0001000144574641"
ticketIds[0] = "0001000000000000"
titleIds[1] = "000100014F484243"
ticketIds[1] = "0023DB4424D033EC"
titleIds[2] = "0001000452464E45"
ticketIds[2] = "00019E9781E7E89F"
titleIds[3] = "0001000452465045"
ticketIds[3] = "0001F72A2763BFA2"
titleIds[4] = "00010004524D4345"
ticketIds[4] = "00019B1950DF85DF"
for i := 0; i < len(titleIds); i++ {
e.AddCustomType(Tickets{
TitleId: titleIds[i],
Version: 1,
// We do not support migration, ticket IDs, or revocation.
TicketId: ticketIds[i],
RevokeDate: 0,
MigrateCount: 0,
MigrateLimit: 0,
})
}
/*e.AddCustomType(Tickets{
TitleId: titleIds[2],
Version: 1,
// We do not support migration, ticket IDs, or revocation.
TicketId: "0",
RevokeDate: 0,
MigrateCount: 0,
MigrateLimit: 0,
})*/
e.AddKVNode("ForceSyncTime", "0")
e.AddKVNode("ExtTicketTime", "0")
e.AddKVNode("SyncTime", e.Timestamp())
}
func getETickets(e *Envelope) {
/* var etickets [8]string
etickets[0] = "AAEAAVtvJd7vSdT1RY1l6q5kaCHrdVis2lLa0/25pbbHaqr560eOnp/VpWjhGfD1ElTDJN3vRgb+ijzM9NrnBQvW/oFXAfsAyhw/UuO3Av7rB6cdd4e2wmpraxIavl9XL8nVrsKVbtI8vH35HUzTDO46ES1M7+cYtCIAWToSJE+UtWK0PNPAlhJZ2poKT96kiq+9bGT1u+Tls7pVrgv8ZnT4dDKUIZkBUWntfCU84WVpvl953lC/8Xff/B29umu9jjKpfvmx436Oi9TpZE94zRdR2DLs0kxtyreV/uIjBYLv2eZcjzskXNoOTEe+3++V5yvNW201bgUwAhiTlIVx+AQwgScAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSb290LUNBMDAwMDAwMDEtWFMwMDAwMDAwMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALTS+bbifI4ioLdMFp2KjnlkPV+Gq0jq3IjvdPz6AEIHZUFmW9vJW+S/PyWqqdndg67xqOOy5EOHteuwAAAAXl16v6DlPOAaZ7b6wUSvqwAAAY7miaw1FQJ7CPUAAQAASEFaQf//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//////////////////////////////////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
etickets[1] = "AAEAATuy8VhF9O0027viWCvKomWmnnJVjhiuYsJ6ycMJzwRLn/o5IEY5v7MZiNY2Sn5+ixDfiean3Wny1Kq9DIMVwoERgCUkY4D051d4YPuPnraERmNU5oMU9DvZosCEdOWlihKYdUGyyCbk2r4uy9Sai+cv+ozru+FAB2LLsgG/t8U5ekKpbYHBB/Z1IfSJiqt7gg5Fn1o+NIeaMNZBYZHQU7tGjzYnkCGJQj00LusAnz76gtOlfYms9mpHr0iWWlDWDYeYosJ1XTf8Swuu0zUf5po1l0li767/sZ/N8oYRSMok2CkXSwyQHlrRJqlmK4IOUIhRrUfsIlwkvp6WSxeBnl8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSb290LUNBMDAwMDAwMDEtWFMwMDAwMDAwMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALTS+bbifI4ioLdMFp2KjnlkPV+Gq0jq3IjvdPz6AEIHZUFmW9vJW+S/PyWqqdndg67xqOOy5EOHteuwAAAACtSjNHpZMSx0mtlhmYd1LgAAAdSMLJjFDAJ7CPUAAQABSENSRf//AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//////////////////////////////////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
//-----
//WiiSOAP eticket
//etickets[2] = "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"
//vWii eticket
etickets[2] = "AAEAASNTxfAL/ZyPDwPVv2IUZsPS/NbZxdcoiHUt5pR33+IWAJEmkgp3SudX/BhuCKTdzGC9gdBIXWNVdzlx5sn0RtmOSsZkazePp2tU+BRi3EpJ8Oiz2Sh03BrZmOayRJybVsc/OfhtbAJo0XOXVoHWXjx/UfBpcPIf4X7Sm4pQUdD0/xQAo0OoCnWVBf1bNYy1VRBN5srdnLu0qSaVX7zj6MqrjXs1JRxYokQ2l1u0/2/Tj1u89Kw2dzgm8vb77kdLJ58co+IJYTii3hsCySItp0t2yzWnWVfRsJFNjsXTDxPwo6Uw+5YfoBrR5+EiuT0MgD5CJIqlJraeI8maqiL7ySMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSb290LUNBMDAwMDAwMDEtWFMwMDAwMDAwMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALTS+bbifI4ioLdMFp2KjnlkPV+Gq0jq3IjvdPz6AEIHZUFmW9vJW+S/PyWqqdndg67xqOOy5EOHteuwAAAAAXc90P6nEAXs/JOIzaCuVwAAAepYrULdZQJ7CPUAAQABSEFURf//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//////////////////////////////////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
//-----
etickets[3] = "AAEAAafzenrwRhEO0HsLSaejzYM+Lm8QW8ahw1PR+pZFEY4YFNygeyDkDtEWf9/aq3y1YG+cEPy1Y5Mrn1fhomhDPKQpoDr8ou3ZiZkiYLBzAp1EfxUIpeLSVwY5AsQ55U1+cihFLBRxC3l3eYxCWqIsiAZm1kj/79cRYHoDGIhRvbGfHjgEFPLF73IeIqZn5kkkukuRjX4PXVe6lungGZuTnvgsb/TzrCVRZ8hjFmCINUiOHxtG4CvybJqxnXekl8hVRmIG3eDmiWuHfskN+jHIe4YjVQotZLIB7FcZr1Tk35XJN08re99cxJfEGC/SRHd/VMwn0/WFJDQh6KWjy4fX1K8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSb290LUNBMDAwMDAwMDEtWFMwMDAwMDAwMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALTS+bbifI4ioLdMFp2KjnlkPV+Gq0jq3IjvdPz6AEIHZUFmW9vJW+S/PyWqqdndg67xqOOy5EOHteuwAAAAhz/e0Ych6ONikIIpXPQq1gAAAapyxP88DQJ7CPUAAQABSEFKRf//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//////////////////////////////////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
etickets[4] = "AAEAAUth7dnIIm2kr6Bw3hZkkBCycZV+73NNOs95tTHFnYyO+GJ/zVOWF1s0Rne2PugqcPHWe9Skn6Z1gGmGdXMgNK5q0+QqWfoG/LkD4F8WvYO06ihcsXfDn9GTDDAcDfFTMWssApcpQeER18S/zG45tug+0ZnsayhmyBbyK6zJnZFm6Mxp9PsuuN79TRpK8pKpRPLXVjzKFUaY2Y3g+Tc+RHJ/ptDb+ONgP6iZ2kd9k81J1kugRn6DqisbC8ZKuEaHqeCQ3hLc9xKc2EfpVWW62grEbkWSZOM7ellar5BM0Hd3Ne3Xcsp8b1v4JFMVdRahrL94qVlFX7GugQubHK9tI4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSb290LUNBMDAwMDAwMDEtWFMwMDAwMDAwMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALTS+bbifI4ioLdMFp2KjnlkPV+Gq0jq3IjvdPz6AEIHZUFmW9vJW+S/PyWqqdndg67xqOOy5EOHteuwAAAAk+b/rdrXVvwTB4zE+vuiXgAAAUaiyG6pjQJ7CPUAAQABSEFQRf//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//////////////////////////////////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
etickets[5] = "AAEAAZzaP2lz+bZIy6fSW/ncG+8BnyYhX6+lyEwGDBMlyTInPeNxvEohK4jjhZzYK6uvV4PM5MPLu0e3rfcEqzYTVKVJI9sn06O+JuTFzEIzyV5OTRRNqAlwzG+6KFSwMxwH5qOfwRCoSYsy6vw+F0QAbwFcdvuN0BpRVhDZKu390GBCM6sKTFE881oX+q2RfhKnA+ZFsSYlklpOAHwdlzRrlgVgnfkHTGh0JgtgcNtvWsPgfEZqkj5lrrvINWwr+yWZKrIhcD1xRIWX2w8zKBCExyqdmIfMKPDR0+RLzi4EAMWcNEA3pDUoneKcxE/tlZ3m5A0heRSCfTlbeMcyra0fR0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSb290LUNBMDAwMDAwMDEtWFMwMDAwMDAwMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALTS+bbifI4ioLdMFp2KjnlkPV+Gq0jq3IjvdPz6AEIHZUFmW9vJW+S/PyWqqdndg67xqOOy5EOHteuwAAAARfNQD9UtTUOVSCUyDkupPAAAASdjIxILugJ7CPUAAQABSENTRf//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//////////////////////////////////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
etickets[6] = "AAEAAX8fEr0hDFGPJalcFmdQztiwR3Fb8tvPEAfeA5kO2Nbynyw2bhL4MAV54sGsxHfBlcm5kTNnH+V+aBum2I+U/Y9sxXQhqvlNJ9qjBNCAJBR8DIz8//biXPwbh0hQOp4FaH9yYfGVNw4m7qrKvFiSVvrCLS4SZJx5Ka8QjiS2smDtj++b7F78C2k6UK7guCRgnVoUTg4po/4+FOSdfkKEdOIKNXKfnocJcUsdmOBO4NLK73/lDdz3XPjWTPtg2/IQ2UGxp5oH1Z+W57jcWFfYgIDcXk4E1xtBd4s33om4RMnL4jvVUtxA2xx/ixQ7H2j3Inqk71lTqEZDHFckwoUY9OUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSb290LUNBMDAwMDAwMDEtWFMwMDAwMDAwMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALTS+bbifI4ioLdMFp2KjnlkPV+Gq0jq3IjvdPz6AEIHZUFmW9vJW+S/PyWqqdndg67xqOOy5EOHteuwAAAAWnOfqtsyBhg0ReE0M9dVxwAAAV8mkF0mKAJ7CPUAAQABSEFXRQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//////////////////////////////////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
etickets[7] = "AAEAAR1dFX4o1WcYlRaHWYwiNPqKuGB6xHbZ46f8w9LBZXRkX/5P9ORxHKwjeWI0sAM3m0bCn4gR1et4821FrDw1gidnMcRqolZqvvtDhtRj3whdYbDgIii1lmpZPOOoWQnTKaKJp6/FUm+eytBB/wr9azJ69tyP3fp0Brg1m8dgRajg5gs2rTKOEpOeuCGMaQBB/a05TRf9zyfvOhL8kHQher8lB2AdkkL88AD2PqIDV4HeE82dk7hi/NI1HZVJejf5aOcswc3FhDNHFSBMKKBPGpVD7RHCjJVEoPXa7LhdZZ9nkpmgQaa1F8s06N5q/yD1rwtWnTyHca7U1z9BoTghn5EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSb290LUNBMDAwMDAwMDEtWFMwMDAwMDAwMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALTS+bbifI4ioLdMFp2KjnlkPV+Gq0jq3IjvdPz6AEIHZUFmW9vJW+S/PyWqqdndg67xqOOy5EOHteuwAAAAT2D7XouKddLkZlKJXqcuegAAtzdsE3eQLQJ7CPUAAQABSEFERf//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//////////////////////////////////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
//etickets[8] = "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"
var certs [2]string
// vWii certs
certs[0] = "AAEAALOtsyJrPD3/G0tAdxb/T3rXZIbIlaxWLSHxBgHU9mQoGRwHdo/fGuLOeyfJD7wK0DEleOwHebZX1DckE6f4bwwUwO9uCUHtKwXsOVc2B4kASoeNLp34x6Wp+MqzEbEYeVe7+JjiolQCz1Q5zyu/oOH4XAZug5rglMpH4BVY9W5vNOkqotw4k343zYxcTf0vEU/oaMmo2f7YbgwhdaK9fom5x7UT9Bp5YUQ5EO/51/5XIhjVbft/SXqky5DU8a6xduRoXaeUQGCYLwRIQB/Pxrrr2hYwtHO0FSM1CAcKn0+JeOYs7F6SRqWovaCFeGh1DDoRL6+V6DjImQ6HsWLNENqzMZZl74ibVBuzNrtnU5+vwq4tCi51wCN06k6sjZlQf1m5U3cwXyY1xgipkJOsj8beI7l66nC0xM9msw5YMg7FtnIESM47sRxTH8twKHy1wnxnT7v9jH/JQiCkcyMdWH5aGhqC43V5obuCbs4Bccl1Y0dLHUbmebKCN2IRzccAL0aHwjxtwNW1eG7h8nP/AZJQD/THUGrucrb0PfYI/qWDofmGD4evUkRUu0fDBgyU6Zv31jKnyKtLT/U1IR/BgEe7evpaK9e4hK2OVk9bif83lzfx9QE7H57EGG+SKtXEs8DVhwucBK8atfO8bQrxfUcI5EPpc/e3cHdUuvPs0qxJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUm9vdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFDQTAwMDAwMDAxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW/p9XLJ5yeLu4SHG6vRP9jn4jweLS3ftn5VgsDWCgbUOVatyERWhd3A8ejD+OunvHGC8HZdGdrI6aMwEsZhSW8lo8R3i21Dk2efwceVi2uIJIjPp02P2HdfBn/OkqR6PZVPUcd17hLnxuM5zNfD1VAVjoeq4OWPgm+kBAR+ZVGNhKHAg6cwNq0h/FA1mJqGDbScRHyBo3kdyFJFRz2nGG6YO+dlJoPcfVJny05rSjHAFNIKTxDH/vTP2vKYNxxleorzFbSALr20G0JxB243pxyAVTKSDK2nAjGnNOwc6AGNgL0YtM4BhpepskVzVYjV5w+tkzkTvWG0UuqqINAGbPuvu03kAAQABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
certs[1] = "AAEAAX2dXrpSgdynBl0vCGjbisc6zn6pkfGWn+HQ8sEfrsDD8BrctEat5coDtiUhlGLG4UENueY/3pjRryY7TLKHhCeCcu8nE0uHwljWe2Lytb+ctrqMiRkuxQaJrHQkoCIJQAPumKS9LwE7WT/lZmzV61rXpJMQ8077tD1Gy/G1I8+C9o61bbkEp8KoK+EdeNObog2Q0wdC2156we/yIVEJYs+pFKiA3PQXupmTCu4IsLDlGj6fr83C1+PLoS86wAeQ3kR6w8U4qGeSOAeL1MSyRawpFohtKg5ZTu1cyDVpi01iON8Fck3M9oGAinB0Blkwv/hRQTfoFfq6oXK44GlsYeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSb290LUNBMDAwMDAwMDEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVhTMDAwMDAwMDMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADxuJ/RrQepN4p7EAx9xzm+nt23MgCJqyWx+HGvWqn0WJ7RgwIyjoEaH+/QCcgGNkP4VLnhO7thOnrPhxSFa6Rbque7xk6y912H6/Jn7Q+kQakzZl5XfVreq/tGLnYAypzpTcTLmDmSq3ovs6Oeor+cU+zQ3Ppri16yy6QP+kB1+PKy3pc4EYct9eKmw4sv3I5X3b1fRusn1hlS9q74YrfumsaCorGaqbVY++uziS+9UMn13EpunJv+RYA0qUIYLd63X+DRs98Ol+OZgIdwGMKyg/E1dXxaMPw/MISkmqrAHucGaU+OFEjaEjrMT/omqjj3778njzaXeXddt8Wtx4mR3PhDjQABAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
//WiiSOAP certs
//certs[0] = "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"
//certs[1] = "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"
for i := 0; i < len(etickets); i++ {
e.AddKVNode("ETickets", etickets[i])
}
for i := 0; i < len(certs); i++ {
e.AddKVNode("Certs", certs[i])
}*/
e.AddKVNode("ForceSyncTime", "0")
e.AddKVNode("ExtTicketTime", e.Timestamp())
e.AddKVNode("SyncTime", e.Timestamp())
}
func purchaseTitle(e *Envelope) {
accountId, err := e.AccountId()
if err != nil {
e.Error(2, "missing account ID", err)
return
}
tempItemId, err := e.getKey("ItemId")
if err != nil {
e.Error(2, "missing item ID", err)
return
}
// Our struct takes an integer rather than a string.
itemId, _ := strconv.Atoi(tempItemId)
// Determine the title ID we're going to purchase.
titleId, err := e.getKey("TitleId")
if err != nil {
e.Error(2, "missing account ID", err)
return
}
ticket := new(bytes.Buffer)
var ticketStruct wadlib.Ticket
err = binary.Read(bytes.NewReader(wadlib.TicketTemplate), binary.BigEndian, &ticketStruct)
if err != nil {
// Should never happen but report
e.Error(2, "error reading ticket template", err)
return
}
// We will now formulate the ticket for this title.
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(contentAesKey)
tikFile, err := os.ReadFile("./tickets/" + strings.ToLower(strings.Replace(titleId, "00010001", "", 1)) + ".tik")
fmt.Println("ticket name: " + strings.ToLower(strings.Replace(titleId, "00010001", "", 1)) + ".tik")
if err != nil {
tikFile, err = os.ReadFile("/media/sdc1/hydrobleach/Local/ccs.cdn.shop.wii.com/ccs/download/" + titleId + "/cetk")
fmt.Println("ticket name: " + titleId + ".tik")
}
/*wad, err := wadlib.LoadWADFromFile("../WiiLikeToParty/wad/ticket/" + strings.ToLower(titleId) + "_bogus.wad")
if err != nil {
e.Error(2, "couldn't read wad", err)
return
}
wadTicket, err := wad.GetTicket()
if err != nil {
e.Error(2, "couldn't read ticket", err)
return
}*/
/*wad, err := wadlib.LoadWADFromFile("./smash bros.wad")
if err != nil {
e.Error(2, "couldn't read wad", err)
return
}
wad.ChangeTitleKey(contentAesKey)
tik, err := wad.GetTicket()
if err != nil {
e.Error(2, "couldn't read ticket", err)
return
}
os.WriteFile("content/cetk", tik, 0777)
wadBytes, err := wad.GetWAD(wadlib.WADTypeCommon)
if err != nil {
e.Error(2, "couldn't get wad", err)
return
}
wadContent1, err := wad.GetContent(0)
os.WriteFile("content/00000000", wadContent1, 0777)
wadContent2, err := wad.GetContent(1)
os.WriteFile("content/00000001", wadContent2, 0777)
wadContent3, err := wad.GetContent(2)
os.WriteFile("content/00000002", wadContent3, 0777)
wadContent4, err := wad.GetContent(3)
os.WriteFile("content/00000003", wadContent4, 0777)
wadContent5, err := wad.GetContent(4)
os.WriteFile("content/00000004", wadContent5, 0777)
wadContent6, err := wad.GetContent(5)
os.WriteFile("content/00000005", wadContent6, 0777)
wadContent7, err := wad.GetContent(6)
os.WriteFile("content/00000006", wadContent7, 0777)
wadContent8, err := wad.GetContent(7)
os.WriteFile("content/00000007", wadContent8, 0777)
wadTmd, err := wad.GetTMD()
os.WriteFile("content/tmd", wadTmd, 0777)
os.WriteFile("smashnew.wad", wadBytes[:], 0777)*/
//version := 0
if titleId == WiinoMaServiceTitleID {
// Wii no Ma needs the ticket to be in the v1 ticket format.
// 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 {
e.Error(2, "failed to create ticket", err)
return
}
refId, err := e.getKey("ReferenceId")
if err != nil {
e.Error(2, "missing reference ID", err)
return
}
// Convert reference ID to bytes
refIdBytes, err := hex.DecodeString(refId)
log.Printf("refIdBytes err: %b", refIdBytes)
if err != nil {
log.Printf("unexpected error converting reference id to bytes: %v", err)
e.Error(2, "error purchasing", nil)
return
}
var referenceId [16]byte
copy(referenceId[:], refIdBytes)
subscriptions := []v1Ticket.V1SubscriptionRecord{
{
ExpirationTime: uint32(time.Now().AddDate(0, 1, 0).Unix()),
ReferenceID: referenceId,
},
}
// Query the database for other purchased items of the same title id.
rows, err := pool.Query(ctx, QueryOwnedServiceTitles, titleId, accountId)
if err != nil {
log.Printf("unexpected error purchasing: %v", err)
e.Error(2, "error purchasing", nil)
return
}
defer rows.Close()
for rows.Next() {
var currentRefIdString string
var purchasedTime time.Time
err = rows.Scan(&currentRefIdString, &purchasedTime, nil)
if err != nil {
log.Printf("unexpected error purchasing: %v", err)
e.Error(2, "error purchasing", nil)
return
}
refIdBytes, err = hex.DecodeString(currentRefIdString)
if err != nil {
log.Printf("unexpected error converting reference id to bytes: %v", err)
e.Error(2, "error purchasing", nil)
return
}
var currentReferenceId [16]byte
copy(currentReferenceId[:], refIdBytes)
subscriptions = append(subscriptions, v1Ticket.V1SubscriptionRecord{
ExpirationTime: uint32(purchasedTime.AddDate(0, 0, 30).Unix()),
ReferenceID: currentReferenceId,
})
}
newTicket, err := v1Ticket.CreateV1Ticket(ticket.Bytes(), subscriptions)
if err != nil {
log.Printf("unexpected error creating v1Ticket: %v", err)
e.Error(2, "error creating ticket", nil)
return
}
ticket = bytes.NewBuffer(newTicket)
} else {
// Validate that this title exists.
app, err := GetOSCApp(titleId)
if err != nil {
e.Error(2, "an error has occurred retrieving app metadata", err)
return
}
if app == nil {
e.Error(2, "title does not exist", nil)
return
}
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.
//_, err = pool.Exec(ctx, AssociateTicketStatement, accountId, titleId, version, itemId, time.Now().UTC())
//if err != nil {
// log.Printf("unexpected error purchasing: %v", err)
// e.Error(2, "error purchasing", nil)
//}
amount, err := e.getKey("Amount")
if err != nil {
e.Error(2, "couldn't get amount", err)
}
amount = strings.ReplaceAll(amount, ".", "")
amountInt, err := strconv.Atoi(amount)
if err != nil {
e.Error(2, "couldn't convert amount to integer", err)
}
// The returned ticket is expected to have two other certificates associated.
//ticketString := b64(append(ticket.Bytes(), wadlib.CertChainTemplate...))
ticketString := b64(tikFile)
//var pointsToRemove int
e.AddCustomType(getBalance(e))
e.AddCustomType(Transactions{
TransactionId: "00000000",
Date: e.Timestamp(),
Type: "PURCHGAME",
TotalPaid: amountInt,
Currency: "POINTS",
ItemId: itemId,
ItemPricing: Prices{
ItemId: itemId,
Price: Price{Amount: amountInt, Currency: "POINTS"},
Limits: LimitStruct(PR),
LicenseKind: PERMANENT,
},
})
var points string
deviceToken, err := e.getKey("DeviceToken")
if err != nil {
e.Error(2, "missing device token", err)
return
}
deviceToken = strings.Split(deviceToken, "-")[1]
md5DeviceToken := fmt.Sprintf("%x", md5.Sum([]byte(deviceToken)))
serialNo, err := e.getKey("SerialNo")
if err != nil {
e.Error(2, "missing serial", err)
return
}
points = strconv.Itoa(getBalance(e).Amount - amountInt)
_, err = pool.Exec(ctx, AssociatePointsStatement, e.DeviceId(), deviceToken, md5DeviceToken, accountId, e.region, serialNo, points)
if err != nil {
e.Error(103, "error calculating points", err)
}
e.AddKVNode("SyncTime", e.Timestamp())
//log.Println(ticketString)
e.AddKVNode("ETickets", ticketString)
//e.AddKVNode("ETickets", "AAEAAR1dFX4o1WcYlRaHWYwiNPqKuGB6xHbZ46f8w9LBZXRkX/5P9ORxHKwjeWI0sAM3m0bCn4gR1et4821FrDw1gidnMcRqolZqvvtDhtRj3whdYbDgIii1lmpZPOOoWQnTKaKJp6/FUm+eytBB/wr9azJ69tyP3fp0Brg1m8dgRajg5gs2rTKOEpOeuCGMaQBB/a05TRf9zyfvOhL8kHQher8lB2AdkkL88AD2PqIDV4HeE82dk7hi/NI1HZVJejf5aOcswc3FhDNHFSBMKKBPGpVD7RHCjJVEoPXa7LhdZZ9nkpmgQaa1F8s06N5q/yD1rwtWnTyHca7U1z9BoTghn5EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSb290LUNBMDAwMDAwMDEtWFMwMDAwMDAwMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALTS+bbifI4ioLdMFp2KjnlkPV+Gq0jq3IjvdPz6AEIHZUFmW9vJW+S/PyWqqdndg67xqOOy5EOHteuwAAAAT2D7XouKddLkZlKJXqcuegAAtzdsE3eQLQJ7CPUAAQABSEFERf//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//////////////////////////////////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==")
// Two cert types must be present.
e.AddKVNode("Certs", b64(wadlib.CertChainTemplate))
e.AddKVNode("Certs", b64(wadlib.CertChainTemplate))
e.AddKVNode("TitleId", titleId)
}
func listPurchaseHistory(e *Envelope) {
accountId, err := e.AccountId()
if err != nil {
e.Error(2, "missing account ID", err)
return
}
titleId, err := e.getKey("ApplicationId")
if err != nil {
e.Error(2, "missing application ID", err)
return
}
var transactions []Transactions
if titleId == WiinoMaApplicationID {
// We will query the database differently for Wii no Ma.
rows, err := pool.Query(ctx, QueryOwnedServiceTitles, WiinoMaServiceTitleID, accountId)
if err != nil {
log.Printf("unexpected error querying owned service titles: %v", err)
e.Error(2, "error purchasing", nil)
return
}
defer rows.Close()
for rows.Next() {
var refId string
var purchasedTime time.Time
var itemId int
err = rows.Scan(&refId, &purchasedTime, &itemId)
if err != nil {
log.Printf("unexpected error purchasing: %v", err)
e.Error(2, "error purchasing", nil)
return
}
transaction := Transactions{
TransactionId: "00000000",
// (Sketch) I don't know why but Wii no Ma won't acknowledge the entry if it isn't past a day from
// purchase.
Date: strconv.Itoa(int(purchasedTime.AddDate(0, 0, -1).UnixMilli())),
Type: "PURCHGAME",
TotalPaid: 0,
Currency: "POINTS",
ItemId: itemId,
ItemPricing: Prices{
ItemId: itemId,
Price: Price{
Amount: 0,
Currency: "POINTS",
},
Limits: LimitStruct(PR),
LicenseKind: SERVICE,
},
TitleId: WiinoMaServiceTitleID,
ItemCode: itemId,
ReferenceId: refId,
}
transactions = append(transactions, transaction)
}
} else {
transactions = append(transactions, Transactions{
TransactionId: "00000000",
// Is timestamp in milliseconds, placeholder one is Wed Oct 19 2022 18:02:46
Date: "1666202566218",
Type: "PURCHGAME",
TotalPaid: 0,
Currency: "POINTS",
ItemId: 0,
ItemPricing: Prices{
ItemId: 0,
Price: Price{
Amount: 0,
Currency: "POINTS",
},
Limits: LimitStruct(PR),
LicenseKind: PERMANENT,
},
TitleId: "000101006843494A",
})
}
e.AddCustomType(transactions)
e.AddKVNode("ListResultTotalSize", strconv.Itoa(len(transactions)))
}
// genServiceUrl returns a URL with the given service against a configured URL.
// Given a baseUrl of example.com and genServiceUrl("ias", "IdentityAuthenticationSOAP"),
// it would return http://ias.example.com/ias/services/ias/IdentityAuthenticationSOAP.
func genServiceUrl(service string, path string) string {
return fmt.Sprintf("http://%s.%s/%s/services/%s", service, baseUrl, service, path)
}
func getECConfig(e *Envelope) {
contentUrl := fmt.Sprintf("http://ccs.%s/ccs/download", baseUrl)
e.AddKVNode("ContentPrefixURL", contentUrl)
e.AddKVNode("UncachedContentPrefixURL", contentUrl)
e.AddKVNode("SystemContentPrefixURL", contentUrl)
e.AddKVNode("SystemUncachedContentPrefixURL", contentUrl)
e.AddKVNode("EcsURL", genServiceUrl("ecs", "ECommerceSOAP"))
e.AddKVNode("IasURL", genServiceUrl("ias", "IdentityAuthenticationSOAP"))
e.AddKVNode("CasURL", genServiceUrl("cas", "CatalogingSOAP"))
e.AddKVNode("NusURL", genServiceUrl("nus", "NetUpdateSOAP"))
}
func purchasePoints(e *Envelope) {
deviceToken, err := e.getKey("DeviceToken")
if err != nil {
e.Error(2, "missing device token", err)
return
}
deviceToken = strings.Split(deviceToken, "-")[1]
md5DeviceToken := fmt.Sprintf("%x", md5.Sum([]byte(deviceToken)))
accountId, err := e.getKey("AccountId")
if err != nil {
e.Error(2, "missing account ID", err)
return
}
serialNo, err := e.getKey("SerialNo")
if err != nil {
e.Error(2, "missing serial", err)
return
}
itemId, err := e.getKey("ItemId")
if err != nil {
e.Error(2, "missing item ID", err)
return
}
amount, err := e.getKey("Amount")
if err != nil {
e.Error(2, "couldn't get amount", err)
}
currency, err := e.getKey("Currency")
if err != nil {
e.Error(2, "couldn't get currency", err)
}
var pointsToAdd int
var points string
if itemId == "100008" {
pointsToAdd = 1000
} else if itemId == "100030" {
pointsToAdd = 2000
} else if itemId == "100031" {
pointsToAdd = 3000
} else if itemId == "100032" {
pointsToAdd = 5000
}
points = strconv.Itoa(getBalance(e).Amount + pointsToAdd)
_, err = pool.Exec(ctx, AssociatePointsStatement, e.DeviceId(), deviceToken, md5DeviceToken, accountId, e.region, serialNo, points)
if err != nil {
e.Error(113, "error purchasing points", err)
}
itemIdStr, err := strconv.Atoi(itemId)
if err != nil {
e.Error(201, "couldn't convert item id to string", err)
}
rand.NewSource(time.Now().UnixNano())
// Generate a random 8-digit integer
transactionId := 10000000 + rand.Intn(90000000)
e.AddCustomType(PointsPurchaseInfo{
Transactions: PointsTransactions{
TransactionId: strconv.Itoa(transactionId),
Date: e.Timestamp(),
Type: "PURCHPOINTS",
TotalPaid: amount,
Currency: currency,
ItemId: itemId,
ItemPricing: GiftPrices{
ItemId: itemIdStr,
Price: GiftPrice{
Amount: amount,
Currency: currency,
},
},
},
})
}
func checkAccountBalance(e *Envelope) {
e.AddCustomType(getBalance(e))
}
func giftTitle(e *Envelope) {
accountId, err := e.getKey("AccountId")
if err != nil {
e.Error(1, "missing mandatory key named AccountId", err)
}
titleId, err := e.getKey("TitleId")
if err != nil {
e.Error(1, "missing mandatory key named TitleId", err)
}
notes, err := e.getKey("Notes")
if err != nil {
e.Error(1, "missing mandatory key named Notes", err)
}
unescapedNotes := html.UnescapeString(notes)
doc, err := xmlquery.Parse(strings.NewReader(unescapedNotes))
if err != nil {
fmt.Println("Error:", err)
return
}
senderFCNode := xmlquery.FindOne(doc, "//DeviceCode")
var senderFC string
if senderFCNode != nil {
senderFC = senderFCNode.InnerText()
} else {
e.Error(124, "Cannot find sender friend code", nil)
}
amount, err := e.getKey("Amount")
if err != nil {
e.Error(2, "couldn't get amount", err)
}
amount = strings.ReplaceAll(amount, ".", "")
log.Println("New amount: " + amount)
amountInt, err := strconv.Atoi(amount)
if err != nil {
e.Error(2, "couldn't convert amount to integer", err)
}
rand.NewSource(time.Now().UnixNano())
// Generate a random 8-digit integer
transactionId := 10000000 + rand.Intn(90000000)
log.Println(transactionId)
_, err = pool.Exec(ctx, AssociateGiftedTitleStatement, titleId, strconv.Itoa(transactionId+1), senderFC)
if err != nil {
e.Error(123, "error putting title in gifted titles table", err)
}
var points string
deviceToken, err := e.getKey("DeviceToken")
if err != nil {
e.Error(2, "missing device token", err)
return
}
deviceToken = strings.Split(deviceToken, "-")[1]
md5DeviceToken := fmt.Sprintf("%x", md5.Sum([]byte(deviceToken)))
serialNo, err := e.getKey("SerialNo")
if err != nil {
e.Error(2, "missing serial", err)
return
}
points = strconv.Itoa(getBalance(e).Amount - amountInt)
_, err = pool.Exec(ctx, AssociatePointsStatement, e.DeviceId(), deviceToken, md5DeviceToken, accountId, e.region, serialNo, points)
if err != nil {
e.Error(133, "error calculating points", err)
}
e.AddCustomType(getBalance(e))
e.AddCustomType(GiftTransactions{
TransactionId: strconv.Itoa(transactionId),
Date: e.Timestamp(),
Type: "PGIFTGAME",
})
e.AddCustomType(GiftTransactions{
TransactionId: strconv.Itoa(transactionId + 1),
Date: e.Timestamp(),
Type: "RGIFTGAME",
})
}
func acceptGiftTitle(e *Envelope) {
titleId, err := e.getKey("TitleId")
if err != nil {
e.Error(2, "missing mandatory key named TitleId", err)
}
accept, err := e.getKey("Accept")
if err != nil {
e.Error(2, "missing mandatory key named Accept", err)
}
transId, err := e.getKey("TransactionId")
if err != nil {
e.Error(2, "missing mandatory key named TransactionId", err)
}
if accept != "1" {
e.Error(10, "not accepting", nil)
}
_, err = pool.Exec(ctx, RemoveGiftedTitleStatement, titleId, transId)
if err != nil {
e.Error(143, "error removing title from database", err)
}
//Probably need to add ETicket, but will do later.
}