diff --git a/src/fr/univ/lyon1/client/Client.java b/src/fr/univ/lyon1/client/Client.java index 33afdfe..abb6001 100644 --- a/src/fr/univ/lyon1/client/Client.java +++ b/src/fr/univ/lyon1/client/Client.java @@ -1,51 +1,39 @@ package fr.univ.lyon1.client; +import fr.univ.lyon1.common.Channel; import fr.univ.lyon1.common.Message; -import fr.univ.lyon1.common.User; +import fr.univ.lyon1.common.command.Command; +import fr.univ.lyon1.common.command.CommandType; +import fr.univ.lyon1.common.exception.ChatException; +import fr.univ.lyon1.common.exception.UnknownCommand; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.Socket; +import java.util.ArrayList; import java.util.List; public class Client { private final int port; private final String address; + private final String username; + private final String password; protected final Socket socket; protected final ObjectOutputStream out; private ObjectInputStream in; + private List channels = new ArrayList<>(); protected boolean started = false; - public Client(String address, int port, String uuid, String password) throws Exception { + + public Client(String address, int port, String username, String password) throws Exception { this.address = address; this.port = port; + this.username = username; + this.password = password; socket = new Socket(address, port); out = new ObjectOutputStream(socket.getOutputStream()); - while (!this.auth(uuid, password)); - out.writeObject("listUsers"); - out.flush(); - out.writeObject("join general"); - out.flush(); - } - - public boolean auth(String uuid, String password) throws IOException { getIn(); - - out.writeUTF(uuid); - out.flush(); - out.writeUTF(password); - out.flush(); - - String response = in.readUTF(); - System.out.println(response); - - if (response.startsWith("err:")) - return false; - else if (response.equals("logged")) - return true; - else - throw new IOException("Uk message"); } public void disconnectedServer() throws IOException { @@ -60,7 +48,7 @@ public class Client { public String sendMessage(String content) { try { - out.writeObject(new Message(content)); + out.writeObject(new Command(CommandType.message, List.of(new Message(content, channels.get(0))))); out.flush(); } catch (IOException e) { System.err.println("Fail to send message !"); @@ -70,26 +58,51 @@ public class Client { return content; } - public Message messageReceived(Message msg) { - System.out.println(); - System.out.println(msg); - return msg; + public void action(Object data) throws IOException { + if (data instanceof Command) + command((Command) data); + else if (data instanceof ChatException) + ((ChatException) data).printStackTrace(); + else { + out.writeObject(new UnknownCommand()); + out.flush(); + } } - public void action(Object data) { - if (data instanceof Message) - messageReceived((Message) data); - else if (data instanceof List) { - List tmpList = (List) data; - if (tmpList.get(0) instanceof User) { - List users = (List) data; - for (User u : users) { - System.out.println(u); - } - } + private void command(Command cmd) throws IOException { + switch (cmd.getType()) { + case login -> commandLogin(); + case message -> commandMessage(cmd); + case list -> commandList(cmd); + case join -> commandJoin(cmd); } } + private void commandLogin() throws IOException { + out.writeObject(new Command(CommandType.list, null)); + out.flush(); + out.writeObject(new Command(CommandType.join, List.of("general"))); + out.flush(); + } + + protected void commandMessage(Command cmd) { + System.out.println(); + System.out.println(cmd.getArgs().get(0)); + } + + private void commandList(Command cmd) { + List users = cmd.getArgs(); + for (Object u : users) { + System.out.println(u); + } + } + + private void commandJoin(Command cmd) { + Channel chan = (Channel) cmd.getArgs().get(0); + channels.add(chan); + System.out.println("You join "+chan); + } + public void run() throws InterruptedException, IOException { if (started) return; @@ -102,6 +115,9 @@ public class Client { started = true; + out.writeObject(new Command(CommandType.login, List.of(username, password))); + out.flush(); + clientSendThread.join(); socket.close(); clientReceiveThread.interrupt(); diff --git a/src/fr/univ/lyon1/client/ClientReceive.java b/src/fr/univ/lyon1/client/ClientReceive.java index f71a9f1..4f5aa9d 100644 --- a/src/fr/univ/lyon1/client/ClientReceive.java +++ b/src/fr/univ/lyon1/client/ClientReceive.java @@ -46,7 +46,11 @@ public class ClientReceive implements Runnable { if (data == null) break; - this.client.action(data); + try { + this.client.action(data); + } catch (IOException e) { + e.printStackTrace(); + } } try { diff --git a/src/fr/univ/lyon1/common/Message.java b/src/fr/univ/lyon1/common/Message.java index f179891..29d6786 100644 --- a/src/fr/univ/lyon1/common/Message.java +++ b/src/fr/univ/lyon1/common/Message.java @@ -24,9 +24,10 @@ public class Message implements Serializable { this.content = content; } - public Message(String content) { + public Message(String content, Channel channel) { this.uuid = UUID.randomUUID(); this.content = content; + this.channel = channel; } public Message repley(User user, String content) { diff --git a/src/fr/univ/lyon1/common/command/Command.java b/src/fr/univ/lyon1/common/command/Command.java new file mode 100644 index 0000000..d3e5ea6 --- /dev/null +++ b/src/fr/univ/lyon1/common/command/Command.java @@ -0,0 +1,23 @@ +package fr.univ.lyon1.common.command; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +public class Command implements Serializable { + private final CommandType type; + private final List args; + + public Command(CommandType type, List args) { + this.type = type; + this.args = args; + } + + public CommandType getType() { + return type; + } + + public List getArgs() { + return new ArrayList<>(args); + } +} diff --git a/src/fr/univ/lyon1/common/command/CommandType.java b/src/fr/univ/lyon1/common/command/CommandType.java new file mode 100644 index 0000000..6e36acc --- /dev/null +++ b/src/fr/univ/lyon1/common/command/CommandType.java @@ -0,0 +1,27 @@ +package fr.univ.lyon1.common.command; + +import java.io.Serializable; + +public enum CommandType implements Serializable { + login("login", "Login to the server"), + message("message", "Send a message"), + join("join", "Join a channel"), + leave("leave", "Leave a channel"), + list("list", "List all users"), + listChannels("listChannels", "List all channels"); + + private final String name; + private final String description; + CommandType(String name, String description) { + this.name = name; + this.description = description; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } +} diff --git a/src/fr/univ/lyon1/common/exception/ChatException.java b/src/fr/univ/lyon1/common/exception/ChatException.java new file mode 100644 index 0000000..7749e46 --- /dev/null +++ b/src/fr/univ/lyon1/common/exception/ChatException.java @@ -0,0 +1,7 @@ +package fr.univ.lyon1.common.exception; + +public class ChatException extends Exception { + public ChatException(String message) { + super(message); + } +} diff --git a/src/fr/univ/lyon1/common/exception/LoginInvalid.java b/src/fr/univ/lyon1/common/exception/LoginInvalid.java new file mode 100644 index 0000000..b412ec0 --- /dev/null +++ b/src/fr/univ/lyon1/common/exception/LoginInvalid.java @@ -0,0 +1,7 @@ +package fr.univ.lyon1.common.exception; + +public class LoginInvalid extends ChatException { + public LoginInvalid(String message) { + super(message); + } +} diff --git a/src/fr/univ/lyon1/common/exception/LoginRequired.java b/src/fr/univ/lyon1/common/exception/LoginRequired.java new file mode 100644 index 0000000..72060ec --- /dev/null +++ b/src/fr/univ/lyon1/common/exception/LoginRequired.java @@ -0,0 +1,7 @@ +package fr.univ.lyon1.common.exception; + +public class LoginRequired extends ChatException { + public LoginRequired() { + super("Login required"); + } +} diff --git a/src/fr/univ/lyon1/common/exception/UnknownCommand.java b/src/fr/univ/lyon1/common/exception/UnknownCommand.java new file mode 100644 index 0000000..32a7c29 --- /dev/null +++ b/src/fr/univ/lyon1/common/exception/UnknownCommand.java @@ -0,0 +1,9 @@ +package fr.univ.lyon1.common.exception; + +import fr.univ.lyon1.common.command.Command; + +public class UnknownCommand extends ChatException { + public UnknownCommand() { + super("Command unknown"); + } +} diff --git a/src/fr/univ/lyon1/gui/ClientGUI.java b/src/fr/univ/lyon1/gui/ClientGUI.java index cb19ab2..18acf9e 100644 --- a/src/fr/univ/lyon1/gui/ClientGUI.java +++ b/src/fr/univ/lyon1/gui/ClientGUI.java @@ -2,22 +2,21 @@ package fr.univ.lyon1.gui; import fr.univ.lyon1.client.Client; import fr.univ.lyon1.client.ClientReceive; -import fr.univ.lyon1.common.Message; +import fr.univ.lyon1.common.command.Command; import java.io.IOException; public class ClientGUI extends Client { private final MainGui gui; - public ClientGUI(MainGui gui, String address, int port) throws IOException, InterruptedException, Exception { + public ClientGUI(MainGui gui, String address, int port) throws Exception { super(address, port, null, null); this.gui = gui; } @Override - public Message messageReceived(Message msg) { - gui.receiveMessage(msg.toString()); - return msg; + protected void commandMessage(Command cmd) { + gui.receiveMessage(cmd.getArgs().get(0).toString()); } @Override diff --git a/src/fr/univ/lyon1/server/ConnectedClient.java b/src/fr/univ/lyon1/server/ConnectedClient.java index 3f583d7..74ce713 100644 --- a/src/fr/univ/lyon1/server/ConnectedClient.java +++ b/src/fr/univ/lyon1/server/ConnectedClient.java @@ -3,6 +3,12 @@ package fr.univ.lyon1.server; import fr.univ.lyon1.common.Channel; import fr.univ.lyon1.common.Message; import fr.univ.lyon1.common.User; +import fr.univ.lyon1.common.command.Command; +import fr.univ.lyon1.common.command.CommandType; +import fr.univ.lyon1.common.exception.ChatException; +import fr.univ.lyon1.common.exception.LoginInvalid; +import fr.univ.lyon1.common.exception.LoginRequired; +import fr.univ.lyon1.common.exception.UnknownCommand; import fr.univ.lyon1.server.models.ChannelModel; import fr.univ.lyon1.server.models.UserModel; @@ -11,6 +17,7 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.Socket; +import java.util.Collections; import java.util.List; public class ConnectedClient implements Runnable { @@ -26,77 +33,67 @@ public class ConnectedClient implements Runnable { this.server = server; this.socket = socket; this.out = new ObjectOutputStream(socket.getOutputStream()); - - System.out.println("New user try to auth"); - while (!this.auth()); - } - - private boolean auth() throws IOException { - if (in == null) - in = new ObjectInputStream(socket.getInputStream()); - - String username = in.readUTF(); - System.out.println("username: "+username); - String password = in.readUTF(); - System.out.println("Pass: "+password); - - if (username.isEmpty() || password.isEmpty()) { - out.writeUTF("err: Login required"); - out.flush(); - return false; - } - - UserModel user = UserModel.get(username); - - if (user == null) - out.writeUTF("err: Username not found !"); - else if (!user.checkPassword(password)) - out.writeUTF("err: Password invalid !"); - else { - out.writeUTF("logged"); - out.flush(); - this.user = user; - return true; - } - out.flush(); - - return false; + this.in = new ObjectInputStream(socket.getInputStream()); } public Message sendMessage(Message message) throws IOException { - out.writeObject(message); + out.writeObject(new Command(CommandType.message, List.of(message))); out.flush(); return message; } - private void actionMessage(Object data) throws IOException, ClassNotFoundException { - Message msg = (Message) data; + private void actionCommand(Command command) throws IOException, ChatException { + CommandType type = command.getType(); + if (user == null && type != CommandType.login) + throw new LoginRequired(); - if (msg.getContent().startsWith("/")) - command(msg); - else { - msg.setSender(this.user); - server.broadcastMessage(msg, id); + switch (command.getType()) { + case login -> commandLogin(command); + case message -> commandMessage(command); + case list -> commandList(); + case join -> commandJoin(command); } } - private void command(Message msg) throws IOException { - String content = msg.getContent(); - if (content.equals("/list")) { - List users = server.getUsers(); - sendMessage(msg.repley(Server.getServerUser(), "Total: "+users.toArray().length + "\n" + String.join(", ", users.stream().map(User::getUsername).toList()))); - } else if (content.startsWith("/join")) - actionJoinChannel(content); + private void commandLogin(Command cmd) throws IOException, ChatException { + List args = cmd.getArgs(); + + String username = (String) args.get(0); + System.out.println("username: "+username); + String password = (String) args.get(1); + System.out.println("Pass: "+password); + + if (username.isEmpty() || password.isEmpty()) + throw new LoginInvalid("Invalid args"); + + UserModel user = UserModel.get(username); + + if (user == null) + throw new LoginInvalid("Username not found"); + else if (!user.checkPassword(password)) + throw new LoginInvalid("Password invalid"); + else { + out.writeObject(new Command(CommandType.login, null)); + out.flush(); + this.user = user; + System.out.println("Client "+user.getUsername()+" is connected !"); + } } - private void actionListUsers() throws IOException { - out.writeObject(server.getUsers()); + private void commandMessage(Command cmd) { + Message msg = (Message) cmd.getArgs().get(0); + msg.setSender(this.user); + // ToDo: Check the user channel + server.broadcastMessage(msg, id); + } + + private void commandList() throws IOException { + out.writeObject(new Command(CommandType.list, Collections.singletonList(server.getUsers()))); out.flush(); } - private void actionJoinChannel(String msg) throws IOException { - String name = msg.replaceAll("^(/?join) ", ""); - System.out.println(name); + private void commandJoin(Command cmd) throws IOException { + String name = (String) cmd.getArgs().get(0); ChannelModel chan = ChannelModel.get(name); if (chan == null) { @@ -106,7 +103,7 @@ public class ConnectedClient implements Runnable { if (!chan.have(user)) chan.addUser(user); - out.writeObject(chan); + out.writeObject(new Command(CommandType.join, List.of((Channel) chan))); out.flush(); server.broadcastMessage(new Message(chan, Server.getServerUser(), user.getUsername()+" joined the channel !"), -1); @@ -115,19 +112,18 @@ public class ConnectedClient implements Runnable { public void run() { try { while (true) { - Object data = in.readObject(); if (data == null) break; - if (data instanceof Message) - actionMessage(data); - else if (data instanceof String) { - String msg = (String) data; - if (data.equals("listUsers")) - actionListUsers(); - else if (((String) data).startsWith("join")) - actionJoinChannel(msg); + try { + if (data instanceof Command) + actionCommand((Command) data); + else + throw new UnknownCommand(); + } catch (ChatException e) { + out.writeObject(e); + out.flush(); } } } catch (IOException | ClassNotFoundException e) { diff --git a/src/fr/univ/lyon1/server/Server.java b/src/fr/univ/lyon1/server/Server.java index 9ab29d6..8bf6058 100644 --- a/src/fr/univ/lyon1/server/Server.java +++ b/src/fr/univ/lyon1/server/Server.java @@ -23,9 +23,6 @@ public class Server { public ConnectedClient addClient(ConnectedClient newClient) { clients.add(newClient); - - System.out.println("Client "+newClient.getUser().getUsername()+" is connected !"); - return newClient; } diff --git a/src/module-info.java b/src/module-info.java index b6f9ef6..670c9b7 100644 --- a/src/module-info.java +++ b/src/module-info.java @@ -8,5 +8,6 @@ module fr.univ.lyon1.gui { requires org.mariadb.jdbc; opens fr.univ.lyon1.gui to javafx.fxml; + exports fr.univ.lyon1.common.command; exports fr.univ.lyon1.gui; } \ No newline at end of file