diff --git a/steam/client/__init__.py b/steam/client/__init__.py index ecc60ea..f1ac480 100644 --- a/steam/client/__init__.py +++ b/steam/client/__init__.py @@ -1,5 +1,7 @@ +import os import logging import gevent +from Crypto.Hash import SHA from eventemitter import EventEmitter from steam.enums.emsg import EMsg from steam.enums import EResult, EOSType @@ -13,6 +15,8 @@ logger = logging.getLogger("SteamClient") class SteamClient(EventEmitter, FeatureBase): current_jobid = 0 + credential_location = None + username = None def __init__(self): self.cm = CMClient() @@ -20,6 +24,7 @@ class SteamClient(EventEmitter, FeatureBase): # register listners self.cm.on(None, self._handle_cm_events) self.on(EMsg.ClientLogOnResponse, self._handle_logon) + self.on(EMsg.ClientUpdateMachineAuth, self._handle_update_machine_auth) self.on("disconnected", self._handle_disconnect) self.logged_on = False @@ -36,11 +41,14 @@ class SteamClient(EventEmitter, FeatureBase): logger.debug("Emit event: %s" % repr(event)) super(SteamClient, self).emit(event, *args) + def set_credential_location(self, path): + self.credential_location = path + @property def steam_id(self): return self.cm.steam_id - @property + @property def connected(self): return self.cm.connected @@ -65,6 +73,7 @@ class SteamClient(EventEmitter, FeatureBase): self.emit("job_%d" % jobid, *args) def _handle_disconnect(self): + self.username = None self.logged_on = False self.current_jobid = 0 @@ -80,13 +89,30 @@ class SteamClient(EventEmitter, FeatureBase): self.disconnect() if result in (EResult.AccountLogonDenied, - EResult.AccountLoginDeniedNeedTwoFactor, - EResult.TwoFactorCodeMismatch, - ): + EResult.AccountLoginDeniedNeedTwoFactor, + EResult.TwoFactorCodeMismatch, + ): self.emit("auth_code_required", result) else: self.emit("error", result) + def _handle_update_machine_auth(self, message): + ok = self.store_sentry(self.username, message.body.bytes) + + if ok: + resp = MsgProto(EMsg.ClientUpdateMachineAuthResponse) + + resp.header.jobid_target = message.header.jobid_source + + resp.body.filename = message.body.filename + resp.body.eresult = EResult.OK + resp.body.sha_file = SHA.new(message.body.bytes).digest() + resp.body.getlasterror = 0 + resp.body.offset = message.body.offset + resp.body.cubwrote = message.body.cubtowrite + + self.send(resp) + def send(self, message): if not self.connected: raise RuntimeError("Cannot send message while not connected") @@ -116,21 +142,44 @@ class SteamClient(EventEmitter, FeatureBase): if not self.cm.channel_secured: self.wait_event("channel_secured") - def anonymous_login(self): - logger.debug("Attempting Anonymous login") - - self._pre_login() - - message = MsgProto(EMsg.ClientLogon) - message.header.steamid = SteamID(type='AnonUser', universe='Public') - message.body.protocol_version = 65575 - self.send(message) + def _get_sentry_path(self, username): + if self.credential_location is not None: + return os.path.join(self.credential_location, + "%s_sentry.bin" % username + ) + return None + + def get_sentry(self, username): + filepath = self._get_sentry_path(username) + + if filepath and os.path.isfile(filepath): + try: + with open(filepath, 'r') as f: + return f.read() + except IOError as e: + logger.error("get_sentry: %s" % str(e)) + + return None + + def store_sentry(self, username, sentry_bytes): + filepath = self._get_sentry_path(username) + if filepath: + try: + with open(filepath, 'w') as f: + f.write(sentry_bytes) + return True + except IOError as e: + logger.error("store_sentry: %s" % str(e)) + + return False def login(self, username, password, auth_code=None, two_factor_code=None, remember=False): logger.debug("Attempting login") self._pre_login() + self.username = username + message = MsgProto(EMsg.ClientLogon) message.header.steamid = SteamID(type='Individual', universe='Public') message.body.protocol_version = 65575 @@ -141,6 +190,13 @@ class SteamClient(EventEmitter, FeatureBase): message.body.account_name = username message.body.password = password + sentry = self.get_sentry(username) + if sentry is None: + message.body.eresult_sentryfile = EResult.FileNotFound + else: + message.body.eresult_sentryfile = EResult.OK + message.body.sha_sentryfile = SHA.new(sentry).digest() + if auth_code: message.body.auth_code = auth_code if two_factor_code: @@ -148,6 +204,16 @@ class SteamClient(EventEmitter, FeatureBase): self.send(message) + def anonymous_login(self): + logger.debug("Attempting Anonymous login") + + self._pre_login() + + message = MsgProto(EMsg.ClientLogon) + message.header.steamid = SteamID(type='AnonUser', universe='Public') + message.body.protocol_version = 65575 + self.send(message) + def logout(self): if self.logged_on: self.logged_on = False