forked from Philipp15b/go-steam
/
web.go
140 lines (119 loc) · 3.5 KB
/
web.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package steam
import (
"code.google.com/p/goprotobuf/proto"
"crypto/aes"
"crypto/rand"
"encoding/base64"
"encoding/json"
"github.com/macb/go-steam/cryptoutil"
. "github.com/macb/go-steam/internal"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"sync/atomic"
)
type Web struct {
// The `sessionid` cookie required to use the steam website.
WebSessionId string
// The `steamLogin` cookie required to use the steam website.
// It is only available after calling LogOn().
SteamLogin string
webLoginKey string
relogOnNonce uint32
client *Client
}
func (w *Web) HandlePacket(packet *PacketMsg) {
switch packet.EMsg {
case EMsg_ClientNewLoginKey:
w.handleNewLoginKey(packet)
case EMsg_ClientRequestWebAPIAuthenticateUserNonceResponse:
w.handleAuthNonceResponse(packet)
}
}
type WebLoggedOnEvent struct{}
// 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("SteamWeb: 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.Errorf("web: Error logging on: %v", 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.WebSessionId))
data := make(url.Values)
data.Add("format", "json")
data.Add("steamid", strconv.FormatUint(uint64(w.client.SteamId()), 10))
data.Add("sessionkey", string(cryptedSessionKey))
data.Add("encrypted_loginkey", string(cryptedLoginKey))
resp, err := http.PostForm("http://api.steampowered.com/ISteamUserAuth/AuthenticateUser/v0001", data)
if err != nil {
return err
}
if resp.StatusCode != 200 {
// our web session id has expired, request a new one
w.client.Write(NewClientMsgProtobuf(EMsg_ClientRequestWebAPIAuthenticateUserNonce, new(CMsgClientRequestWebAPIAuthenticateUserNonce)))
atomic.StoreUint32(&w.relogOnNonce, 1)
return nil
}
result := new(struct {
Authenticateuser struct {
Token string
}
})
b, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return err
}
err = json.Unmarshal(b, result)
if err != nil {
return err
}
w.SteamLogin = result.Authenticateuser.Token
w.client.Emit(new(WebLoggedOnEvent))
return nil
}
type WebSessionIdEvent struct{}
func (w *Web) handleNewLoginKey(packet *PacketMsg) {
msg := new(CMsgClientNewLoginKey)
packet.ReadProtoMsg(msg)
w.client.Write(NewClientMsgProtobuf(EMsg_ClientNewLoginKeyAccepted, &CMsgClientNewLoginKeyAccepted{
UniqueId: proto.Uint32(msg.GetUniqueId()),
}))
w.webLoginKey = msg.GetLoginKey()
// number -> string -> bytes -> base64
w.WebSessionId = base64.StdEncoding.EncodeToString([]byte(strconv.FormatUint(uint64(msg.GetUniqueId()), 10)))
w.client.Emit(new(WebSessionIdEvent))
}
func (w *Web) handleAuthNonceResponse(packet *PacketMsg) {
// this has to be the best name for a message yet.
msg := new(CMsgClientRequestWebAPIAuthenticateUserNonceResponse)
packet.ReadProtoMsg(msg)
w.WebSessionId = msg.GetWebapiAuthenticateUserNonce()
// if the nonce was specifically requested in apiLogOn(),
// don't emit an event.
if atomic.CompareAndSwapUint32(&w.relogOnNonce, 1, 0) {
w.LogOn()
} else {
w.client.Emit(new(WebSessionIdEvent))
}
}