CI / build (push) Successful in 36s
Hosted rooms (internal/hosted): room calls for them answered here, their IMQ queues served (a new proxy.IMQRouter hook takes them out of subscriptions to IMVU), chat relayed through the hub's WebSocket (hub.Client gains Send, OnRoom and OnConnect), the room list extended, room products checked against the real owner, and anything else naming a hosted room kept from IMVU. T5DE_INSTANCE gives a second client's proxy its own data folder and log. Co-Authored-By: Claude Opus 5.5 <[email protected]>
104 lines
7.2 KiB
Markdown
104 lines
7.2 KiB
Markdown
# t5de-proxy
|
|
|
|
T5DE's local proxy for IMVU Classic. With T5DE's `t5de-proxy` patch, the client starts `t5de-proxy.exe` and
|
|
connects to IMVU through it:
|
|
|
|
- **Every HTTP request**, the embedded browser's included (IMVU's browser hands its requests to the client's
|
|
Python). The proxy makes them with current TLS and root certificates (the client has only IMVU's bundled
|
|
certificates and TLS 1.2), pooled connections and HTTP/2.
|
|
- **HTTPS for IMVU hosts the client reaches over plain HTTP** (its XML-RPC API, images, CSS), so its account
|
|
headers don't cross the network in the clear ([docs/hosts.md](docs/hosts.md)).
|
|
- **IMQ**, IMVU's chat and presence connection: the client speaks plaintext to the proxy, which connects to the
|
|
gateway over TLS.
|
|
- **IMVU's reporting endpoints refused**, as T5DE's patches already do in the client.
|
|
- **Community products** from local packs (t5de-api's pack format), kept away from IMVU (below).
|
|
|
|
If the proxy isn't there or stops, the client connects directly, as before.
|
|
|
|
Unofficial: not made by or affiliated with IMVU.
|
|
|
|
## How it works
|
|
|
|
The proxy listens on a free port of 127.0.0.1 and accepts only requests carrying the token it gave the client
|
|
(`Proxy-Authorization: Basic t5de:<token>`), so other programs and web pages can't use it.
|
|
|
|
- HTTP: requests in absolute form (`GET https://api.imvu.com/... HTTP/1.1`) over plain HTTP on loopback, for
|
|
http and https URLs alike. Responses come back as the server sent them: redirects aren't followed, bodies
|
|
aren't decompressed. When the proxy can't reach a server it answers 502 with `X-T5de-Proxy-Error`, which the
|
|
client raises as the network error it would have had connecting directly.
|
|
- `CONNECT host:port`: the client then speaks plaintext and the proxy connects to `host:port` over TLS. The
|
|
proxy reads IMQ's framing (message type and size, not contents) to log it.
|
|
|
|
The client starts it as `t5de-proxy -parent <pid> -ready <file> -log %TEMP%\t5de-proxy.log`; it writes its
|
|
address and token to the ready file once listening, and exits with the client. Setting `T5DE_PROXY_TRACE=1`
|
|
before starting the client adds full URLs (with queries) and every IMQ frame to the log.
|
|
|
|
## Community products
|
|
|
|
Phase 1 of T5DE's community products (design: t5de-hub's `docs/design.md`): products in local packs, a folder
|
|
each, in `%APPDATA%\T5DE\packs` and the folders in `T5DE_PACKS` (separated by `;`). Each product gets a local ID
|
|
from 2,000,000,000 up, kept in `%APPDATA%\T5DE\proxy\products.json`. For those IDs the proxy answers the client
|
|
itself:
|
|
|
|
- `test.inventoryProductBatch` (product info) and `test.authorizeProducts2` (permission, the CFL's address and
|
|
checksum): for a product deriving from an IMVU product, IMVU is asked about the parent and the proxy adds its own
|
|
entry (the client needs the parent's derivation chain).
|
|
- `productdata/<local ID>/<revision>`: the pack built into a CFL.
|
|
- `api/shop/product.php`: putting a product on asks whether it's a bundle (not), and the shop asks for product
|
|
details (answered from the hub's listing, or the pack; IMVU is asked only about its own products).
|
|
|
|
IMVU never sees community products: they're taken out of outfit saves (`api/base_look.php`) and outfit chat
|
|
commands (`*use`, `*putOn`, `*putOnOutfit`, `*remove`, `*takeOff` on `/chat/` queues; a command left empty is
|
|
answered by the proxy), so IMVU and everyone else see the defaults. The proxy remembers each account's community
|
|
products with the outfit they were saved with, and adds them back when the client loads that same outfit
|
|
(`base_look.php`, and `look` in the login response); an outfit changed elsewhere comes back without them.
|
|
Requests it changes are re-signed (`X-imvu-auth`) with the session key from the login response, which never
|
|
leaves the machine.
|
|
The client sends some product IDs as floats (`2000000008.0`), so every ID is read either way (`parseID`). As a
|
|
last check, `products.Guard` refuses any request to IMVU that still names a local ID given out, and logs it
|
|
(`products: refused a request to ...`): such a request is a path not handled yet.
|
|
|
|
## The hub
|
|
|
|
The client's Settings > T5DE page signs in and out (the proxy answers it at `http://t5de-proxy.local/hub`,
|
|
`internal/control`). `t5de-proxy signin [-hub URL]` also signs the proxy in to T5DE's hub (git.t5de.dev/t5de/t5de-hub; URL is the hub's
|
|
API): it shows a code to enter on the hub's website (`/device`, OAuth 2.0 device authorization) and saves the
|
|
session token it gets in `%APPDATA%\T5DE\proxy\hub.json`, encrypted for the Windows user (DPAPI). From then on the
|
|
running proxy keeps a WebSocket to the hub (`internal/hub`) with the token as a bearer token, reporting which IMVU
|
|
account the client signed in as, the community products it wears, and the IMVU chats it's in. If the hub ends the
|
|
session (signed out everywhere, banned), the proxy stops connecting until `signin` is run again.
|
|
|
|
The room channel (`internal/products/room.go`): the hub tells the proxy which other T5DE users are in the same chat
|
|
and what they wear; the proxy gives the client those of their products it has packs for, as `*use`/`*remove` lines
|
|
from them, and adds them to the whole outfits they send. It also keeps community products out of the outfit the
|
|
client posts to IMVU's chat participant endpoint.
|
|
|
|
The shop (the client's `t5de-shop` patch adds a T5 choice to its rating filter): searches with
|
|
`maturity_rating` `t5` (`api/shop/search_st.php`) are answered from the hub's products (`GET /api/v1/products`,
|
|
filtered by the shop's categories against each product's IMVU parent's category path), fetching listed products
|
|
that aren't here yet (up to 15 seconds) so they can be shown and tried on. Buying T5 products
|
|
(`api/service/shop/svc_catalog_purchase.php`) adds them to the person's hub library; IMVU products in the same
|
|
purchase go on to IMVU without them.
|
|
|
|
T5DE-hosted rooms (`internal/hosted`, t5de-hub's `docs/hosted-rooms.md`): rooms living on the hub, with instance IDs
|
|
`2100000000-<n>` and chat IDs `2100000000+n`. The proxy answers the client's room calls for them (`getRoomCardInfo`,
|
|
`getOrMakeChat`, `getRoomState`, `setRoomState`, `getParticipants`, `terminateChat`), serves their IMQ queues
|
|
(`/chat/<id>`, `inv:/room/room-<id>`, `room_occupancy/<id>`: subscriptions answered here through `proxy.IMQRouter`,
|
|
chat to and from the hub over its WebSocket), adds them to the room list, checks the room's products against its real
|
|
owner (`authorizeProducts2`), and answers any other request to IMVU naming one ("not found") rather than send it.
|
|
|
|
`T5DE_INSTANCE=<name>` (set by the client's `t5de-instances` patch for a second client run beside the first) keeps
|
|
the proxy's data in `%APPDATA%\T5DE-<name>\proxy` and its log in `t5de-proxy-<name>.log`.
|
|
|
|
For finding out what IMVU's APIs send, `T5DE_PROXY_DUMP=<text>` logs the request and response bodies of requests
|
|
whose path contains it (test accounts only: bodies hold personal data). In Git Bash, keep the value from starting
|
|
with `/` (or set `MSYS_NO_PATHCONV=1`), or it's turned into a Windows path.
|
|
|
|
## Building
|
|
|
|
go test ./...
|
|
GOOS=windows GOARCH=amd64 go build -trimpath -ldflags "-H=windowsgui" -o dist/t5de-proxy.exe ./cmd/t5de-proxy
|
|
|
|
CI publishes `t5de-proxy.exe`: pushes to main replace the `nightly` pre-release, `vX.Y.Z` tags make releases.
|
|
To ship a release in T5DE, copy its url and sha256 into t5de's `t5de.toml` (`[[programs]]`, patch `t5de-proxy`).
|