diff --git a/POO/src/communication/Communication.java b/POO/src/communication/Communication.java deleted file mode 100644 index 9ae8748..0000000 --- a/POO/src/communication/Communication.java +++ /dev/null @@ -1,106 +0,0 @@ -package communication; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.List; - -import main.Utilisateur; -import standard.VueStandard; - -public class Communication extends Thread{ - protected static ArrayList users = new ArrayList(); - - protected static boolean containsUserFromID(String id) { - for(Utilisateur u : Communication.users) { - if(u.getId().equals(id) ) { - return true; - } - } - - return false; - } - - public static boolean containsUserFromPseudo(String pseudo) { - for(Utilisateur u : Communication.users) { - if(u.getPseudo().equals(pseudo) ) { - return true; - } - } - - return false; - } - - public static InetAddress getIPFromPseudo(String pseudo) { - int index = Communication.getIndexFromPseudo(pseudo); - if (index != -1) { - return Communication.users.get(index).getIp(); - } - return null; - } - - public static int getPortFromPseudo(String pseudo) { - int index = Communication.getIndexFromPseudo(pseudo); - if (index != -1) { - return Communication.users.get(index).getPort(); - } - return -1; - } - - protected static int getIndexFromID(String id) { - for(int i=0; i < Communication.users.size() ; i++) { - if(Communication.users.get(i).getId().equals(id) ) { - return i; - } - } - return -1; - } - - protected static int getIndexFromIP(InetAddress ip) { - for(int i=0; i < Communication.users.size() ; i++) { - if(Communication.users.get(i).getIp().equals(ip)) { - return i; - } - } - return -1; - } - - protected static int getIndexFromPseudo(String pseudo) { - for(int i=0; i < Communication.users.size() ; i++) { - if(Communication.users.get(i).getPseudo().equals(pseudo)) { - return i; - } - } - return -1; - } - - - protected static synchronized void addUser(String idClient, String pseudoClient, InetAddress ipClient, int portTCP) throws UnknownHostException { - System.out.println("port de " +pseudoClient+" : "+ portTCP); - Communication.users.add(new Utilisateur(idClient, pseudoClient, ipClient, portTCP)); - VueStandard.userList.addElement(pseudoClient); - } - - protected static synchronized void changePseudoUser(String idClient, String pseudoClient, InetAddress ipClient, int port) { - int index = Communication.getIndexFromID(idClient); - Communication.users.get(index).setPseudo(pseudoClient); - VueStandard.userList.set(index, pseudoClient); - } - - - protected static synchronized void removeUser(String idClient, String pseudoClient,InetAddress ipClient, int port) { - int index = Communication.getIndexFromIP(ipClient); - if( index != -1) { - Communication.users.remove(index); - VueStandard.userList.remove(index); - } - } - - public static void removeAll(){ - int oSize = Communication.users.size(); - for(int i=0; i 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..def336a --- /dev/null +++ b/POO/src/communication/filetransfer/FileTransferReceivingThread.java @@ -0,0 +1,80 @@ +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(); + 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..b37f5bd --- /dev/null +++ b/POO/src/communication/filetransfer/FileTransferUtils.java @@ -0,0 +1,112 @@ +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; + } +} diff --git a/POO/src/communication/TCPClient.java b/POO/src/communication/tcp/TCPClient.java similarity index 50% rename from POO/src/communication/TCPClient.java rename to POO/src/communication/tcp/TCPClient.java index 4037260..eabc7a7 100644 --- a/POO/src/communication/TCPClient.java +++ b/POO/src/communication/tcp/TCPClient.java @@ -1,18 +1,20 @@ -package communication; +package communication.tcp; -import java.io.BufferedReader; +import java.io.File; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; + import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import java.io.OutputStream; -import java.io.PrintWriter; + import java.net.InetAddress; import java.net.Socket; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + +import observers.ObserverInputMessage; +import observers.ObserverSocketState; -import main.Observer; -//import main.VueSession; import messages.MessageTexte; import messages.MauvaisTypeMessageException; import messages.Message; @@ -24,30 +26,55 @@ public class TCPClient { private ObjectOutputStream output; private TCPInputThread inputThread; + public TCPClient(Socket sockTCP) throws IOException { this.sockTCP = sockTCP; - this.output = new ObjectOutputStream(sockTCP.getOutputStream()) ; + 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 TCPClient(InetAddress addr, int port) throws IOException { + this(new Socket(addr, port)); + + } + public void startInputThread() { this.inputThread.start(); } + - public void sendMessage(String contenu) throws IOException, MauvaisTypeMessageException { + public void sendMessage(Message message) throws IOException, MauvaisTypeMessageException { System.out.println("dans write"); - MessageTexte message = new MessageTexte(TypeMessage.TEXTE, contenu); this.output.writeObject(message); } + - public void setObserverInputThread(Observer o) { - this.inputThread.setObserver(o); + 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/TCPServer.java b/POO/src/communication/tcp/TCPServer.java similarity index 82% rename from POO/src/communication/TCPServer.java rename to POO/src/communication/tcp/TCPServer.java index 71ddf0f..dbd1d34 100644 --- a/POO/src/communication/TCPServer.java +++ b/POO/src/communication/tcp/TCPServer.java @@ -1,4 +1,4 @@ -package communication; +package communication.tcp; import java.io.IOException; import java.net.InetAddress; @@ -6,13 +6,13 @@ import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; -import main.Observer; +import observers.ObserverInputMessage; public class TCPServer extends Thread { private ServerSocket sockListenTCP; - private Observer obs; + private ObserverInputMessage obs; public TCPServer(int port) throws UnknownHostException, IOException { this.sockListenTCP = new ServerSocket(port, 5, InetAddress.getLocalHost()); @@ -34,7 +34,7 @@ public class TCPServer extends Thread { } } - public void addObserver(Observer obs) { + public void addObserver(ObserverInputMessage obs) { this.obs = obs; } } diff --git a/POO/src/communication/CommunicationUDP.java b/POO/src/communication/udp/CommunicationUDP.java similarity index 85% rename from POO/src/communication/CommunicationUDP.java rename to POO/src/communication/udp/CommunicationUDP.java index 3ea40a4..46a369f 100644 --- a/POO/src/communication/CommunicationUDP.java +++ b/POO/src/communication/udp/CommunicationUDP.java @@ -1,4 +1,4 @@ -package communication; +package communication.udp; import java.io.IOException; import java.net.InetAddress; @@ -6,10 +6,10 @@ import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; -import main.Observer; import main.Utilisateur; import standard.VueStandard; import messages.*; +import observers.ObserverUserList; public class CommunicationUDP extends Thread { @@ -19,8 +19,8 @@ public class CommunicationUDP extends Thread { private UDPClient client; private int portServer; private ArrayList portOthers; - private static ArrayList users = new ArrayList(); - private Observer observer; + private ArrayList users = new ArrayList(); + private ObserverUserList observer; public CommunicationUDP(int portClient, int portServer, int[] portsOther) throws IOException { this.portServer = portServer; @@ -29,7 +29,6 @@ public class CommunicationUDP extends Thread { this.client = new UDPClient(portClient); } - private ArrayList getArrayListFromArray(int ports[]) { ArrayList tmp = new ArrayList(); for (int port : ports) { @@ -40,11 +39,44 @@ public class CommunicationUDP extends Thread { return tmp; } - public void setObserver (Observer obs) { + public void setObserver (ObserverUserList obs) { this.observer=obs; } - protected static boolean containsUserFromID(String id) { + + //-------------- 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)); + observer.updateList(this, users); + + } + + protected synchronized void changePseudoUser(String idClient, String pseudoClient, InetAddress ipClient, int port) { + int index = getIndexFromID(idClient); + users.get(index).setPseudo(pseudoClient); + observer.updateList(this, users); + } + + protected synchronized void removeUser(String idClient, String pseudoClient,InetAddress ipClient, int port) { + int index = getIndexFromIP(ipClient); + if( index != -1) { + users.remove(index); + } + observer.updateList(this, users); + } + + public void removeAll(){ + int oSize = users.size(); + for(int i=0; i 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(); + } catch (SQLException e) { + System.out.println("Nom d'utilisateur déjà pris"); + } + + this.closeConnection(); + + } + + + 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 = null; + try { + expectedHash = SQLiteEncprytion.decryptByte(SQLiteEncprytion.encryptAlgorithm, encryptedPasswordHash, dbDataKey, iv); + } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException + | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + 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 username = "Mirasio" ; + String password = "12345" ; + SQLiteManager sqlManager = new SQLiteManager(1); + sqlManager.createNewUserEncrypt(username, password); + try { + + int returnVal = sqlManager.checkPwd(username, password.toCharArray()); + + if(returnVal == -1) { + System.out.println("utilisateur inexistant"); + }else if(returnVal == 0) { + System.out.println("mot de passe incorrect"); + }else { + System.out.println("mot de passe correct"); + } + + } catch (SQLException e) { + System.out.println("erreur recherche utilisateur"); + e.printStackTrace(); + } + } + +} diff --git a/POO/src/main/Main.java b/POO/src/main/Main.java index 8df7e96..7e9a41f 100644 --- a/POO/src/main/Main.java +++ b/POO/src/main/Main.java @@ -1,9 +1,14 @@ package main; import java.io.IOException; +import java.sql.SQLException; +import database.SQLiteManager; import standard.VueStandard; +import javax.swing.UIManager; +import javax.swing.UIManager.*; + public class Main { @@ -11,10 +16,24 @@ public class Main { private static int portServersUDP[] = {1526,1501,1551,1561}; private static String ids[] = {"Raijila", "titi33", "Semtexx", "Salam"}; private static String pseudo[] = {"Raijila", "Mirasio", "Semtexx", "Xaegon"}; + private static String pwd[] = {"azertyuiop","12345","abcde","toto"}; private static int portServersTCP[] = {1625,1600,1650,1660}; public static void main(String[] args) { + + + try { + for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) { + if ("Nimbus".equals(info.getName())) { + UIManager.setLookAndFeel(info.getClassName()); + break; + } + } + } catch (Exception e) { + // If Nimbus is not available, you can set the GUI to another look and feel. + } + switch(args[0]) { case "0": Main.createApp(0); @@ -28,27 +47,21 @@ public class Main { default: Main.createApp(3); } - - - - -// VueStandard.userList.addElement("Mirasio"); -// -// try { -// Thread.sleep(2000); -// VueStandard.userList.addElement("Semtexx"); -// } catch (InterruptedException e) { -// // TODO Auto-generated catch block -// e.printStackTrace(); -// } - } private static void createApp(int i) { try { Utilisateur.setSelf(Main.ids[i], Main.pseudo[i], "localhost", Main.portServersTCP[i]); - new VueStandard("Application", Main.portServersUDP[i]-1, Main.portServersUDP[i], Main.portServersUDP, Main.portServersTCP[i]); + SQLiteManager sqlManager = new SQLiteManager(i); + try { + sqlManager.createNewUserEncrypt(Main.ids[i], Main.pwd[i]); + sqlManager.checkPwd(Main.ids[i], Main.pwd[i].toCharArray()); + } catch (SQLException e) { + System.out.println("erreur recherche utilisateur"); + e.printStackTrace(); + } + new VueStandard("Application", Main.portServersUDP[i]-1, Main.portServersUDP[i], Main.portServersUDP, Main.portServersTCP[i], sqlManager); } catch (IOException e) { System.out.println(e.toString()); } diff --git a/POO/src/main/Observer.java b/POO/src/main/Observer.java deleted file mode 100644 index c95af4d..0000000 --- a/POO/src/main/Observer.java +++ /dev/null @@ -1,6 +0,0 @@ -package main; - -public interface Observer { - - public void update(Object o, Object arg); -} diff --git a/POO/src/messages/Message.java b/POO/src/messages/Message.java index 6036b00..d328385 100644 --- a/POO/src/messages/Message.java +++ b/POO/src/messages/Message.java @@ -2,20 +2,50 @@ package messages; import java.io.Serializable; import java.lang.instrument.Instrumentation; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.Arrays; import messages.Message.TypeMessage; public abstract class Message implements Serializable { - public enum TypeMessage {JE_SUIS_CONNECTE, JE_SUIS_DECONNECTE, INFO_PSEUDO, TEXTE, IMAGE, FICHIER, MESSAGE_NUL} + public enum TypeMessage {JE_SUIS_CONNECTE, JE_SUIS_DECONNECTE, INFO_PSEUDO, TEXTE, IMAGE, FICHIER, MESSAGE_NUL, FICHIER_INIT, FICHIER_ANSWER} protected TypeMessage type; + private String dateMessage; + private String sender; private static final long serialVersionUID = 1L; private static Instrumentation inst; - + + + public static String getDateAndTime() { + DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"); + LocalDateTime now = LocalDateTime.now(); + return dtf.format(now); + } + + public TypeMessage getTypeMessage() { return this.type; } + + public void setDateMessage(String dateMessage) { + this.dateMessage = dateMessage; + } + + public String getDateMessage() { + return this.dateMessage; + } + + public String getSender() { + return this.sender ; + } + + public void setSender(String sender) { + this.sender = sender; + } + + protected abstract String attributsToString(); diff --git a/POO/src/messages/MessageFichier.java b/POO/src/messages/MessageFichier.java index aa8f0bf..c52163b 100644 --- a/POO/src/messages/MessageFichier.java +++ b/POO/src/messages/MessageFichier.java @@ -1,6 +1,6 @@ package messages; -import messages.Message.TypeMessage; +import main.Utilisateur; public class MessageFichier extends Message { @@ -10,10 +10,11 @@ public class MessageFichier extends Message { private String extension; public MessageFichier(TypeMessage type, String contenu, String extension) throws MauvaisTypeMessageException{ - if ((type==TypeMessage.IMAGE)||(type==TypeMessage.FICHIER)) { + if ((type==TypeMessage.IMAGE)||(type==TypeMessage.FICHIER) ||(type==TypeMessage.FICHIER_INIT) || (type==TypeMessage.FICHIER_ANSWER) ) { this.type=type; this.contenu=contenu; this.extension=extension; + this.setDateMessage(Message.getDateAndTime()); } else throw new MauvaisTypeMessageException(); } @@ -30,4 +31,18 @@ public class MessageFichier extends Message { protected String attributsToString() { return this.contenu+"###"+this.extension; } + + public String toString() { + if(this.type == TypeMessage.IMAGE) { + return this.contenu; + }else { + String suffixe; + if(this.getSender().equals("Moi")) { + suffixe = "envoyé\n"; + }else { + suffixe = "reçu\n"; + } + return "<"+this.getDateMessage()+"> : "+this.contenu+" "+suffixe; + } + } } \ No newline at end of file diff --git a/POO/src/messages/MessageTexte.java b/POO/src/messages/MessageTexte.java index e5d354d..38fff5f 100644 --- a/POO/src/messages/MessageTexte.java +++ b/POO/src/messages/MessageTexte.java @@ -1,17 +1,18 @@ package messages; -import messages.Message.TypeMessage; 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(); } @@ -19,9 +20,15 @@ public class MessageTexte extends Message { 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..940bf9a --- /dev/null +++ b/POO/src/observers/ObserverSocketState.java @@ -0,0 +1,9 @@ +package observers; + +import java.net.Socket; + +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 b73b4a0..e3247fc 100644 --- a/POO/src/session/ControleurSession.java +++ b/POO/src/session/ControleurSession.java @@ -5,33 +5,57 @@ 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.sql.SQLException; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +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 +64,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,28 +99,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); } } + + 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(";"); + } + } + + } } - - //Methode appelee quand l'inputStream de la socket de communication recoit des donnees - @Override - public void update(Object o, Object arg) { - MessageTexte messageIn = (MessageTexte) arg; - System.out.println(messageIn.getContenu()); - this.vue.appendMessage(messageIn.getContenu()); - } - - - 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) { @@ -102,5 +148,129 @@ 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) { + int nbFile = this.files.size(); + 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()); + 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; + } } diff --git a/POO/src/session/FileComponent.java b/POO/src/session/FileComponent.java new file mode 100644 index 0000000..079d92b --- /dev/null +++ b/POO/src/session/FileComponent.java @@ -0,0 +1,44 @@ +package session; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextPane; +import javax.swing.text.BadLocationException; + +public class FileComponent extends JPanel { + + private JTextPane container; + JPanel self; + + public FileComponent(JTextPane container,String label) { + super(); + this.self = this; + this.container = container; + JLabel l = new JLabel(label); + l.setEnabled(false); + JButton close = new JButton("X"); + close.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + + try { + container.getDocument().remove(container.getDocument().getLength()-3, 1); + } catch (BadLocationException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + container.repaint(); + + } + }); + this.add(l); + this.add(close); + + + } +} diff --git a/POO/src/session/VueSession.java b/POO/src/session/VueSession.java index 8471522..d42726c 100644 --- a/POO/src/session/VueSession.java +++ b/POO/src/session/VueSession.java @@ -2,108 +2,266 @@ 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.File; 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.imageio.ImageIO; +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.SimpleAttributeSet; +import javax.swing.text.Style; +import javax.swing.text.StyleConstants; +import javax.swing.text.StyleContext; +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; +import messages.MessageFichier; +import messages.MessageTexte; -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 { + 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); + } + } + + } } diff --git a/POO/src/standard/ControleurStandard.java b/POO/src/standard/ControleurStandard.java index 204114e..f955bb9 100644 --- a/POO/src/standard/ControleurStandard.java +++ b/POO/src/standard/ControleurStandard.java @@ -17,25 +17,30 @@ import javax.swing.JList; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; -import communication.CommunicationUDP; -import communication.TCPServer; -import main.Observer; +import communication.tcp.TCPServer; +import communication.udp.CommunicationUDP; +import database.SQLiteManager; import main.Utilisateur; +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; - public ControleurStandard(VueStandard vue, int portClientUDP, int portServerUDP, int[] portsOther, int portServerTCP) throws IOException { + public ControleurStandard(VueStandard vue, int portClientUDP, int portServerUDP, int[] portsOther, int portServerTCP, SQLiteManager sqlManager) throws IOException { this.vue = vue; this.tcpServ = new TCPServer(portServerTCP); @@ -47,55 +52,68 @@ public class ControleurStandard implements ActionListener, ListSelectionListener this.commUDP.sendMessageConnecte(); this.commUDP.sendMessageInfoPseudo(); - this.etatModif = EtatModif.TERMINE; + this.idsSessionEnCours = new ArrayList(); + + this.sqlManager = sqlManager; + + this.modifPseudo = ModifPseudo.TERMINE; } //---------- 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()) { + int a = 5; + + //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(); + String pseudoOther = list.getSelectedValue(); + Utilisateur other = this.commUDP.getUserFromPseudo(pseudoOther); + String idOther = other.getId(); - int choix = this.vue.displayJOptionCreation(pseudo); - System.out.println("choix : "+choix); - if(choix == 0) { - int port = CommunicationUDP.getPortFromPseudo(pseudo); - System.out.println("port = "+port); - try { - - Socket socketComm = new Socket(InetAddress.getLocalHost(), port); - - this.sendMessage(socketComm, Utilisateur.getSelf().getPseudo()); - - String reponse = this.readMessage(socketComm); - - - System.out.println("reponse : " + reponse); - - if(reponse.contains("accepted")) { + //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) { + int port = other.getPort(); + System.out.println("port = "+port); + try { - this.vue.displayJOptionResponse("acceptée"); - this.vue.addSession(pseudo, new VueSession(socketComm)); + Socket socketComm = new Socket(InetAddress.getLocalHost(), port); + this.sendMessage(socketComm, Utilisateur.getSelf().getPseudo()); + String reponse = this.readMessage(socketComm); + + System.out.println("reponse : " + reponse); - }else{ - this.vue.displayJOptionResponse("refusée"); - socketComm.close(); - System.out.println("refused"); + 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) { + e1.printStackTrace(); } - - } 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); } } @@ -109,14 +127,14 @@ public class ControleurStandard implements ActionListener, ListSelectionListener 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 (!CommunicationUDP.containsUserFromPseudo(this.vue.getDisplayedPseudo())) { - + if (this.vue.getDisplayedPseudo().length() >= 1 && !this.commUDP.containsUserFromPseudo(this.vue.getDisplayedPseudo().toLowerCase())) { + Utilisateur.getSelf().setPseudo(this.vue.getDisplayedPseudo()); try { @@ -131,7 +149,7 @@ public class ControleurStandard implements ActionListener, ListSelectionListener } modifierPseudo.setText("Modifier"); - this.etatModif = EtatModif.TERMINE; + this.modifPseudo = ModifPseudo.TERMINE; } this.vue.toggleEditPseudo(); @@ -140,7 +158,7 @@ public class ControleurStandard implements ActionListener, ListSelectionListener //Cas deconnexion - if((JButton) e.getSource() == this.vue.getButtonDeconnexion() ) { + else if((JButton) e.getSource() == this.vue.getButtonDeconnexion() ) { try { this.commUDP.sendMessageDelete(); this.commUDP.removeAll(); @@ -172,6 +190,12 @@ public class ControleurStandard implements ActionListener, ListSelectionListener e1.printStackTrace(); } } + + else if(this.vue.isButtonTab(e.getSource()) ){ + JButton button = (JButton) e.getSource(); + int index = this.vue.removeSession(button); + this.idsSessionEnCours.remove(index); + } } @@ -224,26 +248,52 @@ public class ControleurStandard implements ActionListener, ListSelectionListener // 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) { Socket sockAccept = (Socket) arg; try { - String pseudo = this.readMessage(sockAccept); + String pseudoOther = this.readMessage(sockAccept); + String idOther = this.commUDP.getUserFromPseudo(pseudoOther).getId(); + + int reponse; - int reponse = this.vue.displayJOptionDemande(pseudo); - - System.out.println("reponse : " + reponse); + if(!this.idsSessionEnCours.contains(idOther)) { + reponse = this.vue.displayJOptionAskForSession(pseudoOther); + System.out.println("reponse : " + reponse); + }else { + reponse = 1; + } if(reponse == 0) { - this.sendMessage(sockAccept, "accepted"); - this.vue.addSession(pseudo, new VueSession(sockAccept)); - //new TCPHandlerConnection(sockAccept).start(); + this.idsSessionEnCours.add(idOther); + this.sendMessage(sockAccept, "accepted"); + + VueSession session = new VueSession(sockAccept, idOther, pseudoOther, this.sqlManager); + this.vue.addSession(pseudoOther, session); }else { this.sendMessage(sockAccept, "refused"); sockAccept.close(); @@ -254,32 +304,25 @@ public class ControleurStandard implements ActionListener, ListSelectionListener e.printStackTrace(); } } + } + + @Override + public void updateList(Object o, ArrayList userList) { if(o == this.commUDP) { - ArrayList users = (ArrayList) arg; 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); } } diff --git a/POO/src/standard/VueStandard.java b/POO/src/standard/VueStandard.java index 54de864..a20e746 100644 --- a/POO/src/standard/VueStandard.java +++ b/POO/src/standard/VueStandard.java @@ -1,19 +1,21 @@ 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.sql.SQLException; import java.util.ArrayList; -import java.util.HashMap; + +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,6 +27,7 @@ import javax.swing.ListSelectionModel; import javax.swing.ScrollPaneConstants; import javax.swing.plaf.basic.BasicButtonUI; +import database.SQLiteManager; import main.Utilisateur; import main.Vue; import session.VueSession; @@ -43,49 +46,44 @@ public class VueStandard extends Vue { private JButton seConnecter; private JButton seDeconnecter; private ControleurStandard c; - private HashMap sessions; + private ArrayList tabButtons; + private ArrayList sessions; +// private HashMap sessions; private DefaultListModel userList = new DefaultListModel(); - public VueStandard(String title, int portClientUDP, int portServerUDP, int[] portsOther, int portServerTCP) throws IOException { + //------------ CONSTRUCTEUR -------------// + + public VueStandard(String title, int portClientUDP, int portServerUDP, int[] portsOther, int portServerTCP, SQLiteManager sqlManager) throws IOException { super(title); - this.sessions = new HashMap(); - this.c = new ControleurStandard(this, portClientUDP, portServerUDP, portsOther, portServerTCP); + + this.tabButtons = new ArrayList(); + this.sessions = new ArrayList(); +// this.sessions = new HashMap(); + this.c = new ControleurStandard(this, portClientUDP, portServerUDP, portsOther, portServerTCP, sqlManager); getContentPane().setLayout(new GridBagLayout()); JPanel left = new JPanel(new BorderLayout()); - left.setBackground(Color.red); - //left.setPreferredSize(new Dimension(200, 200)); + //left.setBackground(Color.red); 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.setBackground(Color.WHITE); this.zoneSessions.setPreferredSize(new Dimension(600, 600)); - JPanel bottom = new JPanel(new GridLayout(1, 2)); - bottom.setBackground(Color.yellow); - bottom.setPreferredSize(new Dimension(600, 100)); - - //--------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); @@ -119,7 +117,7 @@ public class VueStandard extends Vue { this.activeUsersList.setLayoutOrientation(JList.VERTICAL); this.activeUsersList.addListSelectionListener(this.c); - System.out.println("listener ajouté"); + System.out.println("listener ajouté userlist"); JScrollPane listScroller = new JScrollPane(this.activeUsersList); listScroller.setPreferredSize(new Dimension(50,50)); @@ -150,41 +148,29 @@ public class VueStandard extends Vue { - GridBagConstraints gridBagConstraint1 = new GridBagConstraints(); - GridBagConstraints gridBagConstraint2 = new GridBagConstraints(); - GridBagConstraints gridBagConstraint3 = new GridBagConstraints(); + GridBagConstraints gridBagConstraintLeft = new GridBagConstraints(); + GridBagConstraints gridBagConstraintSessions = 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; + 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,gridBagConstraint1); + getContentPane().add(left,gridBagConstraintLeft); - gridBagConstraint2.fill = GridBagConstraints.BOTH; - gridBagConstraint2.gridx = 1; - gridBagConstraint2.gridy = 0; - gridBagConstraint2.gridwidth = 2; - gridBagConstraint2.gridheight = 3; - gridBagConstraint2.weightx = 0.66; - gridBagConstraint2.weighty = 0.66; - - getContentPane().add(this.zoneSessions,gridBagConstraint2); - - gridBagConstraint3.fill = GridBagConstraints.BOTH; - gridBagConstraint3.gridx = 1; - gridBagConstraint3.gridy = 3; - gridBagConstraint3.gridwidth = 2; - gridBagConstraint3.gridheight = 1; - gridBagConstraint3.weightx = 0.66; - gridBagConstraint3.weighty = 0.33; - - - getContentPane().add(bottom,gridBagConstraint3); + 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); @@ -192,20 +178,13 @@ public class VueStandard extends Vue { this.addWindowListener(c); } + + //------------ GETTERS -------------// + protected JList getActiveUsersList(){ return this.activeUsersList; } - protected void setActiveUsersList(ArrayList users) { - this.removeAllUsers(); - this.userList.addAll(users); - } - - - protected void removeAllUsers() { - this.userList.removeAllElements(); - } - protected JButton getButtonModifierPseudo() { return this.modifierPseudo; @@ -223,10 +202,43 @@ public class VueStandard extends Vue { return this.pseudoSelf.getText(); } + + //------------ SETTERS -------------// + + + protected void setActiveUsersList(ArrayList users) { + this.removeAllUsers(); + this.userList.addAll(users); + } + protected void setDisplayedPseudo(String pseudo) { this.pseudoSelf.setText(pseudo); } + + //------------ 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()); @@ -240,51 +252,42 @@ public class VueStandard extends Vue { this.seConnecter.setEnabled(!this.seConnecter.isEnabled()); } - protected int displayJOptionCreation(String pseudo) { - return JOptionPane.showConfirmDialog(this, - "Voulez vous créer une session avec "+pseudo+" ?", - "Confirmation session", - JOptionPane.YES_NO_OPTION); + + //------------SESSION-------------// + + protected boolean isButtonTab(Object o) { + return this.tabButtons.contains(o); } - protected int displayJOptionDemande(String pseudo) { - return JOptionPane.showConfirmDialog(this, - pseudo+" souhaite creer une session avec vous.", - "Accepter demande", - JOptionPane.YES_NO_OPTION); + 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 displayJOptionResponse(String reponse) { - JOptionPane.showMessageDialog(this, "Demande de session "+reponse); + protected 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 addSession(String pseudo, VueSession session) { - int nbTab = this.zoneSessions.getTabCount(); -// 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 = sessions.containsKey(button); - if (containsKey) { - zoneSessions.remove(zoneSessions.indexOfTabComponent(button.getParent() ) ); - sessions.remove(button); - } - } - }); - + TabButton closeTab = new TabButton(); tabTitle.add(new JLabel(pseudo)); tabTitle.add(closeTab); @@ -292,10 +295,84 @@ public class VueStandard extends Vue { this.zoneSessions.addTab(pseudo, session); this.zoneSessions.setTabComponentAt(this.zoneSessions.getTabCount()-1, tabTitle); - this.sessions.put(closeTab, session); + this.tabButtons.add(closeTab); + this.sessions.add(session); + session.requestFocus(); } + //------------ OTHERS -------------// + + protected void removeAllUsers() { + this.userList.removeAllElements(); + } + + + //------------- PRIVATE CLASSES FOR THE TABS BUTTON -------------// + private class TabButton extends JButton{ + 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); + } + } + }; + } diff --git a/sqlite-jdbc-3.32.3.2.jar b/sqlite-jdbc-3.32.3.2.jar new file mode 100644 index 0000000..b6f55b7 Binary files /dev/null and b/sqlite-jdbc-3.32.3.2.jar differ