The inventory's product list (test.showInventory) gets the community products the person has: local packs, and hub products in their library. Hiding one is answered by the proxy (and removes a hub product from the library). When the library changes, the client is told to load its inventory again, with the server event IMVU sends after a purchase. A product whose revision or info changes gets a new local ID, since the client keeps product info for good; names lose characters the client's pages could take as HTML. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
c281fb572c
commit
ca7a86ce89
8 files changed
+225
-5
No files matched your search
@@ -4,6 +4,9 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -42,6 +45,8 @@ func (p *Products) Intercept(x *proxy.Exchange) error {
|
||||
}
|
||||
case "/api/base_look.php":
|
||||
return p.interceptBaseLook(x)
|
||||
case "/api/service/inventory/inventory_delete.php":
|
||||
return p.interceptHide(x)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -66,6 +71,10 @@ func (p *Products) interceptXMLRPC(x *proxy.Exchange) error {
|
||||
return p.inventoryProductBatch(x, call)
|
||||
case "test.authorizeProducts2":
|
||||
return p.authorizeProducts2(x, call)
|
||||
case "test.showInventory":
|
||||
x.ModifyResponse = func(resp *http.Response, body []byte) ([]byte, error) {
|
||||
return p.addToInventory(body)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -241,8 +250,8 @@ func (s *served) productInfo(parent xmlrpc.Struct) xmlrpc.Struct {
|
||||
own := map[string]xmlrpc.Value{
|
||||
"products_id": s.local,
|
||||
"manufacturers_id": int64(0),
|
||||
"manufacturers_name": m.Creator,
|
||||
"products_name": m.Name,
|
||||
"manufacturers_name": plain(m.Creator),
|
||||
"products_name": plain(m.Name),
|
||||
"products_mature": mature,
|
||||
"products_bundle": "",
|
||||
"is_published": "1",
|
||||
@@ -486,3 +495,79 @@ func (p *Products) Outgoing(queue, mount string, message []byte) ([]byte, bool)
|
||||
p.logf("products: kept %d community products out of a %s sent to IMVU", len(r.products), command)
|
||||
return m.withText(r.text), false
|
||||
}
|
||||
|
||||
// plain keeps a name to characters the client's pages can show however they insert it: some insert creator and
|
||||
// product names as HTML, in pages that can call into the client.
|
||||
func plain(s string) string {
|
||||
return strings.Map(func(r rune) rune {
|
||||
switch r {
|
||||
case '<', '>', '&', '"', '\'', '`':
|
||||
return -1
|
||||
}
|
||||
return r
|
||||
}, s)
|
||||
}
|
||||
|
||||
// inventoryLocals is the community products in the inventory: local packs, and hub products in the library.
|
||||
func (p *Products) inventoryLocals() []int64 {
|
||||
p.refresh(false)
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
var ids []int64
|
||||
for id, s := range p.byID {
|
||||
if p.HubDir != "" && filepath.Dir(s.dir) == filepath.Clean(p.HubDir) && (p.Owned == nil || !p.Owned(id)) {
|
||||
continue
|
||||
}
|
||||
ids = append(ids, s.local)
|
||||
}
|
||||
sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
|
||||
return ids
|
||||
}
|
||||
|
||||
// addToInventory adds the community products to test.showInventory's answer (a list of product IDs).
|
||||
func (p *Products) addToInventory(body []byte) ([]byte, error) {
|
||||
r, err := xmlrpc.ParseResponse(body)
|
||||
if err != nil || r.Fault != nil || len(r.Params) != 1 {
|
||||
return body, nil
|
||||
}
|
||||
list, ok := r.Params[0].([]xmlrpc.Value)
|
||||
if !ok {
|
||||
return body, nil
|
||||
}
|
||||
have := map[int64]bool{}
|
||||
for _, v := range list {
|
||||
if n, ok := v.(int64); ok {
|
||||
have[n] = true
|
||||
}
|
||||
}
|
||||
added := 0
|
||||
for _, id := range p.inventoryLocals() {
|
||||
if !have[id] {
|
||||
list = append(list, id)
|
||||
added++
|
||||
}
|
||||
}
|
||||
if added == 0 {
|
||||
return body, nil
|
||||
}
|
||||
r.Params[0] = list
|
||||
p.logf("products: %d community products in the inventory", added)
|
||||
return r.Marshal(), nil
|
||||
}
|
||||
|
||||
// interceptHide answers hiding a community product in the inventory itself (IMVU doesn't know it).
|
||||
func (p *Products) interceptHide(x *proxy.Exchange) error {
|
||||
form, err := url.ParseQuery(string(x.Body))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
pid, err := strconv.ParseInt(form.Get("pid"), 10, 64)
|
||||
if err != nil || !IsCommunity(pid) {
|
||||
return nil
|
||||
}
|
||||
if id, ok := p.state.productID(pid); ok && p.OnHide != nil {
|
||||
p.OnHide(id)
|
||||
}
|
||||
x.Response = localResponse(200, "application/json", []byte(`{"success": true}`))
|
||||
return nil
|
||||
}
|
||||
Reference in new issue
Block a user