Show T5 products in the client's shop; keep float IDs and unknown paths off IMVU
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]>
This commit is contained in:
dhkatzandClaude Opus 5.5 committed 2026-10-30 01:15:16 -07:00
1 parent 6506c641f0
commit 2280ad6314
14 files changed
+785 -18

No files matched your search

+21 -8
View File
@@ -40,9 +40,15 @@ func (p *Products) Intercept(x *proxy.Exchange) error {
return p.interceptXMLRPC(x)
case "/api/shop/product.php":
// Putting a product on asks the shop whether it's a bundle; community products aren't.
if id, err := strconv.ParseInt(x.URL.Query().Get("pid"), 10, 64); err == nil && IsCommunity(id) {
if id, ok := parseID(x.URL.Query().Get("pid")); ok && IsCommunity(id) {
x.Response = localResponse(200, "application/json", []byte(`{"products":[]}`))
} else if len(x.Body) > 0 {
return p.interceptProductInfo(x)
}
case "/api/shop/search_st.php":
return p.interceptShopSearch(x)
case "/api/service/shop/svc_catalog_purchase.php":
return p.interceptPurchase(x)
case "/api/base_look.php":
return p.interceptBaseLook(x)
case "/api/service/inventory/inventory_delete.php":
@@ -166,11 +172,18 @@ func productList(v xmlrpc.Value) ([]int64, bool) {
}
ids := make([]int64, 0, len(list))
for _, v := range list {
n, ok := v.(int64)
if !ok {
switch n := v.(type) {
case int64:
ids = append(ids, n)
case float64:
id, ok := parseID(strconv.FormatFloat(n, 'f', -1, 64))
if !ok {
return nil, false
}
ids = append(ids, id)
default:
return nil, false
}
ids = append(ids, n)
}
return ids, true
}
@@ -568,8 +581,8 @@ func (p *Products) interceptHide(x *proxy.Exchange) error {
if err != nil {
return nil
}
pid, err := strconv.ParseInt(form.Get("pid"), 10, 64)
if err != nil || !IsCommunity(pid) {
pid, ok := parseID(form.Get("pid"))
if !ok || !IsCommunity(pid) {
return nil
}
if id, ok := p.state.productID(pid); ok && p.OnHide != nil {
@@ -596,8 +609,8 @@ func (p *Products) interceptOutfits(x *proxy.Exchange) error {
var community []string
stripped := 0
for _, f := range strings.Split(products, ",") {
id, err := strconv.ParseInt(strings.TrimSpace(f), 10, 64)
if err == nil && IsCommunity(id) {
id, ok := parseID(f)
if ok && IsCommunity(id) {
stripped++ // always kept from IMVU, even if its product isn't known anymore
if pid, ok := p.state.productID(id); ok {
community = append(community, pid)