204 lines
6.6 KiB
Go
204 lines
6.6 KiB
Go
package steam
|
|
|
|
import (
|
|
"crypto/sha1"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
. "github.com/Philipp15b/go-steam/protocol"
|
|
. "github.com/Philipp15b/go-steam/protocol/protobuf"
|
|
. "github.com/Philipp15b/go-steam/protocol/steamlang"
|
|
. "github.com/Philipp15b/go-steam/steamid"
|
|
"github.com/golang/protobuf/proto"
|
|
)
|
|
|
|
type Auth struct {
|
|
client *Client
|
|
details *LogOnDetails
|
|
}
|
|
|
|
type SentryHash []byte
|
|
|
|
type LogOnDetails struct {
|
|
Username string
|
|
|
|
// If logging into an account without a login key, the account's password.
|
|
Password string
|
|
|
|
// If you have a Steam Guard email code, you can provide it here.
|
|
AuthCode string
|
|
|
|
// If you have a Steam Guard mobile two-factor authentication code, you can provide it here.
|
|
TwoFactorCode string
|
|
SentryFileHash SentryHash
|
|
LoginKey string
|
|
|
|
// true if you want to get a login key which can be used in lieu of
|
|
// a password for subsequent logins. false or omitted otherwise.
|
|
ShouldRememberPassword bool
|
|
}
|
|
|
|
// Log on with the given details. You must always specify username and
|
|
// password OR username and loginkey. For the first login, don't set an authcode or a hash and you'll
|
|
// receive an error (EResult_AccountLogonDenied)
|
|
// and Steam will send you an authcode. Then you have to login again, this time with the authcode.
|
|
// Shortly after logging in, you'll receive a MachineAuthUpdateEvent with a hash which allows
|
|
// you to login without using an authcode in the future.
|
|
//
|
|
// If you don't use Steam Guard, username and password are enough.
|
|
//
|
|
// After the event EMsg_ClientNewLoginKey is received you can use the LoginKey
|
|
// to login instead of using the password.
|
|
func (a *Auth) LogOn(details *LogOnDetails) {
|
|
if details.Username == "" {
|
|
panic("Username must be set!")
|
|
}
|
|
if details.Password == "" && details.LoginKey == "" {
|
|
panic("Password or LoginKey must be set!")
|
|
}
|
|
|
|
logon := new(CMsgClientLogon)
|
|
logon.AccountName = &details.Username
|
|
logon.Password = &details.Password
|
|
if details.AuthCode != "" {
|
|
logon.AuthCode = proto.String(details.AuthCode)
|
|
}
|
|
if details.TwoFactorCode != "" {
|
|
logon.TwoFactorCode = proto.String(details.TwoFactorCode)
|
|
}
|
|
logon.ClientLanguage = proto.String("english")
|
|
logon.ProtocolVersion = proto.Uint32(MsgClientLogon_CurrentProtocol)
|
|
logon.ShaSentryfile = details.SentryFileHash
|
|
if details.LoginKey != "" {
|
|
logon.LoginKey = proto.String(details.LoginKey)
|
|
}
|
|
if details.ShouldRememberPassword {
|
|
logon.ShouldRememberPassword = proto.Bool(details.ShouldRememberPassword)
|
|
}
|
|
|
|
atomic.StoreUint64(&a.client.steamId, uint64(NewIdAdv(0, 1, int32(EUniverse_Public), int32(EAccountType_Individual))))
|
|
|
|
a.client.Write(NewClientMsgProtobuf(EMsg_ClientLogon, logon))
|
|
}
|
|
|
|
func (a *Auth) HandlePacket(packet *Packet) {
|
|
switch packet.EMsg {
|
|
case EMsg_ClientLogOnResponse:
|
|
a.handleLogOnResponse(packet)
|
|
case EMsg_ClientNewLoginKey:
|
|
a.handleLoginKey(packet)
|
|
case EMsg_ClientSessionToken:
|
|
case EMsg_ClientLoggedOff:
|
|
a.handleLoggedOff(packet)
|
|
case EMsg_ClientUpdateMachineAuth:
|
|
a.handleUpdateMachineAuth(packet)
|
|
case EMsg_ClientAccountInfo:
|
|
a.handleAccountInfo(packet)
|
|
case EMsg_ClientWalletInfoUpdate:
|
|
case EMsg_ClientRequestWebAPIAuthenticateUserNonceResponse:
|
|
case EMsg_ClientMarketingMessageUpdate:
|
|
}
|
|
}
|
|
|
|
func (a *Auth) handleLogOnResponse(packet *Packet) {
|
|
if !packet.IsProto {
|
|
a.client.Fatalf("Got non-proto logon response!")
|
|
return
|
|
}
|
|
|
|
body := new(CMsgClientLogonResponse)
|
|
msg := packet.ReadProtoMsg(body)
|
|
|
|
result := EResult(body.GetEresult())
|
|
if result == EResult_OK {
|
|
atomic.StoreInt32(&a.client.sessionId, msg.Header.Proto.GetClientSessionid())
|
|
atomic.StoreUint64(&a.client.steamId, msg.Header.Proto.GetSteamid())
|
|
a.client.Web.webLoginKey = *body.WebapiAuthenticateUserNonce
|
|
|
|
go a.client.heartbeatLoop(time.Duration(body.GetOutOfGameHeartbeatSeconds()))
|
|
|
|
a.client.Emit(&LoggedOnEvent{
|
|
Result: EResult(body.GetEresult()),
|
|
ExtendedResult: EResult(body.GetEresultExtended()),
|
|
OutOfGameSecsPerHeartbeat: body.GetOutOfGameHeartbeatSeconds(),
|
|
InGameSecsPerHeartbeat: body.GetInGameHeartbeatSeconds(),
|
|
PublicIp: body.GetPublicIp(),
|
|
ServerTime: body.GetRtime32ServerTime(),
|
|
AccountFlags: EAccountFlags(body.GetAccountFlags()),
|
|
ClientSteamId: SteamId(body.GetClientSuppliedSteamid()),
|
|
EmailDomain: body.GetEmailDomain(),
|
|
CellId: body.GetCellId(),
|
|
CellIdPingThreshold: body.GetCellIdPingThreshold(),
|
|
Steam2Ticket: body.GetSteam2Ticket(),
|
|
UsePics: body.GetUsePics(),
|
|
WebApiUserNonce: body.GetWebapiAuthenticateUserNonce(),
|
|
IpCountryCode: body.GetIpCountryCode(),
|
|
VanityUrl: body.GetVanityUrl(),
|
|
NumLoginFailuresToMigrate: body.GetCountLoginfailuresToMigrate(),
|
|
NumDisconnectsToMigrate: body.GetCountDisconnectsToMigrate(),
|
|
})
|
|
} else if result == EResult_Fail || result == EResult_ServiceUnavailable || result == EResult_TryAnotherCM {
|
|
// some error on Steam's side, we'll get an EOF later
|
|
} else {
|
|
a.client.Emit(&LogOnFailedEvent{
|
|
Result: EResult(body.GetEresult()),
|
|
})
|
|
a.client.Disconnect()
|
|
}
|
|
}
|
|
|
|
func (a *Auth) handleLoginKey(packet *Packet) {
|
|
body := new(CMsgClientNewLoginKey)
|
|
packet.ReadProtoMsg(body)
|
|
a.client.Write(NewClientMsgProtobuf(EMsg_ClientNewLoginKeyAccepted, &CMsgClientNewLoginKeyAccepted{
|
|
UniqueId: proto.Uint32(body.GetUniqueId()),
|
|
}))
|
|
a.client.Emit(&LoginKeyEvent{
|
|
UniqueId: body.GetUniqueId(),
|
|
LoginKey: body.GetLoginKey(),
|
|
})
|
|
}
|
|
|
|
func (a *Auth) handleLoggedOff(packet *Packet) {
|
|
result := EResult_Invalid
|
|
if packet.IsProto {
|
|
body := new(CMsgClientLoggedOff)
|
|
packet.ReadProtoMsg(body)
|
|
result = EResult(body.GetEresult())
|
|
} else {
|
|
body := new(MsgClientLoggedOff)
|
|
packet.ReadClientMsg(body)
|
|
result = body.Result
|
|
}
|
|
a.client.Emit(&LoggedOffEvent{Result: result})
|
|
}
|
|
|
|
func (a *Auth) handleUpdateMachineAuth(packet *Packet) {
|
|
body := new(CMsgClientUpdateMachineAuth)
|
|
packet.ReadProtoMsg(body)
|
|
hash := sha1.New()
|
|
hash.Write(packet.Data)
|
|
sha := hash.Sum(nil)
|
|
|
|
msg := NewClientMsgProtobuf(EMsg_ClientUpdateMachineAuthResponse, &CMsgClientUpdateMachineAuthResponse{
|
|
ShaFile: sha,
|
|
})
|
|
msg.SetTargetJobId(packet.SourceJobId)
|
|
a.client.Write(msg)
|
|
|
|
a.client.Emit(&MachineAuthUpdateEvent{sha})
|
|
}
|
|
|
|
func (a *Auth) handleAccountInfo(packet *Packet) {
|
|
body := new(CMsgClientAccountInfo)
|
|
packet.ReadProtoMsg(body)
|
|
a.client.Emit(&AccountInfoEvent{
|
|
PersonaName: body.GetPersonaName(),
|
|
Country: body.GetIpCountry(),
|
|
CountAuthedComputers: body.GetCountAuthedComputers(),
|
|
AccountFlags: EAccountFlags(body.GetAccountFlags()),
|
|
FacebookId: body.GetFacebookId(),
|
|
FacebookName: body.GetFacebookName(),
|
|
})
|
|
}
|