CI / build (push) Successful in 34s
The shop's T5 filter (maturity_rating "t5") is answered from the hub's products, fetched so they can be
shown and tried on; buying T5 products adds them to the hub library, and product.php details for
community products are answered here.
The client sends some product IDs as floats ("2000000008.0"): product.php passed the worn community
product to IMVU that way. Every ID is now read either way, and products.Guard refuses any request to
IMVU that still names a local ID given out. T5DE_PROXY_DUMP logs request and response bodies.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
94 lines
6.4 KiB
Markdown
94 lines
6.4 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.
|
|
|
|
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`).
|