Keep community products out of saved outfits
CI / build (push) Successful in 43s

Saving an outfit (POST /api/outfits.php) sent its community products to IMVU. They're now taken out (any ID in
the community range, known or not), the request re-signed, and the products remembered under the outfit's ID,
then added back when the client loads its saved outfits (marked AP if one is adult).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
dhkatzandClaude Opus 5.5 committed 2026-10-29 19:30:59 -07:00
1 parent 400831bf3c
commit 6506c641f0
3 files changed
+159 -2

No files matched your search

+37
View File
@@ -73,6 +73,13 @@ func (f *fakeIMVU) RoundTrip(r *http.Request) (*http.Response, error) {
return reply(`{"status": "success"}`)
}
switch r.URL.Path {
case "/api/outfits.php":
if len(body) > 0 {
checkSigned(false)
f.record("save outfit", body)
return reply(`{"result": {"outfit_id": 555}}`)
}
return reply(`{"result": [{"user_outfits_id": "555", "pids": [80, 1], "restricted": "0"}, {"user_outfits_id": "9", "pids": [80]}]}`)
case "/api/xmlrpc/chat.php":
f.record("chat", body)
return reply(string((&xmlrpc.Response{Params: []xmlrpc.Value{xmlrpc.Struct{
@@ -361,3 +368,33 @@ func TestInventory(t *testing.T) {
t.Fatalf("product of the new local ID: %q", id)
}
}
func TestSavedOutfits(t *testing.T) {
h, _ := newHarness(t)
local := h.p.byProductID("blue-coat").local
h.do("POST", "https://secure.imvu.com/api/xmlrpc/client.php", call("test.avatarInfoForLogin2"), false, false)
// Saving: IMVU gets the outfit without the community product.
// (2100000000 is a community ID the proxy doesn't know: it's kept from IMVU all the same.)
save := []byte(fmt.Sprintf(`{"outfit_name": "Mine", "products": "80,1,%d,2100000000", "privacy": 1}`, local))
if status, _ := h.do("POST", "https://client-dynamic.imvu.com/api/outfits.php", save, true, false); status != 200 {
t.Fatalf("status %d", status)
}
if got := h.imvu.asked["save outfit"]; len(got) != 1 || !strings.Contains(got[0], `"products":"80,1"`) {
t.Fatalf("IMVU got %q", got)
}
// Loading saved outfits: it's back in that outfit only.
_, body := h.do("GET", "https://client-dynamic.imvu.com/api/outfits.php?batch=555,9", nil, false, false)
var r struct {
Result []struct {
ID string `json:"user_outfits_id"`
Pids []int64 `json:"pids"`
} `json:"result"`
}
if err := json.Unmarshal(body, &r); err != nil {
t.Fatal(err, string(body))
}
if fmt.Sprint(r.Result[0].Pids) != fmt.Sprint([]int64{80, 1, local}) || fmt.Sprint(r.Result[1].Pids) != "[80]" {
t.Fatalf("outfits %s", body)
}
}