summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/Philipp15b/go-steam/web.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/Philipp15b/go-steam/web.go')
-rw-r--r--vendor/github.com/Philipp15b/go-steam/web.go143
1 files changed, 143 insertions, 0 deletions
diff --git a/vendor/github.com/Philipp15b/go-steam/web.go b/vendor/github.com/Philipp15b/go-steam/web.go
new file mode 100644
index 00000000..0b14db2c
--- /dev/null
+++ b/vendor/github.com/Philipp15b/go-steam/web.go
@@ -0,0 +1,143 @@
+package steam
+
+import (
+ "crypto/aes"
+ "crypto/rand"
+ "encoding/base64"
+ "encoding/json"
+ "errors"
+ "github.com/Philipp15b/go-steam/cryptoutil"
+ . "github.com/Philipp15b/go-steam/protocol"
+ . "github.com/Philipp15b/go-steam/protocol/protobuf"
+ . "github.com/Philipp15b/go-steam/protocol/steamlang"
+ "github.com/golang/protobuf/proto"
+ "net/http"
+ "net/url"
+ "strconv"
+ "sync/atomic"
+)
+
+type Web struct {
+ // 64 bit alignment
+ relogOnNonce uint32
+
+ // The `sessionid` cookie required to use the steam website.
+ // This cookie may contain a characters that will need to be URL-escaped, otherwise
+ // Steam (probably) interprets is as a string.
+ // When used as an URL paramter this is automatically escaped by the Go HTTP package.
+ SessionId string
+ // The `steamLogin` cookie required to use the steam website. Already URL-escaped.
+ // This is only available after calling LogOn().
+ SteamLogin string
+ // The `steamLoginSecure` cookie required to use the steam website over HTTPs. Already URL-escaped.
+ // This is only availbile after calling LogOn().
+ SteamLoginSecure string
+
+ webLoginKey string
+
+ client *Client
+}
+
+func (w *Web) HandlePacket(packet *Packet) {
+ switch packet.EMsg {
+ case EMsg_ClientNewLoginKey:
+ w.handleNewLoginKey(packet)
+ case EMsg_ClientRequestWebAPIAuthenticateUserNonceResponse:
+ w.handleAuthNonceResponse(packet)
+ }
+}
+
+// Fetches the `steamLogin` cookie. This may only be called after the first
+// WebSessionIdEvent or it will panic.
+func (w *Web) LogOn() {
+ if w.webLoginKey == "" {
+ panic("Web: webLoginKey not initialized!")
+ }
+
+ go func() {
+ // retry three times. yes, I know about loops.
+ err := w.apiLogOn()
+ if err != nil {
+ err = w.apiLogOn()
+ if err != nil {
+ err = w.apiLogOn()
+ }
+ }
+ if err != nil {
+ w.client.Emit(WebLogOnErrorEvent(err))
+ return
+ }
+ }()
+}
+
+func (w *Web) apiLogOn() error {
+ sessionKey := make([]byte, 32)
+ rand.Read(sessionKey)
+
+ cryptedSessionKey := cryptoutil.RSAEncrypt(GetPublicKey(EUniverse_Public), sessionKey)
+ ciph, _ := aes.NewCipher(sessionKey)
+ cryptedLoginKey := cryptoutil.SymmetricEncrypt(ciph, []byte(w.webLoginKey))
+ data := make(url.Values)
+ data.Add("format", "json")
+ data.Add("steamid", strconv.FormatUint(w.client.SteamId().ToUint64(), 10))
+ data.Add("sessionkey", string(cryptedSessionKey))
+ data.Add("encrypted_loginkey", string(cryptedLoginKey))
+ resp, err := http.PostForm("https://api.steampowered.com/ISteamUserAuth/AuthenticateUser/v0001", data)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode == 401 {
+ // our web login key has expired, request a new one
+ atomic.StoreUint32(&w.relogOnNonce, 1)
+ w.client.Write(NewClientMsgProtobuf(EMsg_ClientRequestWebAPIAuthenticateUserNonce, new(CMsgClientRequestWebAPIAuthenticateUserNonce)))
+ return nil
+ } else if resp.StatusCode != 200 {
+ return errors.New("steam.Web.apiLogOn: request failed with status " + resp.Status)
+ }
+
+ result := new(struct {
+ Authenticateuser struct {
+ Token string
+ TokenSecure string
+ }
+ })
+ err = json.NewDecoder(resp.Body).Decode(result)
+ if err != nil {
+ return err
+ }
+
+ w.SteamLogin = result.Authenticateuser.Token
+ w.SteamLoginSecure = result.Authenticateuser.TokenSecure
+
+ w.client.Emit(new(WebLoggedOnEvent))
+ return nil
+}
+
+func (w *Web) handleNewLoginKey(packet *Packet) {
+ msg := new(CMsgClientNewLoginKey)
+ packet.ReadProtoMsg(msg)
+
+ w.client.Write(NewClientMsgProtobuf(EMsg_ClientNewLoginKeyAccepted, &CMsgClientNewLoginKeyAccepted{
+ UniqueId: proto.Uint32(msg.GetUniqueId()),
+ }))
+
+ // number -> string -> bytes -> base64
+ w.SessionId = base64.StdEncoding.EncodeToString([]byte(strconv.FormatUint(uint64(msg.GetUniqueId()), 10)))
+
+ w.client.Emit(new(WebSessionIdEvent))
+}
+
+func (w *Web) handleAuthNonceResponse(packet *Packet) {
+ // this has to be the best name for a message yet.
+ msg := new(CMsgClientRequestWebAPIAuthenticateUserNonceResponse)
+ packet.ReadProtoMsg(msg)
+ w.webLoginKey = msg.GetWebapiAuthenticateUserNonce()
+
+ // if the nonce was specifically requested in apiLogOn(),
+ // don't emit an event.
+ if atomic.CompareAndSwapUint32(&w.relogOnNonce, 1, 0) {
+ w.LogOn()
+ }
+}