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 // _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" }