260 lines
8.2 KiB
Java
260 lines
8.2 KiB
Java
package fr.univ.lyon1.client;
|
|
|
|
import fr.univ.lyon1.common.Channel;
|
|
import fr.univ.lyon1.common.ChatSSL;
|
|
import fr.univ.lyon1.common.Message;
|
|
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.NotInChannel;
|
|
import fr.univ.lyon1.common.exception.UnknownCommand;
|
|
|
|
import javax.net.SocketFactory;
|
|
import javax.net.ssl.SSLContext;
|
|
import javax.net.ssl.SSLParameters;
|
|
import javax.net.ssl.SSLSocket;
|
|
import java.io.IOException;
|
|
import java.io.ObjectInputStream;
|
|
import java.io.ObjectOutputStream;
|
|
import java.net.Socket;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
import java.util.stream.Collectors;
|
|
|
|
/**
|
|
* The core of the client side
|
|
*/
|
|
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 final List<Channel> channels = new ArrayList<>();
|
|
protected boolean started = false;
|
|
|
|
/**
|
|
* A client need a server and login
|
|
* @param address the server address
|
|
* @param port the server port
|
|
* @param username thr username
|
|
* @param password the password
|
|
* @throws IOException When the initial communication with the server fail
|
|
*/
|
|
public Client(String address, int port, String username, String password) throws IOException {
|
|
this.address = address;
|
|
this.port = port;
|
|
this.username = username;
|
|
this.password = password;
|
|
socket = initSSL();
|
|
out = new ObjectOutputStream(socket.getOutputStream());
|
|
getIn();
|
|
}
|
|
|
|
/**
|
|
* Initialise the SSL WebSocket connection with the server
|
|
* @return the socket
|
|
* @throws IOException when unable to connect with the server
|
|
*/
|
|
private Socket initSSL() throws IOException {
|
|
SSLContext ctx = ChatSSL.getSSLContext();
|
|
|
|
SocketFactory factory = ctx.getSocketFactory();
|
|
|
|
Socket connection = factory.createSocket(address, port);
|
|
((SSLSocket) connection).setEnabledProtocols(new String[] {ChatSSL.tlsVersion});
|
|
SSLParameters sslParams = new SSLParameters();
|
|
sslParams.setEndpointIdentificationAlgorithm("HTTPS");
|
|
((SSLSocket) connection).setSSLParameters(sslParams);
|
|
return connection;
|
|
}
|
|
|
|
/**
|
|
* Close all connection to the server
|
|
* @throws IOException if fail to close the connection to the server
|
|
*/
|
|
public void disconnectedServer() throws IOException {
|
|
socket.close();
|
|
out.close();
|
|
if (in != null)
|
|
in.close();
|
|
|
|
System.exit(0);
|
|
}
|
|
|
|
/**
|
|
* Send a command to the server
|
|
* @param cmd the command
|
|
*/
|
|
private void send(Command cmd) {
|
|
try {
|
|
out.writeObject(cmd);
|
|
out.flush();
|
|
} catch (IOException e) {
|
|
System.err.println("Fail to send command !");
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send a message in the first channel
|
|
* @param content the content of the message
|
|
*/
|
|
public void sendMessage(String content) {
|
|
send(new Command(CommandType.message, List.of(new Message(content, channels.get(0)))));
|
|
}
|
|
|
|
/**
|
|
* Send a command
|
|
* @param content the command name an args (/commandName arg1 arh2 arg3)
|
|
* @throws UnknownCommand if the command can't be found
|
|
*/
|
|
public void sendCommand(String content) throws UnknownCommand {
|
|
List<String> args = Arrays.asList(content.split(" "));
|
|
String commandName = args.get(0).replace("/", "");
|
|
CommandType commandType;
|
|
|
|
try {
|
|
commandType = CommandType.valueOf(commandName);
|
|
} catch (IllegalArgumentException e) {
|
|
throw new UnknownCommand(commandName);
|
|
}
|
|
|
|
send(new Command(commandType, new ArrayList<>(args.subList(1, args.size()))));
|
|
}
|
|
|
|
/**
|
|
* Send a message with a specific channel
|
|
* @param content the channel name and the message content (#chanemName content of the message)
|
|
* @throws NotInChannel if the client isn't in the channel
|
|
*/
|
|
public void sendMessageChannel(String content) throws NotInChannel {
|
|
String[] args = content.split(" ");
|
|
String channelName = args[0].replace("#", "");
|
|
content = String.join(" ", Arrays.stream(args).toList().subList(1, args.length));
|
|
Channel channel = channels.stream().filter(c -> c.getName().equals(channelName)).findFirst().orElse(null);
|
|
|
|
if (channel == null)
|
|
throw new NotInChannel(channelName);
|
|
|
|
send(new Command(CommandType.message, List.of(new Message(content, channel))));
|
|
}
|
|
|
|
/**
|
|
* Manage income data from the server
|
|
* @param data the data
|
|
* @throws IOException if a connection error occur with the server
|
|
*/
|
|
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();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Command manager
|
|
* @param cmd the command
|
|
* @throws IOException if a connection error occur with the server
|
|
*/
|
|
private void command(Command cmd) throws IOException {
|
|
switch (cmd.getType()) {
|
|
case login -> commandLogin();
|
|
case message -> commandMessage(cmd);
|
|
case list -> commandList(cmd);
|
|
case listChannels -> commandListChannels(cmd);
|
|
case join -> commandJoin(cmd);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* After login handler
|
|
* @throws IOException if a connection error occur with the server
|
|
*/
|
|
private void commandLogin() throws IOException {
|
|
out.writeObject(new Command(CommandType.list, null));
|
|
out.flush();
|
|
out.writeObject(new Command(CommandType.listChannels, null));
|
|
out.flush();
|
|
out.writeObject(new Command(CommandType.join, List.of("general")));
|
|
out.flush();
|
|
}
|
|
|
|
/**
|
|
* Receiving message from the server
|
|
* @param cmd the message command
|
|
*/
|
|
protected void commandMessage(Command cmd) {
|
|
System.out.println();
|
|
System.out.println(cmd.getArgs().get(0));
|
|
}
|
|
|
|
/**
|
|
* User list handler
|
|
* @param cmd the command list
|
|
*/
|
|
private void commandList(Command cmd) {
|
|
System.out.println("Users: "+cmd.getArgs().stream().map(Object::toString).collect(Collectors.joining(", ")));
|
|
}
|
|
|
|
/**
|
|
* Channel list handler
|
|
* @param cmd the command channel list
|
|
*/
|
|
private void commandListChannels(Command cmd) {
|
|
System.out.println("Channels: "+cmd.getArgs().stream().map(Object::toString).collect(Collectors.joining(", ")));
|
|
}
|
|
|
|
/**
|
|
* The channel join handler
|
|
* @param cmd the command join
|
|
*/
|
|
private void commandJoin(Command cmd) {
|
|
Channel chan = (Channel) cmd.getArgs().get(0);
|
|
channels.add(chan);
|
|
System.out.println("You join "+chan);
|
|
}
|
|
|
|
/**
|
|
* Main thread function, creating sub thread for user input and output CLI management
|
|
* @throws InterruptedException If the client force exit
|
|
* @throws IOException if a connection error occur with the server
|
|
*/
|
|
public void run() throws InterruptedException, IOException {
|
|
if (started)
|
|
return;
|
|
|
|
Thread clientSendThread = new Thread(new ClientSend(this, out, socket));
|
|
clientSendThread.start();
|
|
|
|
Thread clientReceiveThread = new Thread(new ClientReceive(this, socket));
|
|
clientReceiveThread.start();
|
|
|
|
started = true;
|
|
|
|
out.writeObject(new Command(CommandType.login, List.of(username, password)));
|
|
out.flush();
|
|
|
|
clientSendThread.join();
|
|
socket.close();
|
|
clientReceiveThread.interrupt();
|
|
}
|
|
|
|
/**
|
|
* Get the in stream of the WebSocket
|
|
* @return the in stream
|
|
* @throws IOException if a connection error occur with the server
|
|
*/
|
|
public ObjectInputStream getIn() throws IOException {
|
|
if (in == null)
|
|
in = new ObjectInputStream(socket.getInputStream());
|
|
return in;
|
|
}
|
|
}
|