diff options
Diffstat (limited to 'vendor/github.com/Philipp15b/go-steam/web.go')
-rw-r--r-- | vendor/github.com/Philipp15b/go-steam/web.go | 143 |
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() + } +} |