From f7f8f1ee8fd9ed6c4fc6ca040bb86c5e9039ae89 Mon Sep 17 00:00:00 2001 From: troido Date: Thu, 23 Apr 2020 12:57:36 +0200 Subject: do introduction before starting the client; ask for the password when the name is registered --- asciifarmclient/common/messages.py | 40 ++++++++++++++++++++++++++++++++++- asciifarmclient/connection.py | 2 +- asciifarmclient/gameclient.py | 1 - asciifarmclient/main.py | 43 +++++++++++++++++++++++++++++++++++++- 4 files changed, 82 insertions(+), 4 deletions(-) (limited to 'asciifarmclient') diff --git a/asciifarmclient/common/messages.py b/asciifarmclient/common/messages.py index d3aed9a..df4492b 100644 --- a/asciifarmclient/common/messages.py +++ b/asciifarmclient/common/messages.py @@ -63,11 +63,34 @@ class NameMessage(ClientToServerMessage): if name[0] != "~": for char in name: category = unicodedata.category(char) + print("'{}'".format(name)) assert category in self.categories, InvalidNameError("all name caracters must be in these unicode categories: " + "|".join(self.categories) + " (except for tildenames)") self.name = name def body(self): return self.name + + + +class AuthMessage(ClientToServerMessage): + + typename = "auth" + categories = {"Lu", "Ll", "Lt", "Lm", "Lo", "Nd", "Nl", "No", "Pc"} + + + def __init__(self, name, password): + assert isinstance(name, str), InvalidNameError("name must be a string") + assert (len(name) > 0), InvalidNameError("name needs at least one character") + assert (len(bytes(name, "utf-8")) <= 256), InvalidNameError("name may not be longer than 256 utf8 bytes") + if name[0] != "~": + for char in name: + category = unicodedata.category(char) + assert category in self.categories, InvalidNameError("all name caracters must be in these unicode categories: " + "|".join(self.categories) + " (except for tildenames)") + self.name = name + self.password = password + + def body(self): + return {"name": self.name, "type": "passtoken", "passtoken": self.password} class InputMessage(ClientToServerMessage): @@ -145,6 +168,17 @@ class ErrorMessage(ServerToClientMessage): return [self.typename, self.errType, self.description] +class ConnectedMessage(ServerToClientMessage): + + typename = "connected" + msglen = 2 + + def __init__(self, description=""): + self.description = description + + def to_json(self): + return [self.typename, self.description] + messages = {message.msgType(): message for message in [ NameMessage, @@ -152,6 +186,10 @@ messages = {message.msgType(): message for message in [ ChatMessage, WorldMessage, ErrorMessage, - MessageMessage + MessageMessage, + ConnectedMessage ]} +def message_from_json(msg): + return messages[msg[0]].from_json(msg) + diff --git a/asciifarmclient/connection.py b/asciifarmclient/connection.py index bdcc913..707a02e 100644 --- a/asciifarmclient/connection.py +++ b/asciifarmclient/connection.py @@ -25,7 +25,7 @@ class Connection: return None datastr = databytes.decode('utf-8') msg = json.loads(datastr) - message = messages.messages[msg[0]].from_json(msg) + message = messages.message_from_json(msg) return message def listen(self, callback, onError): diff --git a/asciifarmclient/gameclient.py b/asciifarmclient/gameclient.py index d705b59..9d7bca6 100644 --- a/asciifarmclient/gameclient.py +++ b/asciifarmclient/gameclient.py @@ -44,7 +44,6 @@ class Client: self.log(e.description) def start(self): - self.sendMessage(messages.NameMessage(self.name)) threading.Thread(target=self.listen, daemon=True).start() threading.Thread(target=self.getInput, daemon=True).start() diff --git a/asciifarmclient/main.py b/asciifarmclient/main.py index 8c79fac..09c9b13 100644 --- a/asciifarmclient/main.py +++ b/asciifarmclient/main.py @@ -6,13 +6,16 @@ import sys import termios import tty import signal -#import os +import getpass +import hashlib +import base64 from .connection import Connection from .gameclient import Client from .display import Display from .parseargs import parse_args from ratuil.screen import Screen +from asciifarmclient.common import messages def main(argv=None): @@ -26,6 +29,8 @@ def main(argv=None): print("ERROR: Could not connect to server.\nAre you sure that the server is running and that you're connecting to the right address?", file=sys.stderr) return + if not introduce(connection, name): + return error = None closeMessage = None @@ -63,3 +68,39 @@ def main(argv=None): if closeMessage: print(closeMessage, file=sys.stderr) + +def introduce(connection, name): + connection.send(messages.NameMessage(name)) + print("introducing to server as {}".format(name)) + response = connection.receive() + if response is None: + print("connection lost") + return False + if isinstance(response, messages.ConnectedMessage): + print("connection successful") + return True + if isinstance(response, messages.ErrorMessage): + if response.errType == "registered": + print("'{}' is a registered name. Enter password to login, or restart the client with the -n option to choose a different name".format(name)) + password = getpass.getpass() + m = hashlib.sha256() + m.update(bytes("asciifarm{name}{pw}{name}asciifarm".format(name=name, pw=password), "utf-8")) + passbytes = m.digest() + passtoken = base64.b64encode(passbytes).decode("ascii") + connection.send(messages.AuthMessage(name, passtoken)) + response = connection.receive() + if response is None: + print("connection lost") + return False + if isinstance(response, messages.ConnectedMessage): + print("connection successful") + return True + + print("Connection unsuccessful: {}".format(response.to_json())) + return False + else: + print("Error: {}".format(response.to_json()), file=sys.stderr) + return False + + print("Invalid server response: {}".format(response.to_json()), file=sys.stderr) + return False -- cgit