diff --git a/POO/src/communication/filetransfer/FileTransferClient.java b/POO/src/communication/filetransfer/FileTransferClient.java new file mode 100644 index 0000000..b0d7d25 --- /dev/null +++ b/POO/src/communication/filetransfer/FileTransferClient.java @@ -0,0 +1,38 @@ +package communication.filetransfer; + + +import java.io.File; + +import java.io.IOException; +import java.net.UnknownHostException; + +import java.util.ArrayList; + +import observers.ObserverInputMessage; + + + + +public class FileTransferClient { + + private int port; + private ArrayList files = null; + private ObserverInputMessage obsInput; + + + public FileTransferClient(int port, ArrayList filesToSend, ObserverInputMessage obs) throws UnknownHostException, IOException { + this.port = port; + this.files = filesToSend; + this.obsInput = obs; + } + + public void sendFiles() throws IOException, InterruptedException { + for(File f: this.files) { + FileTransferSendingThread ftc = new FileTransferSendingThread(this.port, f,this.obsInput); + ftc.start(); + ftc.join(); + } + + } + +} diff --git a/POO/src/communication/filetransfer/FileTransferReceivingThread.java b/POO/src/communication/filetransfer/FileTransferReceivingThread.java new file mode 100644 index 0000000..75f3062 --- /dev/null +++ b/POO/src/communication/filetransfer/FileTransferReceivingThread.java @@ -0,0 +1,82 @@ +package communication.filetransfer; + + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; + +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.SocketChannel; + +import messages.Message; +import messages.MessageFichier; +import observers.ObserverInputMessage; + +public class FileTransferReceivingThread extends Thread { + + private SocketChannel sockTransfert; + private ObserverInputMessage obsInput; + + public FileTransferReceivingThread(SocketChannel sock, ObserverInputMessage obs) { + this.sockTransfert = sock; + this.obsInput = obs; + } + + public void run() { + try { + int nbByteRead = 0; + ByteBuffer fileData = ByteBuffer.allocate(4 * FileTransferUtils.KB_SIZE); + + ObjectInputStream inputFileInformation = new ObjectInputStream( + this.sockTransfert.socket().getInputStream()); + + int nbTotalBytesRead; + + nbTotalBytesRead = 0; + + Object o = inputFileInformation.readObject(); + MessageFichier m = (MessageFichier) o; + String[] fileInfo = this.processFileInformation(m); + String filePath = FileTransferUtils.DOWNLOADS_RELATIVE_PATH + fileInfo[0]; + long fileSize = Long.parseLong(fileInfo[1]); + + + FileOutputStream fOutStream = new FileOutputStream(filePath); + + FileChannel fileWriter = fOutStream.getChannel(); + + while (nbTotalBytesRead < fileSize && (nbByteRead = this.sockTransfert.read(fileData)) > 0) { + fileData.flip(); + fileWriter.write(fileData); + fileData.clear(); + + nbTotalBytesRead += nbByteRead; + + } + + fileWriter.close(); + fOutStream.close(); + + Message mUpdate = FileTransferUtils.processMessageToDisplay(new File(filePath)); + mUpdate.setSender("other"); + this.obsInput.update(this, mUpdate); + + + } catch (IOException | ClassNotFoundException e) { + e.printStackTrace(); + } finally { + try { + this.sockTransfert.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + private String[] processFileInformation(MessageFichier m) { + return m.getContenu().split(";"); + } + +} diff --git a/POO/src/communication/filetransfer/FileTransferSendingThread.java b/POO/src/communication/filetransfer/FileTransferSendingThread.java new file mode 100644 index 0000000..61aaa2f --- /dev/null +++ b/POO/src/communication/filetransfer/FileTransferSendingThread.java @@ -0,0 +1,74 @@ +package communication.filetransfer; + +import java.io.File; +import java.io.IOException; + +import java.io.ObjectOutputStream; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.SocketChannel; +import java.nio.file.Paths; + +import messages.MauvaisTypeMessageException; +import messages.Message; +import messages.MessageFichier; +import messages.Message.TypeMessage; +import observers.ObserverInputMessage; + +public class FileTransferSendingThread extends Thread{ + + private SocketChannel sockTransfert; + private File file; + private ObserverInputMessage obsInput; + + public FileTransferSendingThread(int port, File fileToSend, ObserverInputMessage obs) throws IOException { + SocketChannel sock = SocketChannel.open(); + SocketAddress addr = new InetSocketAddress(port); + sock.connect(addr); + this.sockTransfert = sock; + this.file = fileToSend; + this.obsInput = obs; + } + + public void run() { + try { + + ByteBuffer fileData = ByteBuffer.allocate(4 * FileTransferUtils.KB_SIZE); + + ObjectOutputStream outputFileInformation = new ObjectOutputStream( + this.sockTransfert.socket().getOutputStream()); + + FileChannel fileReader = FileChannel.open(Paths.get(file.getPath())); + String str = file.getName() + ";" + file.getTotalSpace(); + + // Send file datas (name + size); + outputFileInformation.writeObject(new MessageFichier(TypeMessage.FICHIER, str, "")); + + while (fileReader.read(fileData) > 0) { + fileData.flip(); + this.sockTransfert.write(fileData); + fileData.clear(); + } + + fileReader.close(); + + Message mUpdate = FileTransferUtils.processMessageToDisplay(this.file); + mUpdate.setSender("Moi"); + this.obsInput.update(this, mUpdate); + + } catch (IOException | MauvaisTypeMessageException e) { + e.printStackTrace(); + } finally { + try { + this.sockTransfert.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + + +} diff --git a/POO/src/communication/filetransfer/FileTransferServer.java b/POO/src/communication/filetransfer/FileTransferServer.java new file mode 100644 index 0000000..e16db67 --- /dev/null +++ b/POO/src/communication/filetransfer/FileTransferServer.java @@ -0,0 +1,46 @@ +package communication.filetransfer; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; + +import observers.ObserverInputMessage; + + +public class FileTransferServer extends Thread { + + private ServerSocketChannel sockFTListen; + private int nbFile; + private ObserverInputMessage obsInput; + + + public FileTransferServer(int nbFile, ObserverInputMessage obs) throws UnknownHostException, IOException { + this.sockFTListen = ServerSocketChannel.open(); + this.sockFTListen.socket().bind(new InetSocketAddress(0)); + this.nbFile = nbFile; + this.obsInput = obs; + } + + public int getPort() { + return this.sockFTListen.socket().getLocalPort(); + } + + @Override + public void run() { + try { + for (int i = 0; i < this.nbFile; i++) { + SocketChannel sock = this.sockFTListen.accept(); + + Thread ft = new FileTransferReceivingThread(sock, this.obsInput); + ft.start(); + ft.join(); + } + + this.sockFTListen.close(); + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/POO/src/communication/filetransfer/FileTransferUtils.java b/POO/src/communication/filetransfer/FileTransferUtils.java new file mode 100644 index 0000000..cd561ac --- /dev/null +++ b/POO/src/communication/filetransfer/FileTransferUtils.java @@ -0,0 +1,120 @@ +package communication.filetransfer; + +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Base64; +import java.util.List; + +import javax.imageio.ImageIO; + +import messages.MessageFichier; +import messages.MauvaisTypeMessageException; +import messages.Message.TypeMessage; + +public class FileTransferUtils { + + protected static final String DOWNLOADS_RELATIVE_PATH = "../downloads/"; + protected static final ArrayList IMAGE_EXTENSIONS = new ArrayList(List.of("tif","tiff","bmp","jpg","jpeg","gif", "png", "eps", "svg")); + protected static final int KB_SIZE = 1024; + + + protected static MessageFichier processMessageToDisplay(File file) throws IOException { + String nameFile = file.getName(); + String extension = processFileExtension(nameFile); + TypeMessage type; + String contenu; + + if(IMAGE_EXTENSIONS.contains(extension)) { + type = TypeMessage.IMAGE; + BufferedImage img = ImageIO.read(file); + contenu = encodeImage(createThumbnail(img), extension) ; + + }else { + type = TypeMessage.FICHIER; + contenu = nameFile; + } + + try { + //return new MessageFichier(type, contenu, extension); + return new MessageFichier(type, contenu, extension); + } catch (MauvaisTypeMessageException e) { + System.out.println("Should never go in"); + e.printStackTrace(); + } + return null; + } + + protected static String processFileExtension(String fileName) { + String extension = ""; + + int i = fileName.indexOf('.'); + if (i >= 0 || i != -1) { + extension = fileName.substring(i+1).toLowerCase(); + } + return extension; + } + + + private static BufferedImage createThumbnail(BufferedImage image){ + float w = image.getWidth(); + float ratio = (w > 150) ? (150F/w) : 1; + BufferedImage scaled = scale(image, ratio); + return scaled; + } + + private static String encodeImage(BufferedImage img, String extension) throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ImageIO.write(img, extension, bos); + String imgString = Base64.getEncoder().encodeToString(bos.toByteArray()); + bos.close(); + return imgString; + } + + + public static BufferedImage decodeImage(String imageString) throws IOException { + byte[] imgData = Base64.getDecoder().decode(imageString); + InputStream is = new ByteArrayInputStream(imgData); + BufferedImage img = ImageIO.read(is); + is.close(); + return img; + } + + private static BufferedImage scale(BufferedImage source, double ratio) { + int w = (int) (source.getWidth() * ratio); + int h = (int) (source.getHeight() * ratio); + BufferedImage bi = getCompatibleImage(w, h); + Graphics2D g2d = bi.createGraphics(); + double xScale = (double) w / source.getWidth(); + double yScale = (double) h / source.getHeight(); + AffineTransform at = AffineTransform.getScaleInstance(xScale, yScale); + g2d.drawRenderedImage(source, at); + g2d.dispose(); + return bi; + } + + private static BufferedImage getCompatibleImage(int w, int h) { + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice gd = ge.getDefaultScreenDevice(); + GraphicsConfiguration gc = gd.getDefaultConfiguration(); + BufferedImage image = gc.createCompatibleImage(w, h); + return image; + } + + public static void createDownloads() { + File downloads = new File(FileTransferUtils.DOWNLOADS_RELATIVE_PATH); + + if(!downloads.exists()) { + downloads.mkdir(); + } + } +} diff --git a/POO/src/communication/tcp/TCPClient.java b/POO/src/communication/tcp/TCPClient.java new file mode 100644 index 0000000..aeaa6c0 --- /dev/null +++ b/POO/src/communication/tcp/TCPClient.java @@ -0,0 +1,74 @@ +package communication.tcp; + +import java.io.IOException; + +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import java.net.InetAddress; +import java.net.Socket; + +import observers.ObserverInputMessage; +import observers.ObserverSocketState; + +import messages.MauvaisTypeMessageException; +import messages.Message; + +public class TCPClient { + + private Socket sockTCP; + private ObjectOutputStream output; + private TCPInputThread inputThread; + + + public TCPClient(Socket sockTCP) throws IOException { + this.sockTCP = sockTCP; + + this.output = new ObjectOutputStream(sockTCP.getOutputStream()); + ObjectInputStream input = new ObjectInputStream(sockTCP.getInputStream()); + this.inputThread = new TCPInputThread(input); + } + + + public TCPClient(InetAddress addr, int port) throws IOException { + this(new Socket(addr, port)); + + } + + public void startInputThread() { + this.inputThread.start(); + } + + + public void sendMessage(Message message) throws IOException, MauvaisTypeMessageException { + System.out.println("dans write"); + this.output.writeObject(message); + } + + + public void setObserverInputThread(ObserverInputMessage o) { + this.inputThread.setObserverInputMessage(o); + } + + + public void setObserverSocketState(ObserverSocketState o) { + this.inputThread.setObserverSocketState(o); + } + + + public void destroyAll() { + try { + if (!this.sockTCP.isClosed()) { + this.output.close(); + this.sockTCP.close(); + this.inputThread.setObserverSocketState(null); + } + this.inputThread = null; + this.sockTCP = null; + this.output = null; + + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/POO/src/communication/tcp/TCPInputThread.java b/POO/src/communication/tcp/TCPInputThread.java new file mode 100644 index 0000000..5287bf5 --- /dev/null +++ b/POO/src/communication/tcp/TCPInputThread.java @@ -0,0 +1,72 @@ +package communication.tcp; + +import java.io.IOException; +import java.io.ObjectInputStream; +import observers.ObserverInputMessage; +import observers.ObserverSocketState; + +public class TCPInputThread extends Thread { + + private ObjectInputStream input; + private boolean running; + private ObserverInputMessage obsInput; + private ObserverSocketState obsState; + + public TCPInputThread(ObjectInputStream input) { + this.input = input; + this.running = true; + } + + @Override + public void run() { + + while (this.running) { + try { + + System.out.println("dans read"); + Object o = this.input.readObject(); + this.obsInput.update(this, o); + + } catch (IOException | ClassNotFoundException e) { + this.interrupt(); + + } + + } + } + + @Override + public void interrupt() { + + try { + //Stop the thread + this.running = false; + //Close the stream and the socket + this.input.close(); + + if(this.obsState != null) { + //Send an update to the controller + this.obsState.updateSocketState(this, true); + } + + + //Set every attribute to null so they're collected by the GC + this.obsInput = null; + this.obsState = null; + this.input = null; + + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + protected void setObserverInputMessage(ObserverInputMessage o) { + this.obsInput = o; + } + + protected void setObserverSocketState(ObserverSocketState o) { + this.obsState = o; + } + +} diff --git a/POO/src/communication/tcp/TCPServer.java b/POO/src/communication/tcp/TCPServer.java new file mode 100644 index 0000000..bdbb16f --- /dev/null +++ b/POO/src/communication/tcp/TCPServer.java @@ -0,0 +1,43 @@ +package communication.tcp; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.UnknownHostException; + +import observers.ObserverInputMessage; + + +public class TCPServer extends Thread { + + //**** + public static int PORT_SERVER = 7000; + + private ServerSocket sockListenTCP; + private ObserverInputMessage obs; + + public TCPServer(int port) throws UnknownHostException, IOException { + this.sockListenTCP = new ServerSocket(port, 50, InetAddress.getLocalHost()); + } + + @Override + public void run() { + System.out.println("TCP running"); + Socket sockAccept; + while(true) { + try { + sockAccept = this.sockListenTCP.accept(); + this.obs.update(this, sockAccept); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + } + + public void addObserver(ObserverInputMessage obs) { + this.obs = obs; + } +} diff --git a/POO/src/communication/udp/CommunicationUDP.java b/POO/src/communication/udp/CommunicationUDP.java new file mode 100644 index 0000000..91eec76 --- /dev/null +++ b/POO/src/communication/udp/CommunicationUDP.java @@ -0,0 +1,206 @@ +package communication.udp; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.util.ArrayList; + +import main.Utilisateur; + +import messages.*; +import observers.ObserverUserList; + +public class CommunicationUDP extends Thread { + + // **** + protected static int PORT_SERVEUR = 3000; + // **** + protected static int PORT_CLIENT = 2000; + + private UDPClient client; + private UDPServer server; + private int portServer; + private ArrayList portOthers; + private ArrayList users = new ArrayList(); + private ObserverUserList observer; + + public CommunicationUDP(int portClient, int portServer, int[] portsOther) throws IOException { + this.portServer = portServer; + this.portOthers = this.getArrayListFromArray(portsOther); + this.server = new UDPServer(portServer, this); + this.server.start(); + this.client = new UDPClient(portClient); + } + + // **** + public CommunicationUDP() throws SocketException, UnknownHostException { + this.portServer = PORT_SERVEUR; + this.server = new UDPServer(portServer, this); + this.server.start(); + this.client = new UDPClient(PORT_CLIENT); + } + + private ArrayList getArrayListFromArray(int ports[]) { + ArrayList tmp = new ArrayList(); + for (int port : ports) { + tmp.add(port); + } + tmp.remove(Integer.valueOf(portServer)); + + return tmp; + } + + public void setObserver(ObserverUserList obs) { + this.observer = obs; + } + + // -------------- USER LIST UPDATE FUNCTION --------------// + + protected synchronized void addUser(String idClient, String pseudoClient, InetAddress ipClient, int port) + throws IOException { + users.add(new Utilisateur(idClient, pseudoClient, ipClient, port)); + this.sendUpdate(); + + } + + protected synchronized void changePseudoUser(String idClient, String pseudoClient, InetAddress ipClient, int port) { + int index = getIndexFromID(idClient); + users.get(index).setPseudo(pseudoClient); + this.sendUpdate(); + } + + protected synchronized void removeUser(String idClient, String pseudoClient, InetAddress ipClient, int port) { + int index = getIndexFromID(idClient); + if (index != -1) { + users.remove(index); + } + this.sendUpdate(); + } + + public void removeAll() { + int oSize = users.size(); + for (int i = 0; i < oSize; i++) { + users.remove(0); + } + } + + // -------------- CHECKERS --------------// + + protected boolean containsUserFromID(String id) { + for (Utilisateur u : users) { + if (u.getId().equals(id)) { + return true; + } + } + return false; + } + + public boolean containsUserFromPseudo(String pseudo) { + for (Utilisateur u : users) { + if (u.getPseudo().toLowerCase().equals(pseudo)) { + return true; + } + } + + return false; + } + + // -------------- GETTERS --------------// + + public Utilisateur getUserFromPseudo(String pseudo) { + for (int i = 0; i < users.size(); i++) { + if (users.get(i).getPseudo().equals(pseudo)) { + return users.get(i); + } + } + return null; + } + + private int getIndexFromID(String id) { + for (int i = 0; i < users.size(); i++) { + if (users.get(i).getId().equals(id)) { + return i; + } + } + return -1; + } + + // -------------- SEND MESSAGES --------------// + + public void sendMessageConnecte() throws UnknownHostException, IOException { + for (int port : this.portOthers) { + try { + this.client.sendMessageUDP_local(new MessageSysteme(Message.TypeMessage.JE_SUIS_CONNECTE), port, + InetAddress.getLocalHost()); + } catch (MauvaisTypeMessageException e) { + /* Si ça marche pas essayer là */} + } + } + + // Send the message "add,id,pseudo" to localhost on all the ports in + // "portOthers" + // This allows the receivers' agent (portOthers) to create or modify an entry + // with the + // data of this agent + // Typically used to notify of a name change + public void sendMessageInfoPseudo() throws UnknownHostException, IOException { + + Utilisateur self = Utilisateur.getSelf(); + + try { + Message msgOut = new MessageSysteme(Message.TypeMessage.INFO_PSEUDO, self.getPseudo(), self.getId(), self.getPort()); + for (int port : this.portOthers) { + this.client.sendMessageUDP_local(msgOut, port, InetAddress.getLocalHost()); + } + } catch (Exception e) { + e.printStackTrace(); + } + + } + + // Same, but on only one port + // Typically used to give your current name and id to a newly arrived host + public void sendMessageInfoPseudo(int portOther) throws UnknownHostException, IOException { + + Utilisateur self = Utilisateur.getSelf(); + try { + Message msgOut = new MessageSysteme(Message.TypeMessage.INFO_PSEUDO, self.getPseudo(), self.getId(), + self.getPort()); + this.client.sendMessageUDP_local(msgOut, portOther, InetAddress.getLocalHost()); + } catch (MauvaisTypeMessageException e) { + e.printStackTrace(); + } + } + + // Send the message "del,id,pseudo" to localhost on all the ports in + // "portOthers" + // This allows the receivers' agent (portOthers) to delete the entry + // corresponding to this agent + public void sendMessageDelete() throws UnknownHostException, IOException { + Utilisateur self = Utilisateur.getSelf(); + try { + + Message msgOut = new MessageSysteme(Message.TypeMessage.JE_SUIS_DECONNECTE, self.getPseudo(), self.getId(), self.getPort()); + for (int port : this.portOthers) { + this.client.sendMessageUDP_local(msgOut, port, InetAddress.getLocalHost()); + } + + } catch (MauvaisTypeMessageException e) { + + } + } + + + private void sendUpdate() { + if(this.observer != null) { + this.observer.updateList(this, users); + } + } + + public void destroyAll() { + this.client.destroyAll(); + this.server.interrupt(); + } + +} diff --git a/POO/src/communication/udp/UDPClient.java b/POO/src/communication/udp/UDPClient.java new file mode 100644 index 0000000..54839df --- /dev/null +++ b/POO/src/communication/udp/UDPClient.java @@ -0,0 +1,43 @@ +package communication.udp; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.UnknownHostException; + +import messages.*; + +public class UDPClient { + + private DatagramSocket sockUDP; + private InetAddress broadcast; + + public UDPClient(int port) throws SocketException, UnknownHostException { + this.sockUDP = new DatagramSocket(port); + + InetAddress localHost = InetAddress.getLocalHost(); + NetworkInterface networkInterface = NetworkInterface.getByInetAddress(localHost); + this.broadcast = networkInterface.getInterfaceAddresses().get(0).getBroadcast(); + System.out.println(this.broadcast); + System.out.println(InetAddress.getLocalHost()); + } + + + //Send a message casted as string to the specified port on localhost + protected void sendMessageUDP_local(Message message, int port, InetAddress clientAddress) throws IOException { + String messageString= message.toString(); + DatagramPacket outpacket = new DatagramPacket(messageString.getBytes(), messageString.length(), clientAddress, port); + this.sockUDP.send(outpacket); + + } + + protected void destroyAll() { + this.sockUDP.close(); + this.sockUDP = null; + this.broadcast = null; + } + +} \ No newline at end of file diff --git a/POO/src/communication/udp/UDPServer.java b/POO/src/communication/udp/UDPServer.java new file mode 100644 index 0000000..529d303 --- /dev/null +++ b/POO/src/communication/udp/UDPServer.java @@ -0,0 +1,85 @@ +package communication.udp; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.SocketException; + +import main.Utilisateur; +import messages.*; + +public class UDPServer extends Thread { + + private DatagramSocket sockUDP; + private CommunicationUDP commUDP; + private byte[] buffer; + private boolean running; + + public UDPServer(int port, CommunicationUDP commUDP) throws SocketException { + this.running = true; + this.commUDP = commUDP; + this.sockUDP = new DatagramSocket(port); + this.buffer = new byte[256]; + } + + @Override + public void run() { + while (this.running) { + + try { + + DatagramPacket inPacket = new DatagramPacket(buffer, buffer.length); + this.sockUDP.receive(inPacket); + String msgString = new String(inPacket.getData(), 0, inPacket.getLength()); + Message msg = Message.stringToMessage(msgString); + + switch (msg.getTypeMessage()) { + case JE_SUIS_CONNECTE: + + if (Utilisateur.getSelf() != null) { + + int portClient = inPacket.getPort(); + int portServer = portClient+1; + this.commUDP.sendMessageInfoPseudo(portServer); + } + + break; + + case INFO_PSEUDO: + MessageSysteme m = (MessageSysteme) msg; + + if (this.commUDP.containsUserFromID(m.getId())) { + this.commUDP.changePseudoUser(m.getId(), m.getPseudo(), inPacket.getAddress(), m.getPort()); + } else { + + this.commUDP.addUser(m.getId(), m.getPseudo(), inPacket.getAddress(), m.getPort()); + System.out.println(m.getId() + ", " + m.getPseudo()); + } + break; + + case JE_SUIS_DECONNECTE: + this.commUDP.removeUser(((MessageSysteme) msg).getId(), ((MessageSysteme) msg).getPseudo(), + inPacket.getAddress(), inPacket.getPort()); + break; + + default: // Others types of messages are ignored because they are supposed to be + // transmitted by TCP and not UDP + } + + } catch (IOException e) { + System.out.println("receive exception"); + } + + } + } + + @Override + public void interrupt() { + // Stop the thread + this.running = false; + // Close the stream and the socket + this.sockUDP.close(); + this.buffer = null; + this.commUDP = null; + } +} diff --git a/POO/src/connexion/ControleurConnexion.java b/POO/src/connexion/ControleurConnexion.java index 030673e..ffb4c4c 100644 --- a/POO/src/connexion/ControleurConnexion.java +++ b/POO/src/connexion/ControleurConnexion.java @@ -4,61 +4,81 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.net.UnknownHostException; +import java.sql.SQLException; - -import communication.*; -import main.Observer; +import communication.udp.CommunicationUDP; +import database.SQLiteManager; import main.Utilisateur; import standard.VueStandard; -public class ControleurConnexion implements ActionListener, Observer{ +public class ControleurConnexion implements ActionListener{ private enum Etat {DEBUT, ID_OK}; private VueConnexion vue; private Etat etat; private CommunicationUDP comUDP; - private String id; - private String pseudo; private int portTCP; + private String username; + private SQLiteManager sqlManager; + private VueStandard vueStd; public ControleurConnexion(VueConnexion vue, int numtest) { this.vue = vue; this.etat = Etat.DEBUT; - this.id=""; + this.username = ""; + this.sqlManager = new SQLiteManager(0); + this.vueStd = null; //Pour les tests, changer pour un truc plus général quand on change CommunicationUDP + + //Note : 3334 est le port du serveur de présence + int[] portServer = {2209, 2309, 2409, 2509, 3334}; try { switch(numtest) { case 0 : - this.comUDP = new CommunicationUDP(2208, 2209, new int[] {2309, 2409, 3334}); + this.comUDP = new CommunicationUDP(2208, 2209, portServer); this.portTCP = 7010; break; case 1 : - this.comUDP = new CommunicationUDP(2308, 2309, new int[] {2209, 2409, 3334}); + this.comUDP = new CommunicationUDP(2308, 2309, portServer); this.portTCP = 7020; break; case 2 : - this.comUDP = new CommunicationUDP(2408, 2409, new int[] {2209, 2309, 3334}); + this.comUDP = new CommunicationUDP(2408, 2409, portServer); this.portTCP = 7030; break; + case 3 : + this.comUDP = new CommunicationUDP(2508, 2509, portServer); + this.portTCP = 7040; + break; default : - this.comUDP = new CommunicationUDP(2408, 2409, new int[] {2209, 2309, 3334}); + this.comUDP = new CommunicationUDP(2408, 2409, portServer); this.portTCP = 7040; } +// + } catch (IOException e) { e.printStackTrace(); } - comUDP.setObserver(this); } @Override public void actionPerformed(ActionEvent e) { + String pseudo; boolean inputOK = false; if (this.etat == Etat.DEBUT) { - id=vue.getValeurTextField(); - //Recherche dans la liste des utilisateurs enregistres, report sur inputOK - inputOK = (id.contentEquals("idvalide")||id.contentEquals("idv2")||id.contentEquals("idv2")); + this.username = this.vue.getUsernameValue(); + char[] password = this.vue.getPasswordValue(); + + try { + int res = this.sqlManager.checkPwd(this.username, password); + inputOK = (res == 1); + + } catch (SQLException e2) { + e2.printStackTrace(); + } + if (inputOK) { this.etat=Etat.ID_OK; @@ -82,23 +102,30 @@ public class ControleurConnexion implements ActionListener, Observer{ } //Mise en place de la demande du pseudo - vue.setTexteLabelInput("Veuillez entrer votre nom"); - vue.resetValeurTextField(); + this.vue.setConnexionInfo(""); + this.vue.removePasswordPanel(); + + this.vue.setTextUsernameField("Veuillez entrer votre pseudonyme"); + this.vue.resetUsernameField(); inputOK=false; } - else vue.setTexteLabelInput("Identifiant invalide, veuillez réessayer"); + else { + this.vue.setConnexionInfo("Identifiant ou mot de passe invalide, veuillez réessayer"); + this.vue.resetPasswordField(); + } + } else { - this.pseudo=vue.getValeurTextField(); + pseudo = vue.getUsernameValue(); //Recherche dans la liste locale des utilisateurs connectes, report sur inputOK - inputOK = !this.comUDP.containsUserFromPseudo(this.pseudo); - if(this.pseudo.equals("")) { - this.vue.setTexteLabelInput("Votre pseudonyme doit contenir au moins 1 caratère"); + inputOK = !this.comUDP.containsUserFromPseudo(pseudo); + if(pseudo.equals("")) { + this.vue.setConnexionInfo("Votre pseudonyme doit contenir au moins 1 caratère"); }else if (inputOK) { //Reglage de l'utilisateur try { - Utilisateur.setSelf(this.id, this.pseudo, "localhost", this.portTCP); + Utilisateur.setSelf(this.username, pseudo, "localhost", this.portTCP); } catch (UnknownHostException e2) { // TODO Auto-generated catch block e2.printStackTrace(); @@ -108,28 +135,42 @@ public class ControleurConnexion implements ActionListener, Observer{ try { this.comUDP.sendMessageInfoPseudo(); } catch (UnknownHostException e1) { - // TODO Auto-generated catch block e1.printStackTrace(); } catch (IOException e1) { - // TODO Auto-generated catch block e1.printStackTrace(); } + try { - this.vue.close(); - new VueStandard("Standard", comUDP, this.portTCP); + this.resetView(); + this.vue.setVisible(false); + this.setVueStandard(); } catch (IOException e1) { - // TODO Auto-generated catch block e1.printStackTrace(); } } - else this.vue.setTexteLabelInput("Ce nom est déjà utilisé, veuillez en choisir un autre"); + else this.vue.setConnexionInfo("Ce nom est déjà utilisé, veuillez en choisir un autre"); } } - @Override - public void update(Object o, Object arg) { - // TODO Auto-generated method stub + + private void setVueStandard() throws IOException { + if(this.vueStd == null) { + this.vueStd = new VueStandard("Standard", this.comUDP, this.portTCP, this.sqlManager, this.vue); + + }else { + this.vueStd.initControleur(); + this.vueStd.setPseudoSelf(); + this.vueStd.setVisible(true); + } + } + + private void resetView() { + this.etat = Etat.DEBUT; + this.vue.addPasswordPanel(); + this.vue.resetPasswordField(); + this.vue.resetUsernameField(); + this.vue.setTextUsernameField("Nom d'utilisateur"); + this.vue.setConnexionInfo(""); } - } diff --git a/POO/src/connexion/VueConnexion.java b/POO/src/connexion/VueConnexion.java index 51ca37b..e5ec22d 100644 --- a/POO/src/connexion/VueConnexion.java +++ b/POO/src/connexion/VueConnexion.java @@ -2,21 +2,26 @@ package connexion; //Importe les librairies import java.awt.*; -import java.awt.event.*; import javax.swing.*; +import database.SQLiteManager; import main.Vue; public class VueConnexion extends Vue { + private static final long serialVersionUID = 1L; //Elements vue - private JPanel panel; private JButton boutonValider; - private JTextField input; - private JLabel labelInput; + private JTextField inputUsername; + private JPasswordField inputPassword; + private JLabel labelUsername; + private JLabel labelPassword; + private JLabel connexionInfo; + private JPanel main; + private JPanel panelPassword; //Controleur - ControleurConnexion controle; + private ControleurConnexion controle; //penser à enlever le numtest public VueConnexion(int numtest) { @@ -25,20 +30,17 @@ public class VueConnexion extends Vue { //Creation fenetre this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - this.setSize(400, 100); + this.setSize(400, 300); this.setLocationRelativeTo(null); - //Creation panel - panel = new JPanel(new GridLayout(3,1)); - //Ajout elements ajouterElements(); //Regle le bouton par défaut this.getRootPane().setDefaultButton(boutonValider); - //Ajoute le panel a la fenetre - this.getContentPane().add(panel, BorderLayout.CENTER); + this.inputUsername.setText(SQLiteManager.hardcodedNames[numtest]+numtest); + this.inputPassword.setText("aze1$"+SQLiteManager.hardcodedNames[numtest].charAt(0)+numtest); //Affiche la fenetre this.setVisible(true); @@ -46,33 +48,82 @@ public class VueConnexion extends Vue { private void ajouterElements() { + //Creation panel + main = new JPanel(new GridLayout(4,1)); + JPanel panelUsername = new JPanel(new GridLayout(1, 2)); + this.panelPassword = new JPanel(new GridLayout(1, 2)); + //Cree les elements - input = new JTextField(); - labelInput = new JLabel("Veuillez entrer votre identifiant unique"); + this.connexionInfo = new JLabel(""); + + this.inputUsername = new JTextField(); + this.inputUsername.setPreferredSize(new Dimension(100, 50)); + + this.labelUsername = new JLabel("Nom d'utilisateur"); + this.labelUsername.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); + + this.inputPassword = new JPasswordField(); + this.inputPassword.setPreferredSize(new Dimension(100, 50)); + + this.labelPassword = new JLabel("Mot de passe :"); + this.labelPassword.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); + boutonValider = new JButton("Valider"); //Le controleur guette les evenements du bouton boutonValider.addActionListener(controle); //Ajoute les elements - panel.add(labelInput); - panel.add(input); - panel.add(boutonValider); + panelUsername.add(this.labelUsername); + panelUsername.add(this.inputUsername); - labelInput.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); + + this.panelPassword.add(this.labelPassword); + this.panelPassword.add(this.inputPassword); + + + + main.add(connexionInfo); + main.add(panelUsername); + main.add(this.panelPassword); + main.add(boutonValider); + + this.add(main); } //Getters et setters - public void setTexteLabelInput(String text) { - labelInput.setText(text); + protected void setConnexionInfo(String text) { + this.connexionInfo.setText(text); } - public String getValeurTextField() { - return input.getText(); + protected void setTextUsernameField(String text) { + this.labelUsername.setText(text); } - public void resetValeurTextField() { - input.setText(""); + protected String getUsernameValue() { + return this.inputUsername.getText(); } + + protected char[] getPasswordValue() { + return this.inputPassword.getPassword(); + } + + + protected void resetUsernameField() { + this.inputUsername.setText(""); + } + + protected void removePasswordPanel() { + this.main.remove(2); + } + + protected void addPasswordPanel() { + this.main.add(this.panelPassword, 2); + } + + protected void resetPasswordField() { + this.inputPassword.setText(""); + } + } diff --git a/POO/src/database/SQLiteCreateTables.java b/POO/src/database/SQLiteCreateTables.java new file mode 100644 index 0000000..45aa061 --- /dev/null +++ b/POO/src/database/SQLiteCreateTables.java @@ -0,0 +1,73 @@ +package database; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +class SQLiteCreateTables { + + + protected static void createTableUser(Connection connec) throws SQLException { + String createTableUser = "CREATE TABLE IF NOT EXISTS user (\r\n" + + " id INTEGER PRIMARY KEY AUTOINCREMENT,\r\n" + + " username VARCHAR (50) NOT NULL\r\n" + + " UNIQUE ON CONFLICT ROLLBACK,\r\n" + + " pwd_salt BLOB,\r\n" + + " db_datakey_salt BLOB,\r\n" + + " encrypted_pwd_hashsalt BLOB,\r\n" + + " encrypted_db_datakey BLOB,\r\n" + + " iv_datakey BLOB\r\n" + + ");"; + + Statement stmt = connec.createStatement(); + stmt.execute(createTableUser); + + } + + protected static void createTableConversation(Connection connec) throws SQLException { + String createTableConversation = "CREATE TABLE IF NOT EXISTS conversation (\r\n" + + " id_conversation INTEGER PRIMARY KEY AUTOINCREMENT,\r\n" + + " id_emetteur INTEGER REFERENCES user (id) \r\n" + + " NOT NULL,\r\n" + + " id_recepteur INTEGER REFERENCES user (id) \r\n" + + " NOT NULL,\r\n" + +" iv_conversation BLOB NOT NULL" + + ");"; + + Statement stmt = connec.createStatement(); + stmt.execute(createTableConversation); + + } + + protected static void createTableMessage(Connection connec) throws SQLException { + String createTableMessage = "CREATE TABLE IF NOT EXISTS message (\r\n" + + " id_message INTEGER PRIMARY KEY AUTOINCREMENT,\r\n" + + " id_conversation INTEGER REFERENCES conversation (id_conversation) \r\n" + + " NOT NULL,\r\n" + + " id_type INTEGER REFERENCES type (id_type) \r\n" + + " NOT NULL,\r\n" + + " content BLOB,\r\n" + + " date INTEGER NOT NULL,\r\n" + + " extension VARCHAR (20) \r\n" + + ");\r\n"; + + Statement stmt = connec.createStatement(); + stmt.execute(createTableMessage); + } + + protected static void createTableType(Connection connec) throws SQLException { + String createTableType = "CREATE TABLE IF NOT EXISTS type (\r\n" + " id_type INTEGER PRIMARY KEY,\r\n" + + " label VARCHAR (20) NOT NULL\r\n" + ");"; + + Statement stmt = connec.createStatement(); + stmt.execute(createTableType); + + String typeText = "INSERT OR IGNORE INTO type (id_type, label) " + "VALUES (0, 'text');"; + String typeFile = "INSERT OR IGNORE INTO type (id_type, label) " + "VALUES (1, 'file');"; + String typeImage = "INSERT OR IGNORE INTO type (id_type, label) " + "VALUES (2, 'image');"; + + stmt.execute(typeText); + stmt.execute(typeFile); + stmt.execute(typeImage); + } +} diff --git a/POO/src/database/SQLiteEncprytion.java b/POO/src/database/SQLiteEncprytion.java new file mode 100644 index 0000000..38c0916 --- /dev/null +++ b/POO/src/database/SQLiteEncprytion.java @@ -0,0 +1,84 @@ +package database; + + +import java.util.Base64; +import java.util.Random; + +import javax.crypto.*; +import javax.crypto.spec.*; +import java.security.*; +import java.security.spec.*; + +class SQLiteEncprytion { + + private static final Random RANDOM = new SecureRandom(); + private static final int ITERATIONS = 10000; + private static final int KEY_LENGTH = 256; + protected static final String encryptAlgorithm = "AES/CBC/PKCS5Padding"; + + protected static byte[] getNextSalt() { + byte[] salt = new byte[24]; + RANDOM.nextBytes(salt); + return salt; + } + + + protected static byte[] hash(char[] password, byte[] salt) { + return SQLiteEncprytion.getKey(password, salt).getEncoded(); + } + + + protected static SecretKey getKey(char[] password, byte[] salt) { + PBEKeySpec saltpwd = new PBEKeySpec(password, salt, ITERATIONS, KEY_LENGTH); + + try { + SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); + SecretKey tmp = skf.generateSecret(saltpwd); + SecretKey key = new SecretKeySpec(tmp.getEncoded(), "AES"); + return key; + } catch (InvalidKeySpecException | NoSuchAlgorithmException e) { + e.printStackTrace(); + } finally { + saltpwd.clearPassword(); + } + return null; + } + + + public static IvParameterSpec generateIv() { + byte[] iv = new byte[16]; + new SecureRandom().nextBytes(iv); + return new IvParameterSpec(iv); + } + + + protected static byte[] encrypt(String algorithm, byte[] input, SecretKey key, IvParameterSpec iv) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException{ + Cipher cipher = Cipher.getInstance(algorithm); + cipher.init(Cipher.ENCRYPT_MODE, key, iv); + byte[] cipherText = cipher.doFinal(input); + return Base64.getEncoder().encode(cipherText); + } + + + protected static byte[] decryptByte(String algorithm, byte[] cipherText, SecretKey key, IvParameterSpec iv) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException{ + Cipher cipher = Cipher.getInstance(algorithm); + cipher.init(Cipher.DECRYPT_MODE, key, iv); + byte[] plainText = cipher.doFinal(Base64.getDecoder().decode(cipherText)); + return plainText; + } + + protected static String decryptString(String algorithm, byte[] cipherText, SecretKey key, IvParameterSpec iv) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { + return new String(SQLiteEncprytion.decryptByte(algorithm, cipherText, key, iv) ); + } + + + public static byte[] keyToByte(SecretKey key) { + return Base64.getEncoder().encode(key.getEncoded()); + } + + + public static SecretKey byteToKey(byte[] encodedKey) { + byte[] decodedKey = Base64.getDecoder().decode(encodedKey); + return new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES"); + } +} diff --git a/POO/src/database/SQLiteManager.java b/POO/src/database/SQLiteManager.java new file mode 100644 index 0000000..fcf0764 --- /dev/null +++ b/POO/src/database/SQLiteManager.java @@ -0,0 +1,562 @@ +package database; + +import java.io.File; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KeyGenerator; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; + + +import main.Utilisateur; +import messages.MauvaisTypeMessageException; +import messages.Message; +import messages.MessageTexte; +import messages.Message.TypeMessage; +import messages.MessageFichier; + +public class SQLiteManager { + + private static final String DATABASE_RELATIVE_PATH = "../database"; + + public static String[] hardcodedNames = {"Olivia","Liam","Benjamin","Sophia","Charlotte","Noah","Elijah","Isabella", + "Oliver","Emma","William","Amelia","Evelyn","James","Mia","Ava","Lucas","Mason","Ethan","Harper"}; + + private Connection connec; + private int numDatabase; + private SecretKey dbDataKey; + + public SQLiteManager(int numDatabase) { + this.numDatabase = numDatabase; + + this.openConnection(); + + try { + + SQLiteCreateTables.createTableUser(this.connec); + SQLiteCreateTables.createTableConversation(this.connec); + SQLiteCreateTables.createTableType(this.connec); + SQLiteCreateTables.createTableMessage(this.connec); + + } catch (SQLException e) { + this.closeConnection(); + File db = new File("../database"+this.numDatabase+".db"); + if(db.delete()) { + System.out.println("supp"); + }else { + System.out.println("no supp"); + } + e.printStackTrace(); + } + + this.closeConnection(); + } + + private void openConnection() { + String url = "jdbc:sqlite:"+ DATABASE_RELATIVE_PATH + this.numDatabase + ".db"; + try { + this.connec = DriverManager.getConnection(url); +// System.out.println("Connection to bdd established"); + } catch (SQLException e) { + System.out.println(e.getMessage()); + } + } + + private void closeConnection() { + try { + if (this.connec != null) { + this.connec.close(); + } + } catch (SQLException e) { + System.out.println(e.getMessage()); + } + + } + + public int insertAllMessages(ArrayList messages, String usernameSender, String usernameReceiver) throws SQLException{ + int nbRows = 0; + this.openConnection(); + + int idSender = this.getIDUser(usernameSender); + int idReceiver = this.getIDUser(usernameReceiver); + + if(idSender == -1) { + this.insertUser(usernameSender); + idSender = this.getIDUser(usernameSender); + } + + if(idReceiver == -1) { + this.insertUser(usernameReceiver); + idReceiver = this.getIDUser(usernameReceiver); + } + + int idConversation = getIDConversation(idSender, idReceiver); + + if(idConversation == -1) { + this.insertConversation(idSender, idReceiver); + idConversation = getIDConversation(idSender, idReceiver); + } + + IvParameterSpec ivConversation = this.getIvConversation(idConversation); + + this.connec.setAutoCommit(false); + + for(Message m : messages) { + try { + nbRows += this.insertMessage(idConversation, m, ivConversation); + } catch (SQLException e) { + e.printStackTrace(); + this.connec.rollback(); + } + } + + this.connec.commit(); + + this.closeConnection(); + + //System.out.println("Nombre de message(s) insérée(s) : " + nbRows); + + return nbRows; + } + + + public ArrayList getHistoriquesMessages(String usernameOther, String pseudoOther) throws SQLException { + + this.openConnection(); + + + ArrayList messages = new ArrayList(); + + String usernameSelf = Utilisateur.getSelf().getId(); + + int idSelf = this.getIDUser(usernameSelf); + int idOther = this.getIDUser(usernameOther); + + int idConversationSelf = this.getIDConversation(idSelf, idOther); + int idConversationOther = this.getIDConversation(idOther, idSelf); + IvParameterSpec ivConversation = this.getIvConversation(idConversationSelf); +// String str = "datetime(d1,'unixepoch','localtime')"; + + + String getHistoriqueRequest = "SELECT id_conversation, id_type, content, date, extension " + + "FROM message " + + "WHERE id_conversation IN (?,?) " + + "ORDER by date"; + + PreparedStatement prepStmt = this.connec.prepareStatement(getHistoriqueRequest); + prepStmt.setInt(1, idConversationSelf); + prepStmt.setInt(2, idConversationOther); + ResultSet res = prepStmt.executeQuery(); + + //Retrieve the messages one by one + //Create the appropriate message object depending on the type and sender/receiver + //and add the message in the list + while(res.next()) { + int idType = res.getInt("id_type"); + String type = this.getType(idType); + + String content = null; + try { + content = this.bytesToStringContent(res.getBytes("content"), ivConversation); + } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException + | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException + | SQLException e1) { + //System.out.println("erreur déchiffrement"); + } + + Message message = null; + String extension; + + if(!type.equals("")) { + try { + switch(type) { + case "text": + message = new MessageTexte(TypeMessage.TEXTE, content); + break; + case "file": + extension = res.getString("extension"); + message = new MessageFichier(TypeMessage.FICHIER, content, extension); + break; + default: + extension = res.getString("extension"); + message = new MessageFichier(TypeMessage.IMAGE, content, extension); + } + + } catch (MauvaisTypeMessageException e) { + e.printStackTrace(); + } + } + + if(res.getInt("id_conversation") == idConversationSelf) { + message.setSender("Moi"); + }else{ + message.setSender(pseudoOther); + } + message.setDateMessage(res.getString("date")); + if(content != null) { + messages.add(message); + } + + } + + + this.closeConnection(); + + return messages; + } + + + private void insertUser(String username) throws SQLException { + String insertUserRequest = "INSERT INTO user (username) " + "VALUES (?);"; + + PreparedStatement prepStmt = this.connec.prepareStatement(insertUserRequest); + prepStmt.setString(1, username); + prepStmt.executeUpdate(); + } + + + private int getIDUser(String username) throws SQLException { + String getIDRequest = "SELECT id " + " FROM user" + " WHERE username = ? ;"; + + PreparedStatement prepStmt = this.connec.prepareStatement(getIDRequest); + prepStmt.setString(1, username); + ResultSet res = prepStmt.executeQuery(); + + if (res.next()) { + return res.getInt("id"); + } + return -1; + + } + + + private void insertConversation(int idSender, int idReceiver) throws SQLException { + String insertConversationRequest = "INSERT INTO conversation (id_emetteur, id_recepteur, iv_conversation) " + "VALUES " + + "(?, ?, ?)," + + "(?, ?, ?);"; + + byte[] ivConversation = SQLiteEncprytion.generateIv().getIV(); + + PreparedStatement prepStmt = this.connec.prepareStatement(insertConversationRequest); + prepStmt.setInt(1, idSender); + prepStmt.setInt(2, idReceiver); + prepStmt.setBytes(3, ivConversation); + prepStmt.setInt(4, idReceiver); + prepStmt.setInt(5, idSender); + prepStmt.setBytes(6, ivConversation); + + prepStmt.executeUpdate(); + } + + + private int getIDConversation(int idSender, int idReceiver) throws SQLException { + String getIDRequest = "SELECT id_conversation " + "FROM conversation " + "WHERE id_emetteur = ? " + + "AND id_recepteur = ? ;"; + + PreparedStatement prepStmt = this.connec.prepareStatement(getIDRequest); + prepStmt.setInt(1, idSender); + prepStmt.setInt(2, idReceiver); + ResultSet res = prepStmt.executeQuery(); + + if (res.next()) { + return res.getInt("id_conversation"); + } + return -1; + } + + private IvParameterSpec getIvConversation(int idConversation) throws SQLException { + String getIvRequest = "SELECT iv_conversation " + "FROM conversation " + "WHERE id_conversation = ?;"; + + PreparedStatement prepStmt = this.connec.prepareStatement(getIvRequest); + prepStmt.setInt(1, idConversation); + ResultSet res = prepStmt.executeQuery(); + + if (res.next()) { + return new IvParameterSpec(res.getBytes("iv_conversation")); + } + + return null; + } + + + + + private int getIDType(String label) throws SQLException { + String getIDRequest = "SELECT id_type FROM type WHERE label = ?;"; + + PreparedStatement prepStmt = this.connec.prepareStatement(getIDRequest); + prepStmt.setString(1, label); + + ResultSet res = prepStmt.executeQuery(); + + if (res.next()) { + return res.getInt("id_type"); + } + return -1; + } + + + private String getType(int idType) throws SQLException { + String getTypeRequest = "SELECT label FROM type WHERE id_type = ?;"; + + PreparedStatement prepStmt = this.connec.prepareStatement(getTypeRequest); + prepStmt.setInt(1, idType); + + ResultSet res = prepStmt.executeQuery(); + + if(res.next()) { + return res.getString("label"); + } + + return ""; + + } + + + private byte[] stringToBytesContent(Message m, IvParameterSpec iv) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { + String content; + if (m.getTypeMessage() == TypeMessage.TEXTE) { + MessageTexte messageTxt = (MessageTexte) m; + content = messageTxt.getContenu(); + }else { + MessageFichier messageFichier = (MessageFichier) m; + content = messageFichier.getContenu(); + } + byte[] encryptedContent = SQLiteEncprytion.encrypt(SQLiteEncprytion.encryptAlgorithm, content.getBytes(), this.dbDataKey, iv); + return encryptedContent; + + } + + private String bytesToStringContent(byte[] encryptedContent, IvParameterSpec iv) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { + return SQLiteEncprytion.decryptString(SQLiteEncprytion.encryptAlgorithm, encryptedContent, this.dbDataKey, iv); + } + + + private String processMessageType(Message m) { + switch (m.getTypeMessage()) { + case TEXTE: return "text"; + case FICHIER: return "file"; + case IMAGE: return "image"; + default: return ""; + } + } + + private String processExtension(Message m) { + if(m.getTypeMessage() == TypeMessage.TEXTE) { + return null; + }else { + MessageFichier mFile = (MessageFichier) m; + return mFile.getExtension(); + } + } + + + private int insertMessage(int idConversation, Message m, IvParameterSpec iv) throws SQLException { + + + String dateMessage = m.getDateMessage(); + String extension = this.processExtension(m); + String type = this.processMessageType(m); + int idType = this.getIDType(type); + + byte[] content = null; + + try { + content = this.stringToBytesContent(m, iv); + } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException + | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { + + e.printStackTrace(); + } + + String insertMessageRequest = "INSERT INTO message(id_conversation, id_type, content, date, extension) " + + "VALUES (?, ?, ?, ?, ?);"; + + PreparedStatement prepStmt = this.connec.prepareStatement(insertMessageRequest); + prepStmt.setInt(1, idConversation); + prepStmt.setInt(2, idType); + prepStmt.setBytes(3, content); + prepStmt.setString(4, dateMessage); + prepStmt.setString(5, extension); + + int nbRows = prepStmt.executeUpdate(); + + return nbRows; + } + + public void createNewUserEncrypt(String username, String password) { + + + String algo = SQLiteEncprytion.encryptAlgorithm; + + KeyGenerator keyGen = null; + try { + keyGen = KeyGenerator.getInstance("AES"); + keyGen.init(256); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + + byte[] passwordSalt = SQLiteEncprytion.getNextSalt(); + byte[] dbDataKeySalt = SQLiteEncprytion.getNextSalt(); + + SecretKey dbDataKey = keyGen.generateKey(); + + SecretKey dbDataEncryptKey = SQLiteEncprytion.getKey(password.toCharArray(), dbDataKeySalt); + IvParameterSpec ivDbDataKey = SQLiteEncprytion.generateIv(); + + byte[] passwordHash = SQLiteEncprytion.hash(password.toCharArray(), passwordSalt); + + byte[] dbDataKeyEncrypted = null; + byte[] encryptedPasswordHash = null; + + try { + dbDataKeyEncrypted = SQLiteEncprytion.encrypt( + algo, SQLiteEncprytion.keyToByte(dbDataKey), dbDataEncryptKey, ivDbDataKey); + encryptedPasswordHash = SQLiteEncprytion.encrypt( + algo, passwordHash , dbDataKey, ivDbDataKey); + + } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException + | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + this.openConnection(); + + String createUserRequest = "INSERT INTO user(username, pwd_salt, db_datakey_salt, encrypted_pwd_hashsalt, encrypted_db_datakey, iv_datakey) " + + "VALUES (?, ?, ?, ?, ?, ?); "; + + PreparedStatement prepStmt = null; + try { + prepStmt = this.connec.prepareStatement(createUserRequest); + prepStmt.setString(1, username); + prepStmt.setBytes(2, passwordSalt); + prepStmt.setBytes(3, dbDataKeySalt); + prepStmt.setBytes(4, encryptedPasswordHash); + prepStmt.setBytes(5, dbDataKeyEncrypted); + prepStmt.setBytes(6, ivDbDataKey.getIV()); + + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + try { + prepStmt.executeUpdate(); + System.out.println("Utilisateur crée"); + } catch (SQLException e) { + System.out.println("Nom d'utilisateur déjà pris"); + } + + this.closeConnection(); + + } + + /** + * + * @param username + * @param password + * @return -1 if user do not exists, + * 0 if password incorrect, + * 1 if password correct + * @throws SQLException + */ + public int checkPwd(String username, char[] password) throws SQLException { + + this.openConnection(); + + String selectUserDataRequest = "SELECT pwd_salt, db_datakey_salt, encrypted_pwd_hashsalt, encrypted_db_datakey, iv_datakey " + + "FROM user " + + "WHERE username = ?;"; + PreparedStatement prepStmt; + prepStmt = this.connec.prepareStatement(selectUserDataRequest); + prepStmt.setString(1, username); + + ResultSet res = prepStmt.executeQuery(); + if(!res.next()) { + return -1; + } + + byte[] passwordSalt = res.getBytes("pwd_salt"); + byte[] dbDataKeySalt = res.getBytes("db_datakey_salt"); + + SecretKey dbDataEncryptKey = SQLiteEncprytion.getKey(password, dbDataKeySalt); + IvParameterSpec iv = new IvParameterSpec(res.getBytes("iv_datakey")); + + byte[] encryptedDbDataKey = res.getBytes("encrypted_db_datakey"); + + + SecretKey dbDataKey = null; + try { + dbDataKey = SQLiteEncprytion.byteToKey( + SQLiteEncprytion.decryptByte(SQLiteEncprytion.encryptAlgorithm, encryptedDbDataKey, dbDataEncryptKey, iv) + ); + } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException + | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { + //System.out.println("Problème déchiffrement clé db"); + } + + this.dbDataKey = dbDataKey; + + byte[] encryptedPasswordHash = res.getBytes("encrypted_pwd_hashsalt"); + + + byte[] passwordHash = SQLiteEncprytion.hash(password, passwordSalt); + + this.closeConnection(); + + boolean checkHash = this.checkHashPwd(passwordHash ,encryptedPasswordHash, dbDataKey, iv); + if(checkHash) { + return 1; + } + return 0; + } + + + private boolean checkHashPwd(byte[] passwordHash, byte[] encryptedPasswordHash, SecretKey dbDataKey, IvParameterSpec iv) { + + byte[] expectedHash = "".getBytes(); + try { + expectedHash = SQLiteEncprytion.decryptByte(SQLiteEncprytion.encryptAlgorithm, encryptedPasswordHash, dbDataKey, iv); + } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException + | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { + } + + + + if (passwordHash.length != expectedHash.length) return false; + for (int i = 0; i < passwordHash.length; i++) { + if (passwordHash[i] != expectedHash[i]) return false; + } + return true; + } + + + public static void main(String[] args) { + String[] hardcodedNames = {"Olivia","Liam","Benjamin","Sophia","Charlotte","Noah","Elijah","Isabella", + "Oliver","Emma","William","Amelia","Evelyn","James","Mia","Ava","Lucas","Mason","Ethan","Harper"}; + + String pwdPrefix = "aze1$"; + + SQLiteManager sqlManager = new SQLiteManager(0); + + for(int i=0; i : "+this.contenu+" "+suffixe; + } + } +} \ No newline at end of file diff --git a/POO/src/messages/MessageSysteme.java b/POO/src/messages/MessageSysteme.java new file mode 100644 index 0000000..9c76271 --- /dev/null +++ b/POO/src/messages/MessageSysteme.java @@ -0,0 +1,47 @@ +package messages; + +public class MessageSysteme extends Message { + + private static final long serialVersionUID = 1L; + private String pseudo; + private String id; + private int port; + + public MessageSysteme(TypeMessage type) throws MauvaisTypeMessageException{ + if ((type==TypeMessage.JE_SUIS_CONNECTE)||(type==TypeMessage.MESSAGE_NUL)) { + this.type=type; + this.pseudo=""; + this.id=""; + this.port = -1; + } + else throw new MauvaisTypeMessageException(); + } + + public MessageSysteme(TypeMessage type, String pseudo, String id, int port) throws MauvaisTypeMessageException { + if (type==TypeMessage.INFO_PSEUDO ||(type==TypeMessage.JE_SUIS_DECONNECTE)) { + this.type=type; + this.pseudo=pseudo; + this.id=id; + this.port = port; + } + else throw new MauvaisTypeMessageException(); + } + + + public String getPseudo() { + return this.pseudo; + } + + public String getId() { + return this.id; + } + + public int getPort() { + return this.port; + } + + @Override + protected String attributsToString() { + return this.pseudo+"###"+this.id+"###"+this.port; + } +} diff --git a/POO/src/messages/MessageTexte.java b/POO/src/messages/MessageTexte.java new file mode 100644 index 0000000..434598b --- /dev/null +++ b/POO/src/messages/MessageTexte.java @@ -0,0 +1,33 @@ +package messages; + +public class MessageTexte extends Message { + + + private static final long serialVersionUID = 1L; + private String contenu; + + + public MessageTexte(TypeMessage type, String contenu) throws MauvaisTypeMessageException{ + if (type==TypeMessage.TEXTE) { + this.type=type; + this.contenu=contenu; + this.setDateMessage(Message.getDateAndTime()); + } + else throw new MauvaisTypeMessageException(); + } + + public String getContenu() { + return this.contenu; + } + + + @Override + protected String attributsToString() { + return this.contenu; + } + + @Override + public String toString() { + return "<"+this.getDateMessage()+"> "+this.getSender()+" : "+this.contenu+"\n"; + } +} \ No newline at end of file diff --git a/POO/src/observers/ObserverInputMessage.java b/POO/src/observers/ObserverInputMessage.java new file mode 100644 index 0000000..415c869 --- /dev/null +++ b/POO/src/observers/ObserverInputMessage.java @@ -0,0 +1,5 @@ +package observers; + +public interface ObserverInputMessage { + public void update(Object o, Object arg); +} diff --git a/POO/src/observers/ObserverSocketState.java b/POO/src/observers/ObserverSocketState.java new file mode 100644 index 0000000..58325df --- /dev/null +++ b/POO/src/observers/ObserverSocketState.java @@ -0,0 +1,7 @@ +package observers; + +public interface ObserverSocketState { + + public void updateSocketState(Object o, Object arg); + +} diff --git a/POO/src/observers/ObserverUserList.java b/POO/src/observers/ObserverUserList.java new file mode 100644 index 0000000..1dc483a --- /dev/null +++ b/POO/src/observers/ObserverUserList.java @@ -0,0 +1,11 @@ +package observers; + +import java.util.ArrayList; + +import main.Utilisateur; + +public interface ObserverUserList { + + public void updateList(Object o, ArrayList userList); + +} diff --git a/POO/src/session/ControleurSession.java b/POO/src/session/ControleurSession.java index d4c671b..3516fa1 100644 --- a/POO/src/session/ControleurSession.java +++ b/POO/src/session/ControleurSession.java @@ -5,33 +5,55 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; +import java.io.File; import java.io.IOException; import java.net.Socket; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; import javax.swing.JButton; -import javax.swing.JList; +import javax.swing.JFileChooser; -import java.util.concurrent.*; - -import communication.TCPClient; -import main.Observer; +import communication.filetransfer.FileTransferClient; +import communication.filetransfer.FileTransferServer; +import communication.tcp.TCPClient; +import database.SQLiteManager; import main.Utilisateur; import messages.MauvaisTypeMessageException; import messages.Message; +import messages.MessageFichier; import messages.MessageTexte; +import messages.Message.TypeMessage; +import observers.ObserverInputMessage; +import observers.ObserverSocketState; -public class ControleurSession implements ActionListener, Observer, KeyListener { +public class ControleurSession implements ActionListener, ObserverInputMessage, ObserverSocketState, KeyListener { private VueSession vue; + private String idOther; + private String pseudoOther; private TCPClient tcpClient; - - protected ControleurSession(VueSession vue, Socket socketComm) throws IOException { + private ArrayList messagesIn; + private ArrayList messagesOut; + private SQLiteManager sqlManager; + private ArrayList files; + + protected ControleurSession(VueSession vue, Socket socketComm, String idOther, String pseudoOther, SQLiteManager sqlManager) throws IOException { this.vue = vue; this.tcpClient = new TCPClient(socketComm); this.tcpClient.setObserverInputThread(this); + this.tcpClient.setObserverSocketState(this); this.tcpClient.startInputThread(); + this.messagesIn = new ArrayList(); + this.messagesOut = new ArrayList(); + + this.idOther = idOther; + this.pseudoOther = pseudoOther; + + this.sqlManager = sqlManager; + + this.files = new ArrayList(); } // ---------- ACTION LISTENER OPERATIONS ----------// @@ -40,16 +62,33 @@ public class ControleurSession implements ActionListener, Observer, KeyListener //Quand le bouton envoyer est presse if ((JButton) e.getSource() == this.vue.getButtonEnvoyer()) { - String messageOut = this.vue.getZoneSaisie().getText(); - System.out.println(messageOut); + String messageContent = this.vue.getInputedText(); + System.out.println(messageContent); - //Si le texte field n'est pas vide - if (!messageOut.equals("")) { + if(!this.files.isEmpty()) { + this.processSelectedFiles(messageContent); + if(!this.files.isEmpty()) { + this.askFileTransfer(); + + this.vue.resetZoneSaisie(); + messageContent = ""; + } + } + + + //If the text field is not empty + if (!messageContent.equals("")) { + + //Retrieve the date and prepare the messages to send/display + MessageTexte messageOut = null; + + try { + messageOut = new MessageTexte(TypeMessage.TEXTE, messageContent); + messageOut.setSender(Utilisateur.getSelf().getPseudo()); + } catch (MauvaisTypeMessageException e2) { + e2.printStackTrace(); + } - //On recupere la date et on prepare les messages a afficher/envoyer - String date = this.getDateAndTime(); - String messageToDisplay = date+" Moi : "+ messageOut; - messageOut = date +" "+ Utilisateur.getSelf().getPseudo() + " : " + messageOut+"\n"; try { this.tcpClient.sendMessage(messageOut); @@ -58,32 +97,33 @@ public class ControleurSession implements ActionListener, Observer, KeyListener e1.printStackTrace(); } - this.vue.appendMessage(messageToDisplay + "\n"); + messageOut.setSender("Moi"); + this.vue.appendMessage(messageOut); this.vue.resetZoneSaisie(); + + this.messagesOut.add(messageOut); } } - - } - - //Methode appelee quand l'inputStream de la socket de communication recoit des donnees - @Override - public void update(Object o, Object arg) { - if(arg instanceof MessageTexte) { - MessageTexte messageIn = (MessageTexte) arg; - System.out.println(messageIn.getContenu()); - this.vue.appendMessage(messageIn.getContenu()); - }else { - } - } + if((JButton) e.getSource() == this.vue.getButtonImportFile()) { + JFileChooser fc = new JFileChooser(); + fc.setMultiSelectionEnabled(true); + int returVal = fc.showDialog(this.vue, "Importer"); + + + if(returVal == JFileChooser.APPROVE_OPTION) { + File[] files = fc.getSelectedFiles(); + Collections.addAll(this.files, files); + for(File file : files) { + this.vue.appendInputedText(file.getName()); + this.vue.appendInputedText(";"); + } + } + + } - - - private String getDateAndTime() { - DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"); - LocalDateTime now = LocalDateTime.now(); - return "<"+dtf.format(now)+">"; } + @Override public void keyTyped(KeyEvent e) { @@ -106,5 +146,131 @@ public class ControleurSession implements ActionListener, Observer, KeyListener // TODO Auto-generated method stub } + + + protected ArrayList getHistorique(){ + try { + ArrayList historique = this.sqlManager.getHistoriquesMessages(idOther, pseudoOther); + return historique; + } catch (SQLException e) { + e.printStackTrace(); + return new ArrayList(); + + } + } + + private void processSelectedFiles(String input) { + String[] tmp = input.split(";"); + ArrayList potentialFiles = new ArrayList(); + Collections.addAll(potentialFiles, tmp); + + for(File file: this.files) { + if(!potentialFiles.contains(file.getName()) ) { + this.files.remove(file); + } + } + } + + private void askFileTransfer() { + try { + MessageFichier messageOut = new MessageFichier(TypeMessage.FICHIER_INIT, ""+this.files.size(), ""); + this.tcpClient.sendMessage(messageOut); + } catch (MauvaisTypeMessageException | IOException e1) { + e1.printStackTrace(); + } + } + + private void answerFileTransfer(int port) { + try { + MessageFichier messageOut = new MessageFichier(TypeMessage.FICHIER_ANSWER, ""+port, ""); + this.tcpClient.sendMessage(messageOut); + } catch (MauvaisTypeMessageException | IOException e1) { + e1.printStackTrace(); + } + } + + //Methode appelee quand l'inputStream de la socket de communication recoit des donnees + @Override + public void update(Object o, Object arg) { + Message message = (Message) arg; + switch(message.getTypeMessage()) { + case TEXTE: + System.out.println(message.toString()); + this.vue.appendMessage(message); + this.messagesIn.add(message); + break; + case IMAGE: + this.vue.appendImage(message); + + if(message.getSender().equals("Moi")) { + this.messagesOut.add(message); + }else { + this.messagesIn.add(message); + } + break; + case FICHIER: + this.vue.appendMessage(message); + + if(message.getSender().equals("Moi")) { + this.messagesOut.add(message); + }else { + this.messagesIn.add(message); + } + break; + case FICHIER_INIT: + try { + MessageFichier mFichier = (MessageFichier) arg; + int nbFile = Integer.parseInt(mFichier.getContenu()); + FileTransferServer fts = new FileTransferServer(nbFile, this); + int port = fts.getPort(); + fts.start(); + this.answerFileTransfer(port); + + } catch (IOException e) { + e.printStackTrace(); + } + break; + case FICHIER_ANSWER: + try { + MessageFichier mFichier = (MessageFichier) arg; + int port = Integer.parseInt(mFichier.getContenu()); + + @SuppressWarnings("unchecked") + FileTransferClient ftc = new FileTransferClient(port ,(ArrayList) this.files.clone(), this); + + ftc.sendFiles(); + this.files.clear(); + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + + break; + default: + } + + } -} + @Override + public void updateSocketState(Object o, Object arg) { + this.vue.endSession(this.pseudoOther); + } + + protected void destroyAll() { + String idSelf = Utilisateur.getSelf().getId(); + String idOther = this.idOther; + + try { + this.sqlManager.insertAllMessages(messagesOut, idSelf, idOther); + this.sqlManager.insertAllMessages(messagesIn, idOther, idSelf); + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + + this.vue = null; + this.tcpClient.destroyAll(); + this.tcpClient = null; + } + +} \ No newline at end of file diff --git a/POO/src/session/VueSession.java b/POO/src/session/VueSession.java index 8471522..98b6e96 100644 --- a/POO/src/session/VueSession.java +++ b/POO/src/session/VueSession.java @@ -2,108 +2,262 @@ package session; import java.awt.*; import java.awt.event.KeyEvent; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; +import java.awt.image.BufferedImage; import java.io.IOException; import java.net.Socket; -import java.net.SocketException; -import java.net.UnknownHostException; import java.util.ArrayList; -import java.util.List; -import java.util.Vector; -import javax.swing.BorderFactory; -import javax.swing.DefaultListModel; +import javax.swing.Icon; +import javax.swing.ImageIcon; import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; -import javax.swing.JTabbedPane; import javax.swing.JTextArea; -import javax.swing.JTextField; import javax.swing.JTextPane; import javax.swing.KeyStroke; -import javax.swing.ListSelectionModel; import javax.swing.ScrollPaneConstants; import javax.swing.border.EmptyBorder; +import javax.swing.text.AbstractDocument; +import javax.swing.text.BadLocationException; +import javax.swing.text.BoxView; +import javax.swing.text.ComponentView; +import javax.swing.text.Document; +import javax.swing.text.Element; +import javax.swing.text.IconView; +import javax.swing.text.LabelView; +import javax.swing.text.ParagraphView; +import javax.swing.text.StyleConstants; +import javax.swing.text.StyledDocument; +import javax.swing.text.StyledEditorKit; +import javax.swing.text.View; +import javax.swing.text.ViewFactory; -import communication.TCPClient; +import communication.filetransfer.FileTransferUtils; +import database.SQLiteManager; +import messages.Message; +import messages.Message.TypeMessage; -public class VueSession extends JPanel{ +public class VueSession extends JPanel { /** * */ private static final long serialVersionUID = 1L; - private JButton envoyerMessage; - private JTextArea chatWindow; + private JButton sendMessage; + private JButton importFile; + private JTextPane chatWindow; private JTextArea chatInput; private ControleurSession c; - public VueSession(Socket socketComm) throws IOException { + public VueSession(Socket socketComm, String idOther, String pseudoOther, SQLiteManager sqlManager) + throws IOException { - this.c = new ControleurSession(this, socketComm); + this.c = new ControleurSession(this, socketComm, idOther, pseudoOther, sqlManager); this.setBorder(new EmptyBorder(5, 5, 5, 5)); this.setLayout(new BorderLayout(0, 0)); - JPanel bottom = new JPanel(); - bottom.setLayout(new BorderLayout(0, 0)); - this.chatInput = new JTextArea(); this.chatInput.setColumns(10); this.chatInput.setLineWrap(true); this.chatInput.setWrapStyleWord(true); this.chatInput.addKeyListener(this.c); - - //remap enter to none to avoid \n after sending message + + this.chatWindow = new JTextPane(); + this.chatWindow.setEditable(false); + this.chatWindow.setEditorKit(new WrapEditorKit()); + + JScrollPane chatScroll = new JScrollPane(this.chatWindow); + chatScroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); + + JPanel bottom = new JPanel(); + bottom.setLayout(new BorderLayout(0, 0)); + + // remap "ENTER" to "none" to avoid "\n" in the input area after sending message KeyStroke enter = KeyStroke.getKeyStroke("ENTER"); this.chatInput.getInputMap().put(enter, "none"); KeyStroke shiftEnter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.SHIFT_DOWN_MASK); this.chatInput.getInputMap().put(shiftEnter, "insert-break"); - + + this.importFile = new JButton("Importer.."); + this.importFile.addActionListener(this.c); + JScrollPane inputScroll = new JScrollPane(this.chatInput); inputScroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - - //inputScroll.add(this.chatInput, BorderLayout.CENTER); - this.envoyerMessage = new JButton("Envoyer"); - this.envoyerMessage.addActionListener(this.c); + this.sendMessage = new JButton("Envoyer"); + this.sendMessage.addActionListener(this.c); - bottom.add(inputScroll); - bottom.add(this.envoyerMessage, BorderLayout.EAST); - - this.chatWindow = new JTextArea(); - this.chatWindow.setEditable(false); - this.chatWindow.setLineWrap(true); - this.chatWindow.setWrapStyleWord(true); - - JScrollPane chatScroll = new JScrollPane(this.chatWindow); + bottom.add(this.importFile, BorderLayout.WEST); + bottom.add(inputScroll, BorderLayout.CENTER); + bottom.add(this.sendMessage, BorderLayout.EAST); this.add(chatScroll, BorderLayout.CENTER); this.add(bottom, BorderLayout.SOUTH); this.setPreferredSize(new Dimension(500, 500)); - // this.setVisible(true); + + this.displayHistorique(); } protected JButton getButtonEnvoyer() { - return this.envoyerMessage; + return this.sendMessage; } - protected JTextArea getZoneSaisie() { - return this.chatInput; + protected JButton getButtonImportFile() { + return this.importFile; } + protected String getInputedText() { + return this.chatInput.getText(); + } + + protected void appendInputedText(String str) { + this.chatInput.append(str); + } + + protected void resetZoneSaisie() { this.chatInput.setText(""); + } - protected void appendMessage(String message) { - this.chatWindow.append(message); + private void setCaretToEnd() { + this.chatWindow.setCaretPosition(this.chatWindow.getDocument().getLength()); + } + + protected void appendString(String str) { + try { + Document doc = this.chatWindow.getDocument(); + doc.insertString(doc.getLength(), str, null); + } catch (BadLocationException e) { + e.printStackTrace(); + } } -} + protected void appendMessage(Message message) { + + try { + StyledDocument sdoc = this.chatWindow.getStyledDocument(); + // sdoc.setParagraphAttributes(sdoc.getLength(), message.toString().length()-1, + // style, false); + sdoc.insertString(sdoc.getLength(), message.toString(), null); + + } catch (BadLocationException e) { + e.printStackTrace(); + } + } + + protected void appendImage(Message message) { + this.setCaretToEnd(); + + String imgString = message.toString(); + Icon ic; + try { + BufferedImage img = FileTransferUtils.decodeImage(imgString); + ic = new ImageIcon(img); + this.chatWindow.insertIcon(ic); + this.appendString("\n"); + + } catch (IOException e) { + e.printStackTrace(); + } + + + } + + private void printLineSeparator() { + this.appendString("------------------------------------------\n"); + } + + protected void endSession(String pseudoOther) { + this.printLineSeparator(); + this.appendString(pseudoOther + " a mis fin à la session."); + this.chatInput.setEnabled(false); + this.chatInput.setFocusable(false); + this.sendMessage.setEnabled(false); + this.importFile.setEnabled(false); + } + + private void displayHistorique() { + ArrayList historique = this.c.getHistorique(); + + for (Message m : historique) { + if(m.getTypeMessage() == TypeMessage.IMAGE) { + this.appendImage(m); + }else { + this.appendMessage(m); + } + } + + if (historique.size() > 0) { + this.printLineSeparator(); + } + } + + public void destroyAll() { + if (this.c != null) { + this.c.destroyAll(); + } + this.c = null; + this.chatInput = null; + this.chatWindow = null; + this.sendMessage = null; + } + + // ------------- PRIVATE CLASS TO WRAP TEXT -------------// + + class WrapEditorKit extends StyledEditorKit { + + private static final long serialVersionUID = 1L; + + ViewFactory defaultFactory = new WrapColumnFactory(); + + public ViewFactory getViewFactory() { + return defaultFactory; + } + + } + + class WrapColumnFactory implements ViewFactory { + public View create(Element elem) { + String kind = elem.getName(); + if (kind != null) { + if (kind.equals(AbstractDocument.ContentElementName)) { + return new WrapLabelView(elem); + } else if (kind.equals(AbstractDocument.ParagraphElementName)) { + return new ParagraphView(elem); + } else if (kind.equals(AbstractDocument.SectionElementName)) { + return new BoxView(elem, View.Y_AXIS); + } else if (kind.equals(StyleConstants.ComponentElementName)) { + return new ComponentView(elem); + } else if (kind.equals(StyleConstants.IconElementName)) { + return new IconView(elem); + } + } + + // default to text display + return new LabelView(elem); + } + } + + class WrapLabelView extends LabelView { + public WrapLabelView(Element elem) { + super(elem); + } + + public float getMinimumSpan(int axis) { + switch (axis) { + case View.X_AXIS: + return 0; + case View.Y_AXIS: + return super.getMinimumSpan(axis); + default: + throw new IllegalArgumentException("Invalid axis: " + axis); + } + } + + } +} \ No newline at end of file diff --git a/POO/src/standard/ControleurStandard.java b/POO/src/standard/ControleurStandard.java index dc3fe1a..58bc043 100644 --- a/POO/src/standard/ControleurStandard.java +++ b/POO/src/standard/ControleurStandard.java @@ -10,125 +10,146 @@ import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.InetAddress; import java.net.Socket; +import java.net.UnknownHostException; import java.util.ArrayList; import javax.swing.JButton; import javax.swing.JList; +import javax.swing.WindowConstants; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; -import communication.CommunicationUDP; -import communication.TCPServer; +import communication.tcp.TCPServer; +import communication.udp.CommunicationUDP; import connexion.VueConnexion; -import main.Observer; +import database.SQLiteManager; import main.Utilisateur; -import standard.VueStandard; +import observers.ObserverInputMessage; +import observers.ObserverSocketState; +import observers.ObserverUserList; import session.VueSession; -public class ControleurStandard implements ActionListener, ListSelectionListener, WindowListener, Observer { +public class ControleurStandard implements ActionListener, ListSelectionListener, WindowListener, ObserverInputMessage, + ObserverUserList, ObserverSocketState { - private enum EtatModif { + private enum ModifPseudo { TERMINE, EN_COURS } - private EtatModif etatModif; + private ModifPseudo modifPseudo; private VueStandard vue; private CommunicationUDP commUDP; private String lastPseudo; private TCPServer tcpServ; + private ArrayList idsSessionEnCours; + private SQLiteManager sqlManager; + private VueConnexion vueConnexion; - public ControleurStandard(VueStandard vue, CommunicationUDP commUDP, int portServerTCP) throws IOException { + public ControleurStandard(VueStandard vue, CommunicationUDP commUDP, int portServerTCP, SQLiteManager sqlManager, + VueConnexion vueConnexion) throws IOException { this.vue = vue; + this.vue.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + + this.vueConnexion = vueConnexion; this.tcpServ = new TCPServer(portServerTCP); + this.tcpServ.addObserver(this); this.tcpServ.start(); - + this.commUDP = commUDP; - this.commUDP.setObserver(this); - this.commUDP.sendMessageConnecte(); - this.commUDP.sendMessageInfoPseudo(); - this.etatModif = EtatModif.TERMINE; + this.idsSessionEnCours = new ArrayList(); + + this.sqlManager = sqlManager; } - //---------- LISTSELECTION LISTENER OPERATIONS ----------// + // ---------- LISTSELECTION LISTENER OPERATIONS ----------// @Override public void valueChanged(ListSelectionEvent e) { - - //Cas où un élément de la liste est sélectionné - if (this.vue.getActiveUsersList().isFocusOwner() && !e.getValueIsAdjusting()) { - + + // Case when a list element is selected + if (this.vue.getActiveUsersList().isFocusOwner() && !e.getValueIsAdjusting() + && this.vue.getActiveUsersList().getSelectedValue() != null) { + JList list = this.vue.getActiveUsersList(); - String pseudo = list.getSelectedValue(); - - int choix = this.vue.displayJOptionCreation(pseudo); - System.out.println("choix : "+choix); - if(choix == 0) { - - int tcpServerPort = this.commUDP.getPortFromPseudo(pseudo); - System.out.println("port = "+tcpServerPort); - try { - - //Send this user's pseudonyme through a TCP-client socket - //to the TCP-server socket of the selected user - Socket socketComm = new Socket(InetAddress.getLocalHost(), tcpServerPort); - this.sendMessage(socketComm, Utilisateur.getSelf().getPseudo()); - - //Wait for the answer, either "accepted or refused" - String reponse = this.readMessage(socketComm); + String pseudoOther = list.getSelectedValue(); + Utilisateur other = this.commUDP.getUserFromPseudo(pseudoOther); + String idOther = other.getId(); + + // Check if we are already asking for a session/chatting with the person + // selected + // null condition because the list.clearSelection() generates an event + if (!this.idsSessionEnCours.contains(idOther)) { + + int choix = this.vue.displayJOptionSessionCreation(pseudoOther); + System.out.println("choix : " + choix); + + if (choix == 0) { - System.out.println("reponse : " + reponse); + int port = other.getPort(); - if(reponse.contains("accepted")) { + System.out.println("port = " + port); + try { + + Socket socketComm = new Socket(InetAddress.getLocalHost(), port); - this.vue.displayJOptionResponse("acceptée"); - this.vue.addSession(pseudo, new VueSession(socketComm)); - - }else{ - this.vue.displayJOptionResponse("refusée"); - socketComm.close(); - System.out.println("refused"); + this.sendMessage(socketComm, Utilisateur.getSelf().getPseudo()); + String reponse = this.readMessage(socketComm); + + System.out.println("reponse : " + reponse); + + if (reponse.equals("accepted")) { + this.idsSessionEnCours.add(idOther); + + VueSession session = new VueSession(socketComm, idOther, pseudoOther, this.sqlManager); + this.vue.addSession(pseudoOther, session); + + this.vue.displayJOptionResponse("acceptee"); + + } else { + this.vue.displayJOptionResponse("refusee"); + socketComm.close(); + System.out.println("refused"); + } + + } catch (IOException e1) { + vue.displayJOptionResponse("refusee"); } - - } catch (IOException e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); } - + } - - this.vue.getButtonModifierPseudo().requestFocus(); - System.out.println("pseudo de la personne a atteindre : " + pseudo); - + + list.clearSelection(); + System.out.println("pseudo de la personne a atteindre : " + pseudoOther); + } } - - //---------- ACTION LISTENER OPERATIONS ----------// + // ---------- ACTION LISTENER OPERATIONS ----------// @Override public void actionPerformed(ActionEvent e) { - - //Cas Modifier Pseudo + + // Cas Modifier Pseudo if ((JButton) e.getSource() == this.vue.getButtonModifierPseudo()) { JButton modifierPseudo = (JButton) e.getSource(); - if (this.etatModif == EtatModif.TERMINE) { + if (this.modifPseudo == ModifPseudo.TERMINE) { this.lastPseudo = Utilisateur.getSelf().getPseudo(); modifierPseudo.setText("OK"); - this.etatModif = EtatModif.EN_COURS; + this.modifPseudo = ModifPseudo.EN_COURS; } else { - if (!this.commUDP.containsUserFromPseudo(this.vue.getDisplayedPseudo())) { + if (this.vue.getDisplayedPseudo().length() >= 1 + && !this.commUDP.containsUserFromPseudo(this.vue.getDisplayedPseudo().toLowerCase())) { Utilisateur.getSelf().setPseudo(this.vue.getDisplayedPseudo()); try { this.commUDP.sendMessageInfoPseudo(); } catch (IOException e1) { - // TODO Auto-generated catch block e1.printStackTrace(); } @@ -137,139 +158,168 @@ public class ControleurStandard implements ActionListener, ListSelectionListener } modifierPseudo.setText("Modifier"); - this.etatModif = EtatModif.TERMINE; + this.modifPseudo = ModifPseudo.TERMINE; } this.vue.toggleEditPseudo(); } - - - - //Cas deconnexion - else if((JButton) e.getSource() == this.vue.getButtonDeconnexion() ) { + + // Cas deconnexion + else if ((JButton) e.getSource() == this.vue.getButtonDeconnexion()) { try { - this.commUDP.sendMessageDelete(); - this.commUDP.removeAll(); - this.vue.removeAllUsers(); - Utilisateur.resetSelf(); - vue.dispose(); - new VueConnexion(5); -// this.vue.toggleEnableButtonConnexion(); -// this.vue.toggleEnableButtonDeconnexion(); + this.setVueConnexion(); } catch (IOException e1) { - + e1.printStackTrace(); } } - - - } - - //---------- WINDOW LISTENER OPERATIONS ----------// + else if (this.vue.isButtonTab(e.getSource())) { + JButton button = (JButton) e.getSource(); + int index = this.vue.removeSession(button); + this.idsSessionEnCours.remove(index); + } + } + + // ---------- WINDOW LISTENER OPERATIONS ----------// @Override public void windowClosing(WindowEvent e) { - + try { - this.commUDP.sendMessageDelete(); + this.setVueConnexion(); } catch (IOException e1) { - // TODO Auto-generated catch block e1.printStackTrace(); } - + } - + @Override public void windowOpened(WindowEvent e) { // TODO Auto-generated method stub - + } @Override public void windowClosed(WindowEvent e) { - // TODO Auto-generated method stub - + } @Override public void windowIconified(WindowEvent e) { // TODO Auto-generated method stub - + } @Override public void windowDeiconified(WindowEvent e) { // TODO Auto-generated method stub - + } @Override public void windowActivated(WindowEvent e) { // TODO Auto-generated method stub - + } @Override public void windowDeactivated(WindowEvent e) { // TODO Auto-generated method stub - + } + // ------------SOCKET-------------// + + private void sendMessage(Socket sock, String message) throws IOException { + PrintWriter output = new PrintWriter(sock.getOutputStream(), true); + output.println(message); + } + + private String readMessage(Socket sock) throws IOException { + + BufferedReader input = new BufferedReader(new InputStreamReader(sock.getInputStream())); + return input.readLine(); + } + + // ------------OBSERVERS-------------// + @Override public void update(Object o, Object arg) { - - if(o == this.tcpServ) { - + + if (o == this.tcpServ) { + Socket sockAccept = (Socket) arg; - + try { - - String pseudo = this.readMessage(sockAccept); - - int reponse = this.vue.displayJOptionDemande(pseudo); - - System.out.println("reponse : " + reponse); - - if(reponse == 0) { + + String pseudoOther = this.readMessage(sockAccept); + String idOther = this.commUDP.getUserFromPseudo(pseudoOther).getId(); + + int reponse; + + if (!this.idsSessionEnCours.contains(idOther)) { + reponse = this.vue.displayJOptionAskForSession(pseudoOther); + System.out.println("reponse : " + reponse); + } else { + reponse = 1; + } + + if (reponse == 0) { + + this.idsSessionEnCours.add(idOther); this.sendMessage(sockAccept, "accepted"); - this.vue.addSession(pseudo, new VueSession(sockAccept)); - }else { + + VueSession session = new VueSession(sockAccept, idOther, pseudoOther, this.sqlManager); + this.vue.addSession(pseudoOther, session); + } else { this.sendMessage(sockAccept, "refused"); sockAccept.close(); } - - + } catch (IOException e) { e.printStackTrace(); } } - - if(o == this.commUDP) { - ArrayList users = (ArrayList) arg; + } + + @Override + public void updateList(Object o, ArrayList userList) { + + if (o == this.commUDP) { ArrayList pseudos = new ArrayList(); - for (Utilisateur u : users) { + for (Utilisateur u : userList) { pseudos.add(u.getPseudo()); } this.vue.setActiveUsersList(pseudos); } - - } - - - private void sendMessage(Socket sock, String message) throws IOException { - PrintWriter output= new PrintWriter(sock.getOutputStream(), true); - output.println(message); - } - - private String readMessage(Socket sock) throws IOException { - - BufferedReader input = new BufferedReader(new InputStreamReader( sock.getInputStream() )); - char buffer[] = new char[25]; - input.read(buffer); - - String reponse = new String(buffer).split("\n")[0]; - return reponse; } -} + @Override + public void updateSocketState(Object o, Object arg) { + VueSession session = (VueSession) arg; + int index = this.vue.removeSession(session); + this.idsSessionEnCours.remove(index); + } + + private void setVueConnexion() throws UnknownHostException, IOException { + this.commUDP.sendMessageDelete(); + this.vue.removeAllUsers(); + this.vue.closeAllSession(); + this.idsSessionEnCours.clear(); + Utilisateur.resetSelf(); + this.commUDP.setObserver(null); + + this.vue.setVisible(false); + this.vueConnexion.setVisible(true); + } + + protected void init() throws UnknownHostException, IOException { + this.commUDP.setObserver(this); + this.commUDP.sendMessageConnecte(); + this.commUDP.sendMessageInfoPseudo(); + this.modifPseudo = ModifPseudo.TERMINE; + } + + +} \ No newline at end of file diff --git a/POO/src/standard/VueStandard.java b/POO/src/standard/VueStandard.java index 23038c8..11c0995 100644 --- a/POO/src/standard/VueStandard.java +++ b/POO/src/standard/VueStandard.java @@ -1,19 +1,20 @@ package standard; import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; - +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; import java.io.IOException; +import java.net.UnknownHostException; import java.util.ArrayList; -import java.util.HashMap; +import java.util.Iterator; +import javax.swing.AbstractButton; import javax.swing.BorderFactory; import javax.swing.DefaultListModel; import javax.swing.JButton; -import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; @@ -25,251 +26,348 @@ import javax.swing.ListSelectionModel; import javax.swing.ScrollPaneConstants; import javax.swing.plaf.basic.BasicButtonUI; -import communication.CommunicationUDP; +import communication.udp.CommunicationUDP; +import connexion.VueConnexion; +import database.SQLiteManager; import main.Utilisateur; import main.Vue; import session.VueSession; public class VueStandard extends Vue { - + /** * */ private static final long serialVersionUID = 1L; - + private JList activeUsersList; private JTextField pseudoSelf; private JTabbedPane zoneSessions; private JButton modifierPseudo; + private JButton seConnecter; private JButton seDeconnecter; private ControleurStandard c; - - private ArrayList closeButtonSessions; - private ArrayList pseudoSessions; - + private ArrayList tabButtons; + private ArrayList sessions; private DefaultListModel userList = new DefaultListModel(); - - - public VueStandard(String title, CommunicationUDP commUDP, int portServerTCP) throws IOException { + + public VueStandard(String title, CommunicationUDP commUDP, int portServerTCP, SQLiteManager sqlManager, VueConnexion vueConnexion) throws IOException { super(title); - - this.closeButtonSessions = new ArrayList(); - this.c = new ControleurStandard(this, commUDP, portServerTCP); - - + + this.tabButtons = new ArrayList(); + this.sessions = new ArrayList(); + this.c = new ControleurStandard(this, commUDP, portServerTCP, sqlManager, vueConnexion); + this.c.init(); + getContentPane().setLayout(new GridBagLayout()); - - + JPanel left = new JPanel(new BorderLayout()); - left.setBackground(Color.red); - //left.setPreferredSize(new Dimension(200, 200)); - + this.zoneSessions = new JTabbedPane(); this.zoneSessions.setTabPlacement(JTabbedPane.BOTTOM); - - //JPanel defaultTab = new JPanel(new GridLayout(1,1)); - - //JLabel noSession = new JLabel("Aucune session en cours"); - //noSession.setHorizontalAlignment(JLabel.CENTER); - //defaultTab.add(noSession); - - //this.zoneSessions.addTab("1", defaultTab); - - this.zoneSessions.setBackground(Color.green); + this.zoneSessions.setPreferredSize(new Dimension(600, 600)); - - - - //--------Panel haut pseudo--------// + + // --------Panel haut pseudo--------// JPanel self = new JPanel(new FlowLayout()); - + this.pseudoSelf = new JTextField(Utilisateur.getSelf().getPseudo()); - this.pseudoSelf.setPreferredSize(new Dimension(100, 20)); + this.pseudoSelf.setPreferredSize(new Dimension(100, 30)); this.pseudoSelf.setEditable(false); this.pseudoSelf.setFocusable(false); - + this.pseudoSelf.setEnabled(false); + this.modifierPseudo = new JButton("Modifier"); - this.modifierPseudo.addActionListener(this.c); - + this.modifierPseudo.addActionListener(this.c); + self.add(new JLabel("Moi : ")); self.add(this.pseudoSelf); self.add(this.modifierPseudo); this.pseudoSelf.addKeyListener(new KeyListener() { - + @Override - public void keyTyped(KeyEvent e) { + public void keyTyped(KeyEvent e) { } - + @Override - public void keyReleased(KeyEvent e) { + public void keyReleased(KeyEvent e) { } - + @Override public void keyPressed(KeyEvent e) { - if(e.getKeyCode() == KeyEvent.VK_ENTER) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) { modifierPseudo.doClick(); } } }); - - //--------Panel milieu liste utilisateurs--------// + + // --------Panel milieu liste utilisateurs--------// this.activeUsersList = new JList(this.userList); this.activeUsersList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); this.activeUsersList.setLayoutOrientation(JList.VERTICAL); this.activeUsersList.addListSelectionListener(this.c); - + System.out.println("listener ajouté"); - + JScrollPane listScroller = new JScrollPane(this.activeUsersList); - listScroller.setPreferredSize(new Dimension(50,50)); + listScroller.setPreferredSize(new Dimension(50, 50)); listScroller.setAlignmentX(LEFT_ALIGNMENT); listScroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); listScroller.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createTitledBorder("Utilisateurs Actifs"), - BorderFactory.createEmptyBorder(5,2,2,2))); - - - //--------Panel bas deconnexion--------// + BorderFactory.createTitledBorder("Utilisateurs Actifs"), BorderFactory.createEmptyBorder(5, 2, 2, 2))); + + // --------Panel bas deconnexion--------// JPanel deconnexion = new JPanel(new GridLayout(1, 2)); - + this.seDeconnecter = new JButton("Se Déconnecter"); this.seDeconnecter.addActionListener(this.c); deconnexion.add(this.seDeconnecter); - //--------Ajout à la vue--------// + if(Utilisateur.getSelf().getId() == "admin") { + JButton addNewUser = new JButton("Ajouter un nouvel utilisateur"); + deconnexion.add(addNewUser); + } + + // --------Ajout à la vue--------// left.add(self, BorderLayout.PAGE_START); left.add(listScroller, BorderLayout.CENTER); left.add(deconnexion, BorderLayout.PAGE_END); - - - - GridBagConstraints gridBagConstraint1 = new GridBagConstraints(); - GridBagConstraints gridBagConstraint2 = new GridBagConstraints(); - - gridBagConstraint1.fill = GridBagConstraints.BOTH; - gridBagConstraint1.gridx = 0; - gridBagConstraint1.gridy = 0; - gridBagConstraint1.gridwidth = 1; - gridBagConstraint1.gridheight = 4; - gridBagConstraint1.weightx = 0.33; - gridBagConstraint1.weighty = 1; - - getContentPane().add(left,gridBagConstraint1); - - gridBagConstraint2.fill = GridBagConstraints.BOTH; - gridBagConstraint2.gridx = 1; - gridBagConstraint2.gridy = 0; - gridBagConstraint2.gridwidth = 2; - gridBagConstraint2.gridheight = 4; - gridBagConstraint2.weightx = 0.66; - gridBagConstraint2.weighty = 1; - - getContentPane().add(this.zoneSessions,gridBagConstraint2); - - + + GridBagConstraints gridBagConstraintLeft = new GridBagConstraints(); + GridBagConstraints gridBagConstraintSessions = new GridBagConstraints(); + + gridBagConstraintLeft.fill = GridBagConstraints.BOTH; + gridBagConstraintLeft.gridx = 0; + gridBagConstraintLeft.gridy = 0; + gridBagConstraintLeft.gridwidth = 1; + gridBagConstraintLeft.gridheight = 4; + gridBagConstraintLeft.weightx = 0.33; + gridBagConstraintLeft.weighty = 1; + + getContentPane().add(left, gridBagConstraintLeft); + + gridBagConstraintSessions.fill = GridBagConstraints.BOTH; + gridBagConstraintSessions.gridx = 1; + gridBagConstraintSessions.gridy = 0; + gridBagConstraintSessions.gridwidth = 2; + gridBagConstraintSessions.gridheight = 4; + gridBagConstraintSessions.weightx = 0.66; + gridBagConstraintSessions.weighty = 1; + + getContentPane().add(this.zoneSessions, gridBagConstraintSessions); + this.pack(); this.setVisible(true); - + this.addWindowListener(c); } - - protected JList getActiveUsersList(){ + + // ------------ GETTERS -------------// + + protected JList getActiveUsersList() { return this.activeUsersList; } - + + protected JButton getButtonModifierPseudo() { + return this.modifierPseudo; + } + + protected JButton getButtonDeconnexion() { + return this.seDeconnecter; + } + + protected JButton getButtonConnexion() { + return this.seConnecter; + } + + protected String getDisplayedPseudo() { + return this.pseudoSelf.getText(); + } + + // ------------ SETTERS -------------// + protected void setActiveUsersList(ArrayList users) { this.removeAllUsers(); this.userList.addAll(users); } - - - protected void removeAllUsers() { - this.userList.removeAllElements(); - } - - - protected JButton getButtonModifierPseudo() { - return this.modifierPseudo; - } - - protected JButton getButtonDeconnexion() { - return this.seDeconnecter; - } - - - protected String getDisplayedPseudo() { - return this.pseudoSelf.getText(); - } - + protected void setDisplayedPseudo(String pseudo) { this.pseudoSelf.setText(pseudo); } + public void setPseudoSelf() { + this.setDisplayedPseudo(Utilisateur.getSelf().getPseudo()); + } + + // ------------ JOPTIONS -------------// + + protected int displayJOptionSessionCreation(String pseudo) { + return JOptionPane.showConfirmDialog(this, "Voulez vous créer une session avec " + pseudo + " ?", + "Confirmation session", JOptionPane.YES_NO_OPTION); + } + + protected int displayJOptionAskForSession(String pseudo) { + return JOptionPane.showConfirmDialog(this, pseudo + " souhaite creer une session avec vous.", + "Accepter demande", JOptionPane.YES_NO_OPTION); + } + + protected void displayJOptionResponse(String reponse) { + JOptionPane.showMessageDialog(this, "Demande de session " + reponse); + } + + // ------------ TOGGLEBUTTONS -------------// + protected void toggleEditPseudo() { this.pseudoSelf.setEditable(!this.pseudoSelf.isEditable()); this.pseudoSelf.setFocusable(!this.pseudoSelf.isFocusable()); + this.pseudoSelf.setEnabled(!this.pseudoSelf.isEnabled()); } - + protected void toggleEnableButtonDeconnexion() { this.seDeconnecter.setEnabled(!this.seDeconnecter.isEnabled()); } - - - protected int displayJOptionCreation(String pseudo) { - return JOptionPane.showConfirmDialog(this, - "Voulez vous créer une session avec "+pseudo+" ?", - "Confirmation session", - JOptionPane.YES_NO_OPTION); + + protected void toggleEnableButtonConnexion() { + this.seConnecter.setEnabled(!this.seConnecter.isEnabled()); } - - protected int displayJOptionDemande(String pseudo) { - return JOptionPane.showConfirmDialog(this, - pseudo+" souhaite creer une session avec vous.", - "Accepter demande", - JOptionPane.YES_NO_OPTION); + + // ------------SESSION-------------// + + protected boolean isButtonTab(Object o) { + return this.tabButtons.contains(o); } - - protected void displayJOptionResponse(String reponse) { - JOptionPane.showMessageDialog(this, "Demande de session "+reponse); + + protected int removeSession(JButton button) { + int index = this.tabButtons.indexOf(button); + + VueSession vue = this.sessions.get(index); + vue.destroyAll(); + + this.zoneSessions.remove(vue); + this.sessions.remove(index); + this.tabButtons.remove(index); + + return index; } - + protected void addSession(String pseudo, VueSession session) { -// if(nbTab == 1) { -// this.zoneSessions.removeTabAt(0); -// } - JPanel tabTitle = new JPanel(); - - JButton closeTab = new JButton("X"); - closeTab.setToolTipText("close this tab"); - //Make the button looks the same for all Laf's - closeTab.setUI(new BasicButtonUI()); - //Make it transparent - closeTab.setContentAreaFilled(false); - closeTab.addActionListener(new ActionListener() { - - @Override - public void actionPerformed(ActionEvent e) { - JButton button = (JButton) e.getSource(); - boolean containsKey = closeButtonSessions.contains(button); - if (containsKey) { - zoneSessions.remove(closeButtonSessions.indexOf(button) ); - closeButtonSessions.remove(button); - } - } - }); - - + + TabButton closeTab = new TabButton(); + tabTitle.add(new JLabel(pseudo)); tabTitle.add(closeTab); - + this.zoneSessions.addTab(pseudo, session); - this.zoneSessions.setTabComponentAt(this.zoneSessions.getTabCount()-1, tabTitle); - - this.closeButtonSessions.add(closeTab); + this.zoneSessions.setTabComponentAt(this.zoneSessions.getTabCount() - 1, tabTitle); + + this.tabButtons.add(closeTab); + this.sessions.add(session); + session.requestFocus(); - + } + protected synchronized int removeSession(VueSession vue) { + int index = this.sessions.indexOf(vue); + + vue.destroyAll(); + + this.zoneSessions.remove(vue); + this.sessions.remove(index); + this.tabButtons.remove(index); + + return index; + } -} + protected void closeAllSession() { + Iterator it = this.sessions.iterator(); + while(it.hasNext()) { + VueSession session = it.next(); + this.zoneSessions.remove(session); + session.destroyAll(); + it.remove(); + } + + this.tabButtons.clear(); + } + + + // ------------ OTHERS -------------// + + protected void removeAllUsers() { + this.userList.removeAllElements(); + } + + public void initControleur() throws UnknownHostException, IOException { + this.c.init(); + } + + // ------------- PRIVATE CLASSES FOR THE TABS BUTTON -------------// + private class TabButton extends JButton { + + private static final long serialVersionUID = 1L; + + public TabButton() { + int size = 17; + setPreferredSize(new Dimension(size, size)); + setToolTipText("close this tab"); + // Make the button looks the same for all Laf's + setUI(new BasicButtonUI()); + // Make it transparent + setContentAreaFilled(false); + // No need to be focusable + setFocusable(false); + setBorder(BorderFactory.createEtchedBorder()); + setBorderPainted(false); + + addMouseListener(VueStandard.buttonMouseListener); + // Making nice rollover effect + // we use the same listener for all buttons + addActionListener(c); + setRolloverEnabled(true); + } + + // we don't want to update UI for this button + public void updateUI() { + } + + // paint the cross + protected void paintComponent(Graphics g) { + super.paintComponent(g); + Graphics2D g2 = (Graphics2D) g.create(); + // shift the image for pressed buttons + if (getModel().isPressed()) { + g2.translate(1, 1); + } + g2.setStroke(new BasicStroke(2)); + g2.setColor(Color.BLACK); + if (getModel().isRollover()) { + g2.setColor(Color.MAGENTA); + } + int delta = 6; + g2.drawLine(delta, delta, getWidth() - delta - 1, getHeight() - delta - 1); + g2.drawLine(getWidth() - delta - 1, delta, delta, getHeight() - delta - 1); + g2.dispose(); + } + } + + private final static MouseListener buttonMouseListener = new MouseAdapter() { + public void mouseEntered(MouseEvent e) { + Component component = e.getComponent(); + if (component instanceof AbstractButton) { + AbstractButton button = (AbstractButton) component; + button.setBorderPainted(true); + } + } + + public void mouseExited(MouseEvent e) { + Component component = e.getComponent(); + if (component instanceof AbstractButton) { + AbstractButton button = (AbstractButton) component; + button.setBorderPainted(false); + } + } + }; + +} \ No newline at end of file