69 lines
2.0 KiB
Go
69 lines
2.0 KiB
Go
package products
|
|
|
|
import (
|
|
"crypto/md5"
|
|
"encoding/hex"
|
|
"net/http"
|
|
"regexp"
|
|
"sync"
|
|
)
|
|
|
|
// IMVU signs the client's requests: X-imvu-auth is md5(user ID + session security key + body), with the query
|
|
// string added for some calls. The proxy re-signs requests it changes (taking community products out), with the key
|
|
// from the login response. The key never leaves this machine.
|
|
|
|
func sign(user, key string, body []byte, query string, withQuery bool) string {
|
|
h := md5.New()
|
|
h.Write([]byte(user + key))
|
|
h.Write(body)
|
|
if withQuery {
|
|
h.Write([]byte(query))
|
|
}
|
|
return hex.EncodeToString(h.Sum(nil))
|
|
}
|
|
|
|
// keys holds each signed-in user's security key.
|
|
type keys struct {
|
|
mu sync.Mutex
|
|
byID map[string]string
|
|
}
|
|
|
|
var securityKeyPattern = regexp.MustCompile(`<name>securityKey</name>\s*<value>(?:<string>)?([0-9A-Za-z]+)`)
|
|
var userIDPattern = regexp.MustCompile(`<name>userId</name>\s*<value>(?:<int>|<i4>|<string>)?([0-9]+)`)
|
|
|
|
// learn takes the user's key from a login response (test.avatarInfoForLogin2).
|
|
func (k *keys) learn(body []byte) {
|
|
key, user := securityKeyPattern.FindSubmatch(body), userIDPattern.FindSubmatch(body)
|
|
if key == nil || user == nil {
|
|
return
|
|
}
|
|
k.mu.Lock()
|
|
defer k.mu.Unlock()
|
|
if k.byID == nil {
|
|
k.byID = map[string]string{}
|
|
}
|
|
k.byID[string(user[1])] = string(key[1])
|
|
}
|
|
|
|
// resign recomputes X-imvu-auth for a request whose body or query changed, in the scheme the original used. It
|
|
// reports false if it couldn't (no key, or the original signature didn't match either scheme).
|
|
func (k *keys) resign(h http.Header, oldBody []byte, oldQuery string, newBody []byte, newQuery string) bool {
|
|
auth, user := h.Get("X-imvu-auth"), h.Get("X-imvu-userid")
|
|
if auth == "" {
|
|
return true // not signed
|
|
}
|
|
k.mu.Lock()
|
|
key, ok := k.byID[user]
|
|
k.mu.Unlock()
|
|
if !ok {
|
|
return false
|
|
}
|
|
for _, withQuery := range []bool{false, true} {
|
|
if sign(user, key, oldBody, oldQuery, withQuery) == auth {
|
|
h.Set("X-imvu-auth", sign(user, key, newBody, newQuery, withQuery))
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|