def _handle_logon(self, msg): CMClient._handle_logon(self, msg) result = EResult(msg.body.eresult) if result == EResult.OK: self._reconnect_backoff_c = 0 self.logged_on = True self.set_persona(EPersonaState.Online) self.emit("logged_on") return # CM kills the connection on error anyway self.disconnect() if result in ( EResult.AccountLogonDenied, EResult.InvalidLoginAuthCode, EResult.AccountLoginDeniedNeedTwoFactor, EResult.TwoFactorCodeMismatch, ): is_2fa = (result in ( EResult.AccountLoginDeniedNeedTwoFactor, EResult.TwoFactorCodeMismatch, )) if is_2fa: code_mismatch = (result == EResult.TwoFactorCodeMismatch) else: code_mismatch = (result == EResult.InvalidLoginAuthCode) self.emit("auth_code_required", is_2fa, code_mismatch)
def _handle_cm_list(self, msg): if (self.cm_servers.last_updated + 3600*24 > time() and self.cm_servers.cell_id != 0): return CMClient._handle_cm_list(self, msg) # clear and merge if self.credential_location: filepath = os.path.join(self.credential_location, 'cm_servers.json') if os.path.exists(filepath): try: with open(filepath, 'r') as f: data = json.load(f) except ValueError: self._LOG.error("Failed parsing %s", repr(filepath)) except IOError as e: self._LOG.error("Failed reading %s (%s)", repr(filepath), str(e)) else: if data.get('last_updated', 0) + 3600*24 > time(): return self._LOG.debug("Persisted CM server list is stale") data = { 'cell_id': self.cm_servers.cell_id, 'last_updated': self.cm_servers.last_updated, 'servers': list(zip(map(ip_from_int, msg.body.cm_addresses), msg.body.cm_ports)), } try: with open(filepath, 'wb') as f: f.write(json.dumps(data, indent=True).encode('ascii')) self._LOG.debug("Saved CM servers to %s" % repr(filepath)) except IOError as e: self._LOG.error("saving %s: %s" % (filepath, str(e)))
def _handle_cm_list(self, msg): if self._cm_servers_timestamp is None: self._cm_servers_timestamp = int(time()) self.cm_servers.clear() CMClient._handle_cm_list(self, msg) # just merges the list if self.credential_location: filepath = os.path.join(self.credential_location, 'cm_servers.json') if not os.path.exists(filepath) or time() - ( 3600 * 24) > self._cm_servers_timestamp: data = { 'timestamp': self._cm_servers_timestamp, 'servers': list( zip(map(ip_from_int, msg.body.cm_addresses), msg.body.cm_ports)), } try: with open(filepath, 'wb') as f: f.write(json.dumps(data, indent=True).encode('ascii')) self._LOG.debug("Saved CM servers to %s" % repr(filepath)) except IOError as e: self._LOG.error("saving %s: %s" % (filepath, str(e)))
def _handle_logon(self, msg): CMClient._handle_logon(self, msg) result = EResult(msg.body.eresult) if result == EResult.OK: self._reconnect_backoff_c = 0 self.logged_on = True self.emit(self.EVENT_LOGGED_ON) return # CM kills the connection on error anyway self.disconnect() if result == EResult.InvalidPassword: self.login_key = None if result in (EResult.AccountLogonDenied, EResult.InvalidLoginAuthCode, EResult.AccountLoginDeniedNeedTwoFactor, EResult.TwoFactorCodeMismatch, ): is_2fa = (result in (EResult.AccountLoginDeniedNeedTwoFactor, EResult.TwoFactorCodeMismatch, )) if is_2fa: code_mismatch = (result == EResult.TwoFactorCodeMismatch) else: code_mismatch = (result == EResult.InvalidLoginAuthCode) self.emit(self.EVENT_AUTH_CODE_REQUIRED, is_2fa, code_mismatch)
def test_connect_auto_discovery_success(self, mock_recv, mock_emit): # setup self.conn.connect.return_value = True self.server_list.__len__.return_value = 0 def fake_servers(*args, **kwargs): self.server_list.__len__.return_value = 10 return True self.server_list.bootstrap_from_webapi.side_effect = fake_servers # run cm = CMClient() with gevent.Timeout(3, False): cm.connect(retry=1) gevent.idle() # verify self.server_list.bootstrap_from_webapi.assert_called_once_with() self.server_list.bootstrap_from_dns.assert_not_called() self.conn.connect.assert_called_once_with((127001, 20000)) mock_emit.assert_called_once_with('connected') mock_recv.assert_called_once_with()
def _handle_logon(self, msg): CMClient._handle_logon(self, msg) result = EResult(msg.body.eresult) if result == EResult.OK: self._reconnect_backoff_c = 0 self.logged_on = True self.emit(self.EVENT_LOGGED_ON) return # CM kills the connection on error anyway self.disconnect() if result == EResult.InvalidPassword: self.login_key = None if result in ( EResult.AccountLogonDenied, EResult.InvalidLoginAuthCode, EResult.AccountLoginDeniedNeedTwoFactor, EResult.TwoFactorCodeMismatch, ): is_2fa = (result in ( EResult.AccountLoginDeniedNeedTwoFactor, EResult.TwoFactorCodeMismatch, )) if is_2fa: code_mismatch = (result == EResult.TwoFactorCodeMismatch) else: code_mismatch = (result == EResult.InvalidLoginAuthCode) self.emit(self.EVENT_AUTH_CODE_REQUIRED, is_2fa, code_mismatch)
def test_channel_encrypt_sequence(self): # setup self.conn.connect.return_value = True # run ------------ cm = CMClient() cm.connected = True gevent.spawn(cm._recv_messages) # recieve ChannelEncryptRequest self.conn_in.put( b'\x17\x05\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x00\x00\x00\x01\x00\x00\x00' ) gevent.idle() gevent.idle() gevent.idle() gevent.idle() self.conn.put_message.assert_called_once_with( b'\x18\x05\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x00\x00\x00\x80\x00\x00\x00PUBKEY ENCRYPTED SESSION KEY\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00h-\xc4@\x00\x00\x00\x00' ) # recieve ChannelEncryptResult (OK) self.conn_in.put( b'\x19\x05\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x00\x00\x00' ) cm.wait_event('channel_secured', timeout=2, raises=True)
def send(self, message): """ Send a message to CM :param message: a message instance :type message: :class:`steam.core.msg.Msg`, :class:`steam.core.msg.MsgProto` """ if not self.connected: raise RuntimeError("Cannot send message while not connected") CMClient.send(self, message)
def __init__(self): CMClient.__init__(self) # register listners self.on(self.EVENT_DISCONNECTED, self._handle_disconnect) self.on(self.EVENT_RECONNECT, self._handle_disconnect) self.on(EMsg.ClientNewLoginKey, self._handle_login_key) self.on(EMsg.ClientUpdateMachineAuth, self._handle_update_machine_auth) #: indicates logged on status. Listen to ``logged_on`` when change to ``True`` self.logged_on = False BuiltinBase.__init__(self)
def __init__(self): CMClient.__init__(self) self._LOG = logging.getLogger("SteamClient") # register listners self.on(None, self._handle_jobs) self.on(self.EVENT_DISCONNECTED, self._handle_disconnect) self.on(self.EVENT_RECONNECT, self._handle_disconnect) self.on(EMsg.ClientNewLoginKey, self._handle_login_key) self.on(EMsg.ClientUpdateMachineAuth, self._handle_update_machine_auth) #: indicates logged on status. Listen to ``logged_on`` when change to ``True`` self.logged_on = False BuiltinBase.__init__(self)
def send(self, message, body_params=None): """Send a message to CM :param message: a message instance :type message: :class:`.Msg`, :class:`.MsgProto` :param body_params: a dict with params to the body (only :class:`.MsgProto`) :type body_params: dict """ if not self.connected: self._LOG.debug("Trying to send message when not connected. (discarded)") else: if body_params and isinstance(message, MsgProto): proto_fill_from_dict(message.body, body_params) CMClient.send(self, message)
def __init__(self): CMClient.__init__(self) self._LOG = logging.getLogger("SteamClient") # register listners self.on(None, self._handle_jobs) self.on("disconnected", self._handle_disconnect) self.on("reconnect", self._handle_disconnect) self.on(EMsg.ClientNewLoginKey, self._handle_login_key) self.on(EMsg.ClientUpdateMachineAuth, self._handle_update_machine_auth) #: indicates logged on status. Listen to ``logged_on`` when change to ``True`` self.logged_on = False BuiltinBase.__init__(self)
def test_connect(self, mock_recv, mock_emit): # setup self.conn.connect.return_value = True # run cm = CMClient() with gevent.Timeout(2, False): cm.connect() gevent.idle() # verify self.conn.connect.assert_called_once_with((127001, 1)) mock_emit.assert_called_once_with('connected') mock_recv.assert_called_once_with()
def send(self, message, body_params=None): """.. versionchanged:: 0.8.4 Send a message to CM :param message: a message instance :type message: :class:`.Msg`, :class:`.MsgProto` :param body_params: a dict with params to the body (only :class:`.MsgProto`) :type body_params: dict """ if not self.connected: self._LOG.debug("Trying to send message when not connected. (discarded)") else: if body_params and isinstance(message, MsgProto): proto_fill_from_dict(message.body, body_params) CMClient.send(self, message)
def _parse_message(self, message): result = CMClient._parse_message(self, message) if result is None: return emsg, msg = result # emit job events if msg.proto: jobid = msg.header.jobid_target else: jobid = msg.header.targetJobID if jobid not in (-1, 18446744073709551615): jobid = "job_%d" % jobid if msg.body is None and self.count_listeners(jobid): msg.parse() self.emit(jobid, msg) # emit UMs if emsg in (EMsg.ServiceMethod, EMsg.ServiceMethodResponse, EMsg.ServiceMethodSendToClient): if msg.body is None and self.count_listeners( msg.header.target_job_name): msg.parse() self.emit(msg.header.target_job_name, msg)
def test_connect(self, mock_recv, mock_emit): # setup self.conn.connect.return_value = True self.server_list.__len__.return_value = 10 # run cm = CMClient() with gevent.Timeout(2, False): cm.connect(retry=1) gevent.idle() # verify self.conn.connect.assert_called_once_with((127001, 20000)) mock_emit.assert_called_once_with('connected') mock_recv.assert_called_once_with()
def test_connect_auto_discovery_failing(self, mock_recv, mock_emit): # setup self.conn.connect.return_value = True self.server_list.__len__.return_value = 0 # run cm = CMClient() with gevent.Timeout(3, False): cm.connect(retry=1) gevent.idle() # verify self.server_list.bootstrap_from_webapi.assert_called_once_with() self.server_list.bootstrap_from_dns.assert_called_once_with() self.conn.connect.assert_not_called()
def test_channel_encrypt_sequence(self): # setup self.conn.connect.return_value = True # run ------------ cm = CMClient() cm.connected = True gevent.spawn(cm._recv_messages) # recieve ChannelEncryptRequest self.conn_in.put(b'\x17\x05\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x00\x00\x00\x01\x00\x00\x00') gevent.idle(); gevent.idle(); gevent.idle(); gevent.idle() self.conn.put_message.assert_called_once_with(b'\x18\x05\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x00\x00\x00\x80\x00\x00\x00PUBKEY ENCRYPTED SESSION KEY\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00h-\xc4@\x00\x00\x00\x00') # recieve ChannelEncryptResult (OK) self.conn_in.put(b'\x19\x05\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x00\x00\x00') cm.wait_event('channel_secured', timeout=2, raises=True)
def _handle_cm_list(self, msg): if self._cm_servers_timestamp is None: self._cm_servers_timestamp = int(time()) self.cm_servers.clear() CMClient._handle_cm_list(self, msg) # just merges the list if self.credential_location: filepath = os.path.join(self.credential_location, 'cm_servers.json') if not os.path.exists(filepath) or time() - (3600*24) > self._cm_servers_timestamp: data = { 'timestamp': self._cm_servers_timestamp, 'servers': list(zip(map(ip_from_int, msg.body.cm_addresses), msg.body.cm_ports)), } try: with open(filepath, 'wb') as f: f.write(json.dumps(data, indent=True).encode('ascii')) self._LOG.debug("Saved CM servers to %s" % repr(filepath)) except IOError as e: self._LOG.error("saving %s: %s" % (filepath, str(e)))
def disconnect(self, *args, **kwargs): """Close connection, see :meth:`.CMClient.disconnect`""" self.logged_on = False CMClient.disconnect(self, *args, **kwargs)
def connect(self, *args, **kwargs): """Attempt to establish connection, see :meth:`.CMClient.connect`""" self._bootstrap_cm_list_from_file() return CMClient.connect(self, *args, **kwargs)
def connect(self, *args, **kwargs): """Attempt to establish connection, see :meth:`.CMClient.connect`""" self._bootstrap_cm_list_from_file() CMClient.connect(self, *args, **kwargs)