You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
205 lines
4.7 KiB
205 lines
4.7 KiB
package altvr
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
)
|
|
|
|
/*
|
|
X-CSRF-Token: 14VIIeFLgADJVsdemydLkdyEq61KFedTfpC5AGuunv4Svodeu86AysE+VADloZv6c0l9yqCcXWtZ3hP1p93qrQ==
|
|
User-Agent: UnityPlayer/2019.4.2f1 (UnityWebRequest/1.0, libcurl/7.52.0-DEV)
|
|
X-AppName: altspace_vr_client
|
|
X-AppVersion: 4.0.95.d065d
|
|
X-AltspaceVR-Version: AltspaceVR-App 4.0.95.d065d
|
|
Authorization: Token <token> // _Positron_session
|
|
X-Unity-Version: 2019.4.2f1
|
|
|
|
// {"reason":"bad_credentials"}
|
|
*/
|
|
|
|
const (
|
|
// Host is the API host
|
|
host = "account.altvr.com"
|
|
)
|
|
|
|
var (
|
|
errorLogin = errors.New("Not logged in")
|
|
etags = map[string]etag{}
|
|
)
|
|
|
|
type etag struct {
|
|
key string
|
|
date string
|
|
body []byte
|
|
}
|
|
|
|
// Clear HTTP response cache.
|
|
func flushEtags() {
|
|
for url := range etags {
|
|
delete(etags, url)
|
|
}
|
|
}
|
|
|
|
func jarGetToken(jar http.CookieJar, u *url.URL) string {
|
|
for _, cookie := range jar.Cookies(u) {
|
|
if cookie.Name == "_Positron_session" {
|
|
return cookie.Value
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func (avr *AltVR) reqSetAuthToken(r *http.Request, u *url.URL) {
|
|
token := jarGetToken(avr.jar, u)
|
|
if token != "" {
|
|
r.Header.Set("Authorization", "Token "+token)
|
|
}
|
|
}
|
|
|
|
func (avr *AltVR) get(path string) ([]byte, error) {
|
|
apiURL, err := absolutePath(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
req, err := avr.buildGetRequest(apiURL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
client := &http.Client{Jar: avr.jar}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
// Check for no changes
|
|
if resp.StatusCode == http.StatusNotModified {
|
|
return etags[apiURL.String()].body, nil
|
|
}
|
|
|
|
if resp.StatusCode == 401 {
|
|
return nil, errorLogin
|
|
}
|
|
|
|
if resp.StatusCode != 200 {
|
|
return nil, fmt.Errorf("Page unavailable (%d)", resp.StatusCode)
|
|
}
|
|
/*
|
|
ctype := resp.Header.Get("content-Type")
|
|
if !strings.HasPrefix(ctype, "application/json") {
|
|
return "", errors.New("Invalid content type")
|
|
}
|
|
*/
|
|
return avr.updateEtagCache(apiURL.String(), resp)
|
|
}
|
|
|
|
func (avr *AltVR) post(path string, reader *strings.Reader) ([]byte, error) {
|
|
apiURL, err := absolutePath(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
req, err := avr.buildPostRequest(apiURL, reader)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
client := &http.Client{Jar: avr.jar}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode == 401 {
|
|
return nil, errorLogin
|
|
}
|
|
|
|
if resp.StatusCode != 200 && resp.StatusCode != 201 {
|
|
return nil, fmt.Errorf("Page unavailable (%d)", resp.StatusCode)
|
|
}
|
|
/*
|
|
ctype := resp.Header.Get("content-Type")
|
|
if !strings.HasPrefix(ctype, "application/json") {
|
|
return "", errors.New("Invalid content type")
|
|
}
|
|
*/
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return body, nil
|
|
}
|
|
|
|
func (avr *AltVR) updateEtagCache(apiURL string, resp *http.Response) ([]byte, error) {
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
key := resp.Header.Get("ETag")
|
|
date := resp.Header.Get("Date")
|
|
if key == "" /*|| date == ""*/ {
|
|
return body, err
|
|
}
|
|
etags[apiURL] = etag{key, date, body}
|
|
return body, err
|
|
}
|
|
|
|
func (avr *AltVR) buildRequest(req *http.Request, apiURL *url.URL) (*http.Request, error) {
|
|
req.Header.Set("User-Agent", "UnityPlayer/2019.4.2f1 (UnityWebRequest/1.0, libcurl/7.52.0-DEV)")
|
|
req.Header.Set("X-AppName", "altspace_vr_client")
|
|
req.Header.Set("X-AppVersion", "4.0.95.d065d")
|
|
req.Header.Set("X-AltspaceVR-Version", "AltspaceVR-App 4.0.95.d065d")
|
|
req.Header.Set("X-Unity-Version", "2019.4.2f1")
|
|
req.Header.Set("Accept-Encoding", "identity")
|
|
req.Header.Set("X-CSRF-Token", avr.csrfToken)
|
|
req.Header.Set("Accept", "*/*")
|
|
avr.reqSetAuthToken(req, apiURL)
|
|
return req, nil
|
|
}
|
|
|
|
func (avr *AltVR) buildGetRequest(apiURL *url.URL) (*http.Request, error) {
|
|
req, err := http.NewRequest("GET", apiURL.String(), nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if _, present := etags[apiURL.String()]; present {
|
|
req.Header.Add("If-None-Match", etags[apiURL.String()].key)
|
|
//req.Header.Add("If-Modified-Since", etags[apiURL.String()].date)
|
|
}
|
|
return avr.buildRequest(req, apiURL)
|
|
}
|
|
|
|
func (avr *AltVR) buildPostRequest(apiURL *url.URL, reader *strings.Reader) (*http.Request, error) {
|
|
req, err := http.NewRequest("POST", apiURL.String(), reader)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
req.Header.Set("Content-Type", "application/json; charset=utf8")
|
|
return avr.buildRequest(req, apiURL)
|
|
}
|
|
|
|
func absolutePath(path string) (*url.URL, error) {
|
|
if !strings.HasPrefix(path, "/") {
|
|
path = "/" + path
|
|
}
|
|
apiURL, err := url.Parse(api() + path)
|
|
query := apiURL.Query()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
apiURL.RawQuery = query.Encode()
|
|
return apiURL, nil
|
|
}
|
|
|
|
func api() string {
|
|
return "https://" + host + "/api"
|
|
}
|