Browse Source

Snapshot 23797f5059

master 23797f5059
commit
fb6a792965
41 changed files with 2634 additions and 0 deletions
  1. +1
    -0
      Licence note
  2. +64
    -0
      README.md
  3. +11
    -0
      client/.classpath
  4. +91
    -0
      client/.gitignore
  5. +17
    -0
      client/.project
  6. BIN
      client/resource/iconCommandPC.png
  7. BIN
      client/resource/iconCommunication.png
  8. BIN
      client/resource/iconConnection.png
  9. +38
    -0
      client/src/bundle/Bundle.properties
  10. +38
    -0
      client/src/bundle/Bundle_fr.properties
  11. +53
    -0
      client/src/generic/Status.java
  12. +173
    -0
      client/src/gui/GUI.java
  13. +85
    -0
      client/src/gui/panel/CommunicationPanel.java
  14. +145
    -0
      client/src/gui/panel/ConnectionPanel.java
  15. +97
    -0
      client/src/gui/panel/LogPanel.java
  16. +76
    -0
      client/src/main/Primary.java
  17. +159
    -0
      client/src/network/Client.java
  18. +340
    -0
      client/src/network/CommunicationManager.java
  19. BIN
      img/CommandPC_commandIpconfig.png
  20. BIN
      img/CommandPC_commandLs.png
  21. BIN
      img/CommandPC_connectionResult.png
  22. BIN
      img/CommandPC_systemTrayIcon.png
  23. BIN
      img/CommandPC_systemTrayIcon2.png
  24. BIN
      img/CommandPC_systemTrayLinux.png
  25. BIN
      img/CommandPC_systemTrayWindows.png
  26. +11
    -0
      server/.classpath
  27. +91
    -0
      server/.gitignore
  28. +17
    -0
      server/.project
  29. BIN
      server/resource/iconStart.png
  30. BIN
      server/resource/iconStop.png
  31. +34
    -0
      server/src/bundle/Bundle.properties
  32. +34
    -0
      server/src/bundle/Bundle_fr.properties
  33. +57
    -0
      server/src/command/ExecuteCommand.java
  34. +53
    -0
      server/src/generic/Status.java
  35. +140
    -0
      server/src/gui/GUI.java
  36. +48
    -0
      server/src/main/Primary.java
  37. +159
    -0
      server/src/network/Client.java
  38. +333
    -0
      server/src/network/CommunicationManager.java
  39. +57
    -0
      server/src/network/Server.java
  40. +98
    -0
      server/src/thread/ClientThread.java
  41. +114
    -0
      server/src/thread/ServerThread.java

+ 1
- 0
Licence note View File

@ -0,0 +1 @@
If you find software that doesn’t have a license, that means you have no permission from the creators of the software to use, modify, or share the software. Although a code host such as GitHub may allow you to view and fork the code, this does not imply that you are permitted to use, modify, or share the software for any purpose.

+ 64
- 0
README.md View File

@ -0,0 +1,64 @@
# CommandPC
CommandPC is a program for executing commands on a remote machine regardless of the operating system.
## Description
The too long didn't read version is that: it's like SSH, but less secure and just a fun experimentation with threads, internationalization, encryption and GUI in JAVA.
Composed of two part, a client and a server, this application allow you to send encrypted command from one machine to another. Both the client and the server can operate with or without a gui.
English and French language are disponible within the application.
## Instruction
### Server
Without the `nogui` argument, the server display its status with a system tray icon. By right-clicking the icon you can interact with it.
| Server started | Server stopped |
|:--------------:|:--------------:|
|![server-systemTray-icon](img/CommandPC_systemTrayIcon.png)|![server-systemTray-icon](img/CommandPC_systemTrayIcon2.png)|||
Example:
- Windows sytem tray: ![server-systemTray-icon](img/CommandPC_systemTrayWindows.png)
- Linux XFCE system tray: ![server-systemTray-icon](img/CommandPC_systemTrayLinux.png)
Right-clicking on the icon allow you to start and stop the server. The command line argument `port=X` chand the listening port.
### Client
The client use a graphical interface to guide the user and is available in both French and English (using the "Français"/"English" button at the bottom)
The interface is composed of two pane: "connection" and "command".
The "connection" pane allows the connection to a remote server.
![connection-result](img/CommandPC_connectionResult.png)
After the connection established, the "command" pane allow to send commands to the server.
| 'ls /' on a Linux machine | 'ipconfig' on a Windows machine and gui in French for a change |
|:-------------------------:|:-------------------------------:|
|![ls](img/CommandPC_commandLs.png)|![ipconfig](img/CommandPC_commandIpconfig.png)|
## Security concerns
**TLDR: Use at your own risk over a secure network.**
### Command
No effort is made to check if the command that you send is compatible with the user privilige, exist or is even safe to use.
### Identity
No identification of the user or the machine is made. In other words, if a server is running any number of client can connect and run command on it.
### Encryption
This software use AES 128 bits encryption, but like too many software (including payed one) it has flaws that make it unsuitable to use over unsecured networks.
The main issue is the fact that during the first communication between the client and the server, the communication is encrypted via a master key.
This master key, is the same for all firsts exchanges. After that, a new communication key is generated between individual client and the server.
If somebody were to use a packet sniffer on the network, knowing the master key it's possible to get the communication key thus defeating the encryption entirely.
Side note, AES 128 allow this program to run on any implementation of the Java platform. See the [Cipher documentation](https://docs.oracle.com/javase/8/docs/api/javax/crypto/Cipher.html) for the complete list of "safe to use" cipher standard.

+ 11
- 0
client/.classpath View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-15">
<attributes>
<attribute name="module" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="gui/G.java|gui/PanelCommunication2.java|gui/PanelConnection2.java|thread/" kind="src" path="src"/>
<classpathentry kind="src" path="resource"/>
<classpathentry kind="output" path="bin"/>
</classpath>

+ 91
- 0
client/.gitignore View File

@ -0,0 +1,91 @@
########################
#JAVA
########################
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
########################
# ECLIPSE
########################
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
.recommenders
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# PyDev specific (Python IDE for Eclipse)
*.pydevproject
# CDT-specific (C/C++ Development Tooling)
.cproject
# CDT- autotools
.autotools
# Java annotation processor (APT)
.factorypath
# PDT-specific (PHP Development Tools)
.buildpath
# sbteclipse plugin
.target
# Tern plugin
.tern-project
# TeXlipse plugin
.texlipse
# STS (Spring Tool Suite)
.springBeans
# Code Recommenders
.recommenders/
# Annotation Processing
.apt_generated/
.apt_generated_test/
# Scala IDE specific (Scala & Java development for Eclipse)
.cache-main
.scala_dependencies
.worksheet
# Uncomment this line if you wish to ignore the project description file.
# Typically, this file would be tracked if it contains build/dependency configurations:
#.project

+ 17
- 0
client/.project View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>client</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

BIN
client/resource/iconCommandPC.png View File

Before After
Width: 64  |  Height: 64  |  Size: 3.3 KiB

BIN
client/resource/iconCommunication.png View File

Before After
Width: 52  |  Height: 32  |  Size: 2.1 KiB

BIN
client/resource/iconConnection.png View File

Before After
Width: 52  |  Height: 32  |  Size: 3.5 KiB

+ 38
- 0
client/src/bundle/Bundle.properties View File

@ -0,0 +1,38 @@
address = Address
clear = Clear
command = Command
commandToExecute = Command to execute:
connected = Connected
connectedTo = Connected to
connection = Connection
connectionEnded = Connection ended
errorSpecifyAddressAndPort = Specify both address and port.
execute = Execute
help_argument = Arguments:
help_cmd = Send a command X to the remote server. Don't launch the graphical user interface.
help_help = Show this help message.
help_intro = CommandPC is a program for executing commands on a remote machine regardless of the operating system.
help_ip = Set the server address to X.
help_port = Set the server port to X.
languageAlt = Fran\u00E7ais
languageName = English
port = Port

+ 38
- 0
client/src/bundle/Bundle_fr.properties View File

@ -0,0 +1,38 @@
address = Adresse
clear = Vider
command = Commande
commandToExecute = Commande \u00E0 ex\u00E9cuter:
connected = Connect\u00E9
connectedTo = Connect\u00E9 \u00E0
connection = Connexion
connectionEnded = Connexion termin\u00E9e
errorSpecifyAddressAndPort = Pr\u00E9cisez l'adresse et le port.
execute = Ex\u00E9cuter
help_argument = Arguments:
help_cmd = Envoie une commande X au serveur distant. Ne d\u00E9marre pas l'interface graphique.
help_help = Affiche ce message d'aide.
help_intro = CommandPC est un programme destin\u00E9 \u00E0 ex\u00E9cuter des commandes sur un serveur distant, quelque soit son syst\u00E8me d'exploitation.
help_ip = D\u00E9fini l'adresse du serveur distant.
help_port = D\u00E9fini le port du serveur distant.
languageAlt = English
languageName = Fran\u00E7ais
port = Port

+ 53
- 0
client/src/generic/Status.java View File

@ -0,0 +1,53 @@
package generic;
/**
* Generic response class.
*/
public class Status<T,E> {
/** Indicate if the operation was a success. By default, <code>false</code>. */
public boolean success;
/** Response message.*/
public String message;
/** Response payload.*/
public T payload;
/** Response error */
public E error;
public Status() {
super();
this.success=false;
}
public Status(boolean success) {
super();
this.success = success;
}
public Status(boolean success, String message) {
super();
this.success = success;
this.message = message;
}
public Status(boolean success, String message, T payload) {
super();
this.success = success;
this.message = message;
this.payload = payload;
}
public Status(boolean success, String message, T payload, E error) {
super();
this.success = success;
this.message = message;
this.payload = payload;
this.error = error;
}
@Override
public String toString() {
String result="Status [success="+success+", message="+message;
if(payload!=null) {
result+=", payload="+payload.toString();
}
if(error!=null) {
result+=", error="+error.toString();
}
return result+"]";
}
}

+ 173
- 0
client/src/gui/GUI.java View File

@ -0,0 +1,173 @@
package gui;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.util.Locale;
import java.util.ResourceBundle;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.SwingConstants;
import javax.swing.WindowConstants;
import generic.Status;
import gui.panel.CommunicationPanel;
import gui.panel.ConnectionPanel;
import network.CommunicationManager;
public class GUI extends JFrame {
private static final long serialVersionUID = 1L;
private ConnectionPanel connectionPanel;
private CommunicationPanel communicationPanel;
private JPanel languagePanel;
private JButton languageButton;
private JTabbedPane tabbedPane;
private CommunicationManager communicationManager;
public GUI(CommunicationManager communicationManager) {
super();
this.communicationManager=communicationManager;
connectionPanel=new ConnectionPanel(this);
connectionPanel.setAddress(communicationManager.getAddress());
connectionPanel.setPort(String.valueOf(communicationManager.getPort()));
communicationPanel=new CommunicationPanel(this);
initialize();
}
/**
* Initialize the default state of the different GUI elements.
*/
private void initialize() {
this.setTitle("CommandPC - MARTIN Romain");
this.setIconImage(getToolkit().getImage(getClass().getResource("/iconCommandPC.png")));
this.setSize(600,300);
this.setResizable(true);
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
this.addWindowListener(new java.awt.event.WindowAdapter() {
@Override
public void windowClosing(java.awt.event.WindowEvent e) {
communicationManager.disconnect();
dispose();
};
});
GridBagLayout gbl_contentPane=new GridBagLayout();
getContentPane().setLayout(gbl_contentPane);
tabbedPane=new JTabbedPane(SwingConstants.TOP);
tabbedPane.add("panelConnection",connectionPanel);
tabbedPane.add("panelCommunication",communicationPanel);
tabbedPane.setIconAt(0,new ImageIcon(getClass().getResource("/iconConnection.png")));
tabbedPane.setIconAt(1,new ImageIcon(getClass().getResource("/iconCommunication.png")));
tabbedPane.setEnabledAt(1,false);
GridBagConstraints gbc_tabbedPane=new GridBagConstraints();
gbc_tabbedPane.gridx=0;
gbc_tabbedPane.gridy=0;
gbc_tabbedPane.weightx=1.0d;
gbc_tabbedPane.weighty=1.0d;
gbc_tabbedPane.fill=GridBagConstraints.BOTH;
getContentPane().add(tabbedPane,gbc_tabbedPane);
GridBagLayout gbl_languagePanel=new GridBagLayout();
languagePanel=new JPanel(gbl_languagePanel);
GridBagConstraints gbc_languagePanel=new GridBagConstraints();
gbc_languagePanel.gridx=0;
gbc_languagePanel.gridy=1;
gbc_languagePanel.fill=GridBagConstraints.HORIZONTAL;
languageButton=new JButton("languageButton");
languageButton.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(java.awt.event.ActionEvent e) {
if(Locale.getDefault().getLanguage().equals(Locale.FRENCH.toString())) {
Locale.setDefault(Locale.ENGLISH);
}else {
Locale.setDefault(Locale.FRENCH);
}
//ResourceBundle.clearCache();
setText();
}
});
languagePanel.add(languageButton);
getContentPane().add(languagePanel,gbc_languagePanel);
setText();
}
/**
* Set the correct text for all graphical elements according to the default {@link Locale}.
* @see java.util.Locale#getDefault()
*/
private void setText(){
ResourceBundle resourceBundle=ResourceBundle.getBundle("bundle/Bundle",Locale.getDefault());
languageButton.setText(resourceBundle.getString("languageAlt"));
tabbedPane.setTitleAt(0,resourceBundle.getString("connection"));
tabbedPane.setTitleAt(1,resourceBundle.getString("command"));
communicationPanel.setText();
connectionPanel.setText();
}
/**
* Request connection status change.
* @param isConnection true if this is a connection request, false to disconnect.
* @param address address of the remote server.
* @param port port of the remote server.
* @return true if operation successful.
*/
public boolean changeConnectionStatus(boolean isConnection,String address,String port) {
//Disconnection
if(isConnection==false) {
communicationManager.disconnect();
tabbedPane.setEnabledAt(1,false);
tabbedPane.setSelectedIndex(0);
return true;
}
ResourceBundle resourceBundle=ResourceBundle.getBundle("bundle/Bundle",Locale.getDefault());
if(address.isBlank() || port.isBlank()) {
connectionPanel.addLog(resourceBundle.getString("errorSpecifyAddressAndPort"),true);
return false;
}
Status<Object,Exception> result=communicationManager.connect(address,Integer.valueOf(port));
if(result.success) {
connectionPanel.addLog(resourceBundle.getString("connectedTo")+" "+result.message.toString(),false);
tabbedPane.setEnabledAt(1,true);
tabbedPane.setSelectedIndex(1);
} else {
connectionPanel.addLog(result.error.toString(),true);
}
return result.success;
}
/**
* Send command to the server and receive his answer.
* @param text command to send.
*/
public void sendCommand(String text) {
Status<Object,Exception> resultSend=communicationManager.encryptAndSendString(text);
if(resultSend.success==false) {
communicationPanel.addLog(resultSend.error.toString(), true);
return;
}
Status<String, Exception> resultReceive=communicationManager.receiveAndDecryptString();
if(resultReceive.success) {
if(resultReceive.payload.trim().startsWith("java")) {
communicationPanel.addLog(resultReceive.payload.trim(), true);
} else {
communicationPanel.addLog(resultReceive.payload.trim(), false);
}
} else {
communicationPanel.addLog(resultReceive.error.toString(), true);
}
}
}

+ 85
- 0
client/src/gui/panel/CommunicationPanel.java View File

@ -0,0 +1,85 @@
package gui.panel;
import java.awt.GridBagConstraints;
import java.awt.event.KeyEvent;
import java.util.Locale;
import java.util.ResourceBundle;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JTextField;
import gui.GUI;
/**
* <p>Handle the communication with the server</p>
* <p>Allow the user to input and send commands and display their results.</p>
*/
public class CommunicationPanel extends LogPanel {
private static final long serialVersionUID = 1L;
private JLabel commandLabel;
private JTextField commandText;
private JButton executeButton;
private GUI gui;
public CommunicationPanel(GUI gui) {
super();
this.gui=gui;
initialize();
}
@Override
protected void initialize() {
super.initialize();
//GridBagLayout gbl_main=new GridBagLayout();
//this.setLayout(gbl_main);
commandLabel=new JLabel("labelCommand");
GridBagConstraints gbc_commandLabel=new GridBagConstraints();
gbc_commandLabel.gridx=0;
gbc_commandLabel.gridy=0;
gbc_commandLabel.anchor=GridBagConstraints.WEST;
this.add(commandLabel,gbc_commandLabel);
commandText=new JTextField();
commandText.addKeyListener(new java.awt.event.KeyAdapter() {
@Override
public void keyPressed(java.awt.event.KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_ENTER){
executeButton.doClick();
}
}
});
GridBagConstraints gbc_commandText=new GridBagConstraints();
gbc_commandText.gridx=0;
gbc_commandText.gridy=1;
gbc_commandText.weightx=1.0d;
gbc_commandText.fill=GridBagConstraints.BOTH;
this.add(commandText,gbc_commandText);
executeButton=new JButton("executeButton");
executeButton.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(java.awt.event.ActionEvent e) {
if(commandText.getText().isBlank()==false) {
gui.sendCommand(commandText.getText());
}
}
});
GridBagConstraints gbc_executeButton=new GridBagConstraints();
gbc_executeButton.gridx=3;
gbc_executeButton.gridy=1;
this.add(executeButton,gbc_executeButton);
setText();
}
@Override
public void setText() {
super.setText();
ResourceBundle resourceBundle=ResourceBundle.getBundle("bundle/Bundle",Locale.getDefault());
commandLabel.setText(resourceBundle.getString("commandToExecute"));
executeButton.setText(resourceBundle.getString("execute"));
}
}

+ 145
- 0
client/src/gui/panel/ConnectionPanel.java View File

@ -0,0 +1,145 @@
package gui.panel;
import java.awt.GridBagConstraints;
import java.awt.event.KeyEvent;
import java.util.Locale;
import java.util.ResourceBundle;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.SwingConstants;
import gui.GUI;
/**
* Handle connection state, allowing to set up and establish a connection as well as displaying it status.
*/
public class ConnectionPanel extends LogPanel {
private static final long serialVersionUID = 1L;
private JLabel addressLabel;
private JTextField addressText;
private JLabel portLabel;
private JTextField portText;
private JLabel addressPortSepartatorLabel;
private JToggleButton connectionToggleButton;
private GUI gui;
public void setAddress(String address) {
if(addressText!=null) addressText.setText(address);
}
public void setPort(String port) {
if(portText!=null) portText.setText(port);
}
public ConnectionPanel(GUI gui) {
super();
this.gui=gui;
initialize();
}
@Override
protected void initialize() {
super.initialize();
/*GridBagLayout gbl_main=new GridBagLayout();
this.setLayout(gbl_main);*/
addressLabel=new JLabel("addressLabel");
GridBagConstraints gbc_addressLabel=new GridBagConstraints();
gbc_addressLabel.gridx=0;
gbc_addressLabel.gridy=0;
this.add(addressLabel,gbc_addressLabel);
addressText=new JTextField();
addressText.setHorizontalAlignment(SwingConstants.CENTER);
addressText.addKeyListener(new java.awt.event.KeyAdapter() {
@Override
public void keyPressed(java.awt.event.KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_ENTER){
if(connectionToggleButton.isSelected()==false) {
connectionToggleButton.doClick();
}
}
}
});
GridBagConstraints gbc_addressText=new GridBagConstraints();
gbc_addressText.gridx=0;
gbc_addressText.gridy=1;
gbc_addressText.weightx=0.5d;
gbc_addressText.fill=GridBagConstraints.BOTH;
this.add(addressText,gbc_addressText);
addressPortSepartatorLabel=new JLabel(" : ");
addressPortSepartatorLabel.setHorizontalAlignment(SwingConstants.CENTER);
GridBagConstraints gbc_addressPortSepartatorLabel=new GridBagConstraints();
gbc_addressPortSepartatorLabel.gridx=1;
gbc_addressPortSepartatorLabel.gridy=1;
this.add(addressPortSepartatorLabel,gbc_addressPortSepartatorLabel);
portLabel=new JLabel("portLabel");
GridBagConstraints gbc_portLabel=new GridBagConstraints();
gbc_portLabel.gridx=2;
gbc_portLabel.gridy=0;
this.add(portLabel,gbc_portLabel);
portText=new JTextField();
portText.setHorizontalAlignment(SwingConstants.CENTER);
portText.addKeyListener(new java.awt.event.KeyAdapter() {
@Override
public void keyPressed(java.awt.event.KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_ENTER){
if(connectionToggleButton.isSelected()==false) {
connectionToggleButton.doClick();
}
}
}
});
GridBagConstraints gbc_portText=new GridBagConstraints();
gbc_portText.gridx=2;
gbc_portText.gridy=1;
gbc_portText.weightx=0.5d;
gbc_portText.fill=GridBagConstraints.BOTH;
this.add(portText,gbc_portText);
connectionToggleButton=new JToggleButton("connectionToggleButton");
connectionToggleButton.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(java.awt.event.ActionEvent e) {
boolean success=gui.changeConnectionStatus(connectionToggleButton.isSelected(),addressText.getText(),portText.getText());
if(success==false) {
connectionToggleButton.setSelected(false);
}
setTextConnectionToggleButton();
}
});
GridBagConstraints gbc_connectionToggleButton=new GridBagConstraints();
gbc_connectionToggleButton.gridx=3;
gbc_connectionToggleButton.gridy=1;
gbc_connectionToggleButton.fill=GridBagConstraints.HORIZONTAL;
this.add(connectionToggleButton,gbc_connectionToggleButton);
setText();
}
/**
* Set the correct text for toggleButtonConnection regarding the selected language and it's status.
*/
private void setTextConnectionToggleButton() {
ResourceBundle resourceBundle=ResourceBundle.getBundle("bundle/Bundle",Locale.getDefault());
if(connectionToggleButton.isSelected()) {
connectionToggleButton.setText(resourceBundle.getString("connected"));
}else {
connectionToggleButton.setText(resourceBundle.getString("connection"));
}
}
@Override
public void setText() {
super.setText();
ResourceBundle resourceBundle=ResourceBundle.getBundle("bundle/Bundle",Locale.getDefault());
addressLabel.setText(resourceBundle.getString("address"));
portLabel.setText(resourceBundle.getString("port"));
setTextConnectionToggleButton();
}
}

+ 97
- 0
client/src/gui/panel/LogPanel.java View File

@ -0,0 +1,97 @@
package gui.panel;
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.util.Locale;
import java.util.ResourceBundle;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
public class LogPanel extends JPanel {
private static final long serialVersionUID = 1L;
private JTextPane logTextPane;
private StyledDocument logStyleDocument;
private SimpleAttributeSet normalAttributeSet;
private SimpleAttributeSet errorAttributeSet;
private JScrollPane logScrollPane;
private JButton clearLogButton;
public LogPanel( ) {
super();
normalAttributeSet=new SimpleAttributeSet();
normalAttributeSet.addAttribute(StyleConstants.Foreground,Color.black);
errorAttributeSet=new SimpleAttributeSet();
errorAttributeSet.addAttribute(StyleConstants.Foreground,Color.red);
// initialize();
}
/**
* Initialize the default state of the different GUI elements.
*/
protected void initialize() {
GridBagLayout gbl_main=new GridBagLayout();
this.setLayout(gbl_main);
logTextPane=new JTextPane();
logStyleDocument=logTextPane.getStyledDocument();
logScrollPane=new JScrollPane(logTextPane);
GridBagConstraints gbc_logScrollPane=new GridBagConstraints();
gbc_logScrollPane.gridx=0;
gbc_logScrollPane.gridy=2;
gbc_logScrollPane.weightx=1.0d;
gbc_logScrollPane.weighty=1.0d;
gbc_logScrollPane.gridwidth=4;
gbc_logScrollPane.fill=GridBagConstraints.BOTH;
gbc_logScrollPane.insets=new Insets(5,0,0,0);
this.add(logScrollPane,gbc_logScrollPane);
clearLogButton=new JButton("clearLogButton");
clearLogButton.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(java.awt.event.ActionEvent e) {
logTextPane.setText("");
}
});
GridBagConstraints gbc_clearLogButton=new GridBagConstraints();
gbc_clearLogButton.gridx=3;
gbc_clearLogButton.gridy=3;
gbc_clearLogButton.fill=GridBagConstraints.HORIZONTAL;
this.add(clearLogButton,gbc_clearLogButton);
}
/**
* Set the correct text for all graphical elements according to the default {@link Locale}.
* @see java.util.Locale#getDefault()
*/
public void setText() {
ResourceBundle resourceBundle=ResourceBundle.getBundle("bundle/Bundle",Locale.getDefault());
clearLogButton.setText(resourceBundle.getString("clear"));
}
/**
* Append data to the log.
* @param data message to append.
* @param error indicate if data correspond to an error or not.
*/
public void addLog(String data,Boolean error) {
try {
if(logStyleDocument.getLength()>0) data="\n"+data;
if(error) {
logStyleDocument.insertString(logStyleDocument.getLength(), data, errorAttributeSet);
}else {
logStyleDocument.insertString(logStyleDocument.getLength(), data, normalAttributeSet);
}
}catch(BadLocationException e) {
logTextPane.setText(logTextPane.getText()+"\n"+data.trim().trim());
}
}
}

+ 76
- 0
client/src/main/Primary.java View File

@ -0,0 +1,76 @@
package main;
import java.util.Locale;
import java.util.ResourceBundle;
import generic.Status;
import gui.GUI;
import network.Client;
import network.CommunicationManager;
public class Primary {
public static void main(String[] args) {
String serverAddress="127.0.0.1";
int serverPort=6200;
String command="";
//Handle user arguments
if(args.length>0){
for(int i=0;i<args.length;i++){
if(args[i].startsWith("ip=")){
serverAddress=args[i].substring(3);
}
if(args[i].startsWith("port=")){
serverPort=Integer.valueOf(args[i].substring(5));
}
if(args[i].startsWith("cmd=")){
command=args[i].substring(4);
}
if(args[i].startsWith("help")||args[i].startsWith("?")){
ResourceBundle resourceBundle=ResourceBundle.getBundle("bundle/Bundle",Locale.getDefault());
System.out.println(resourceBundle.getString("help_intro")+"\n");
System.out.println(resourceBundle.getString("help_argument"));
System.out.println("ip=X\t"+resourceBundle.getString("help_ip"));
System.out.println("port=X\t"+resourceBundle.getString("help_port"));
System.out.println("cmd=X\t"+resourceBundle.getString("help_cmd"));
System.out.println("help,?\t"+resourceBundle.getString("help_help"));
return;
}
}
}
Client client=new Client(serverPort,serverAddress);
CommunicationManager communicationManager=new CommunicationManager(client);
if(command.isBlank()) {
GUI gui=new GUI(communicationManager);
gui.setVisible(true);
return;
}
ResourceBundle resourceBundle=ResourceBundle.getBundle("bundle/Bundle",Locale.getDefault());
Status<Object,Exception> connectStatus=communicationManager.connect();
if(connectStatus.success==false) {
connectStatus.error.toString();
return;
}
System.out.println(resourceBundle.getString("connectedTo")+": "+serverAddress+":"+serverPort);
Status<Object,Exception> sendStatus=communicationManager.encryptAndSendString(command);
if(sendStatus.success) {
Status<String,Exception> receiveStatus=communicationManager.receiveAndDecryptString();
if(receiveStatus.success) {
System.out.println(receiveStatus.payload.trim());
} else {
receiveStatus.error.toString();
}
} else {
sendStatus.error.toString();
}
communicationManager.disconnect();
System.out.println(resourceBundle.getString("connectionEnded"));
}
}

+ 159
- 0
client/src/network/Client.java View File

@ -0,0 +1,159 @@
package network;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintStream;
import java.net.Socket;
import generic.Status;
/**
* TCP client allowing to send and receive Objects.
*/
public class Client {
private int serverPort;
private String serverAddress;
private Socket socket;
private BufferedReader is;
private PrintStream os;
private ObjectInputStream ois;
private ObjectOutputStream oos;
public Client(){
serverPort=8033;
serverAddress="127.0.0.1";
}
public Client(int port){
serverPort=port;
serverAddress="127.0.0.1";
}
public Client(int port,String ip){
serverPort=port;
serverAddress=ip;
}
public int getServerPort() {
return serverPort;
}
public void setServerPort(int serverPort) {
this.serverPort = serverPort;
}
public String getServerAddress() {
return serverAddress;
}
public void setServerAddress(String serverAddress) {
this.serverAddress = serverAddress;
}
public Socket getSocket() {
return socket;
}
public void setSocket(Socket socket) {
this.socket = socket;
if(socket!=null) {
try {
setIs(new BufferedReader(new InputStreamReader(socket.getInputStream())));
setOs(new PrintStream(socket.getOutputStream()));
setOos(new ObjectOutputStream(socket.getOutputStream()));
setOis(new ObjectInputStream(socket.getInputStream()));
} catch (Exception e) {
}
}
}
public BufferedReader getIs() {
return is;
}
public void setIs(BufferedReader is) {
this.is = is;
}
public PrintStream getOs() {
return os;
}
public void setOs(PrintStream os) {
this.os = os;
}
public ObjectInputStream getOis() {
return ois;
}
public void setOis(ObjectInputStream ois) {
this.ois = ois;
}
public ObjectOutputStream getOos() {
return oos;
}
public void setOos(ObjectOutputStream oos) {
this.oos = oos;
}
public Status<Object,Exception> connect(){
Status<Object,Exception> result=new Status<Object,Exception>(true);
try {
socket=new Socket(serverAddress,serverPort);
is=new BufferedReader(new InputStreamReader(socket.getInputStream()));
os=new PrintStream(socket.getOutputStream());
ois=new ObjectInputStream(socket.getInputStream());
oos=new ObjectOutputStream(socket.getOutputStream());
result.message=socket.getInetAddress().toString().substring(1)+":"+socket.getPort();
} catch (Exception e) {
result.success=false;
result.error=e;
}
return result;
}
public Status<Object,Exception> sendString(String message){
Status<Object,Exception> result=new Status<Object,Exception>(true);
try {
os.println(message);
} catch (Exception e) {
result.success=false;
result.error=e;
}
return result;
}
public Status<String,Exception> receiveString(){
Status<String,Exception> result=new Status<String,Exception>(true);
try {
result.payload=is.readLine();
} catch (IOException e) {
result.success=false;
result.error=e;
}
return result;
}
public Status<Object,IOException> sendObject(Object object){
Status<Object,IOException> result=new Status<Object,IOException>(true);
try {
oos.writeObject(object);
} catch (IOException e) {
result.success=false;
result.error=e;
}
return result;
}
public Status<Object,Exception> receiveObject(){
Status<Object,Exception> result=new Status<Object,Exception>(true);
try {
result.payload=ois.readObject();
} catch (Exception e) {
result.success=false;
result.error=e;
}
return result;
}
public Status<Object,IOException> close(){
Status<Object,IOException> result=new Status<Object,IOException>(true);
if(this.socket==null) return result;
try {
socket.close();
} catch (IOException e) {
result.success=false;
result.error=e;
}
return result;
}
}

+ 340
- 0
client/src/network/CommunicationManager.java View File

@ -0,0 +1,340 @@
package network;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import generic.Status;
public class CommunicationManager {
private Client client;
/** Key used to initialize the connection. */
private SecretKey masterKey;
/** Communication key between the client and the server. */
private SecretKey communicationKey;
/** If <code>true</code> then the message will be send using the communicationKey. Otherwise, the masterKey will be used.*/
private boolean useCommunicationKey;
public CommunicationManager(Client client) {
this.client=client;
this.masterKey=new SecretKeySpec(new byte[]{-70,-45,-79,-32,-36,108,-117,-7,-97,56,46,-86,-35,-11,-35,-81}, "AES");
this.useCommunicationKey=false;
}
public String getAddress() {
if(client!=null) return client.getServerAddress();
return "";
}
public int getPort() {
if(client!=null) return client.getServerPort();
return 0;
}
/**
* Establish connection with the server set in the {@link #client}.
* If no error, then swap the key from master to communication.
* @return response {@link Status} object.
*/
public Status<Object,Exception> connect() {
Status<Object,Exception> status=new Status<Object,Exception>(true);
//Stop immediately if already connected
if(client!=null && client.getSocket()!=null && client.getSocket().isConnected()) {
return status;
}
//Connect to server
status=client.connect();
if(status.success==false) {
return status;
}
//Change from master key to a communication key
Status<String,Exception> keyStatus=client.receiveString();
if(keyStatus.success==false) {
status.success=keyStatus.success;
status.error=keyStatus.error;
return status;
}
Status<String,Exception> decryptStatus=decryptString(keyStatus.payload, masterKey);
if(decryptStatus.success==false) {
status.success=decryptStatus.success;
status.error=decryptStatus.error;
return status;
}
String[] encodedKeyString=decryptStatus.payload.split(";");
byte[] encodedKey=new byte[encodedKeyString.length];
for(int i=0;i<encodedKeyString.length;i++) {
encodedKey[i]=Byte.parseByte(encodedKeyString[i]);
}
communicationKey=new SecretKeySpec(encodedKey, "AES");
useCommunicationKey=true;
return status;
}
/**
* Establish connection with a server.
* @param address server address
* @param port server remote port
* @return response {@link Status} object.
*/
public Status<Object,Exception> connect(String address,int port) {
if(client.getSocket()!=null && client.getSocket().isConnected()) {
disconnect();
}
this.client=new Client(port, address);
return connect();
}
/**
* Encrypt and send data.
* @param data data to send.
* @return response {@link Status} object.
*/
public Status<Object,Exception> encryptAndSendString(String data) {
String encryptedMessage="";
if(useCommunicationKey) {
encryptedMessage=encryptString(data, communicationKey).payload;
}else {
encryptedMessage=encryptString(data, masterKey).payload;
}
return client.sendString(encryptedMessage);
}
/**
* Receive and decrypt data.
* @return response {@link Status} object.
*/
public Status<String,Exception> receiveAndDecryptString() {
Status<String,Exception> status=client.receiveString();
if(status.success==false) {
status.success=status.success;
status.error=status.error;
return status;
}
if(useCommunicationKey) {
status=decryptString(status.payload, communicationKey);
}else {
status=decryptString(status.payload, masterKey);
}
if(status.success==false) {
status.success=status.success;
status.error=status.error;
}
return status;
}
/**
* Close the communication socket.
* Inform the remote client of the deconnection.
* @return response {@link Status} object.
*/
public Status<Object,IOException> disconnect() {
encryptAndSendString("STOPstopSTOP");
return client.close();
}
/**
* Generate and setup a communication key to replace the master key for future communication.
* Send the new key to the client.
*/
public void setupCommunicationKey() {
communicationKey=generateSecretKey();
String encodedKey="";
for(int i=0;i<communicationKey.getEncoded().length;i++) {
encodedKey+=communicationKey.getEncoded()[i]+";";
}
encryptAndSendString(encodedKey);
useCommunicationKey=true;
}
/**
* Generates a secret key.
* @return secret key.
*/
private SecretKey generateSecretKey() {
KeyGenerator keyGenerator = null;
try {
keyGenerator = KeyGenerator.getInstance("AES");
} catch (NoSuchAlgorithmException e) {
//Never trigger as per the javax.crypto.KeyGenerator documentation:
//Every implementation of the Java platform is required to support
//the following standard KeyGenerator algorithms with the keysizes
//in parentheses: AES (128)
e.printStackTrace();
}
keyGenerator.init(128);
return keyGenerator.generateKey();
}
/**
* Encrypt a message.
* @param data message to encrypt.
* @return response {@link Status} object.
*/
public Status<byte[],Exception> encrypt(byte[] data) {
if(useCommunicationKey) {
return encrypt(data,communicationKey);
}else{
return encrypt(data,masterKey);
}
}
/**
* Encrypt a message with the specified key.
* @param data message to encrypt.
* @param secretKey secret key to use.
* @return response {@link Status} object.
*/
private Status<byte[],Exception> encrypt(byte[] data,SecretKey secretKey) {
Status<byte[],Exception> result=new Status<byte[],Exception>(true);
try {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encrypt=cipher.doFinal(data);
/*
* Base64.getEncoder().encode() is here to
* prevent javax.crypto.IllegalBlockSizeException:
* Input length must be multiple of 16 when decrypting with padded cipher
*/
result.payload=Base64.getEncoder().encode(encrypt);
} catch (Exception e) {
result.success=false;
result.error=e;
}
return result;
}
/**
* Encrypt a message.
* @param data message to encrypt.
* @return response {@link Status} object.
*/
public Status<String,Exception> encryptString(String data) {
if(useCommunicationKey) {
return encryptString(data,communicationKey);
}else{
return encryptString(data,masterKey);
}
}
/**
* Use {@link #encrypt(String, SecretKey)} to encrypt a message.
* @param data message to encrypt.
* @param secretKey secret key to use.
* @return response {@link Status} object.
*/
private Status<String,Exception> encryptString(String data,SecretKey secretKey) {
Status<String,Exception> result=new Status<String,Exception>(true);
Status<byte[],Exception> encryptResult=encrypt(data.getBytes(StandardCharsets.UTF_8),secretKey);
result.message=encryptResult.message;
result.success=encryptResult.success;
result.error=encryptResult.error;
if(encryptResult.success) {
result.payload=new String(encryptResult.payload,StandardCharsets.UTF_8);
}
return result;
}
/*private String padStringForCrypto (String data) {
//Prevent javax.crypto.IllegalBlockSizeException:
//Input length must be multiple of 16 when decrypting with padded cipher
if(data.length()%16!=0) {
System.out.println("data length="+data.length());
double dataLengthNeeded=Math.ceil((double)data.length()/16d)*16;
data=String.format("%-"+(int)dataLengthNeeded+"s", data);
System.out.println("data length="+data.length());
}
return data;
}*/
/**
* Decrypt a message.
* @param data encrypted message.
* @return response {@link Status} object.
*/
public Status<byte[],Exception> decrypt(byte[] data){
if(useCommunicationKey) {
return decrypt(data,communicationKey);
}else{
return decrypt(data,masterKey);
}
}
/**
* Decrypt a message with the specified key.
* @param data encrypted message.
* @param secretKey secret key to use.
* @return response {@link Status} object.
*/
private Status<byte[],Exception> decrypt(byte[] data,SecretKey secretKey) {
Status<byte[],Exception> result=new Status<byte[],Exception>(true);
try {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
/*
* Base64.getDecoder().decode() is here to
* prevent javax.crypto.IllegalBlockSizeException:
* Input length must be multiple of 16 when decrypting with padded cipher
*/
result.payload=cipher.doFinal(Base64.getDecoder().decode(data));
} catch (Exception e) {
result.success=false;
result.error=e;
}
return result;
}
/**
* Decrypt a {@link String}.
* @param data encrypted message.
* @return response {@link Status} object.
*/
public Status<String,Exception> decryptString(String data) {
if(useCommunicationKey) {
return decryptString(data,communicationKey);
}else{
return decryptString(data,masterKey);
}
}
/**
* Use {@link #encrypt(String, SecretKey)} to decrypt a message.
* @param data encrypted message.
* @param secretKey secret key to use.
* @return response {@link Status} object.
*/
private Status<String,Exception> decryptString(String data,SecretKey secretKey) {
Status<String,Exception> result=new Status<String,Exception>(true);
byte[] dataByte;
try {
dataByte=data.getBytes(StandardCharsets.UTF_8);
} catch (NullPointerException e) {
result.error=e;
result.success=false;
return result;
}
Status<byte[],Exception> decryptResult=decrypt(dataByte,secretKey);
result.message=decryptResult.message;
result.success=decryptResult.success;
result.error=decryptResult.error;
if(decryptResult.success) {
result.payload=new String(decryptResult.payload,StandardCharsets.UTF_8);
}
return result;
}
}

BIN
img/CommandPC_commandIpconfig.png View File

Before After
Width: 600  |  Height: 789  |  Size: 83 KiB

BIN
img/CommandPC_commandLs.png View File

Before After
Width: 600  |  Height: 516  |  Size: 26 KiB

BIN
img/CommandPC_connectionResult.png View File

Before After
Width: 600  |  Height: 300  |  Size: 18 KiB

BIN
img/CommandPC_systemTrayIcon.png View File

Before After
Width: 64  |  Height: 64  |  Size: 3.4 KiB

BIN
img/CommandPC_systemTrayIcon2.png View File

Before After
Width: 64  |  Height: 64  |  Size: 3.4 KiB

BIN
img/CommandPC_systemTrayLinux.png View File

Before After
Width: 213  |  Height: 26  |  Size: 2.4 KiB

BIN
img/CommandPC_systemTrayWindows.png View File

Before After
Width: 214  |  Height: 30  |  Size: 2.2 KiB

+ 11
- 0
server/.classpath View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-15">
<attributes>
<attribute name="module" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="resource"/>
<classpathentry kind="output" path="bin"/>
</classpath>

+ 91
- 0
server/.gitignore View File

@ -0,0 +1,91 @@
########################
#JAVA
########################
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
########################
# ECLIPSE
########################
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
.recommenders
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# PyDev specific (Python IDE for Eclipse)
*.pydevproject
# CDT-specific (C/C++ Development Tooling)
.cproject
# CDT- autotools
.autotools
# Java annotation processor (APT)
.factorypath
# PDT-specific (PHP Development Tools)
.buildpath
# sbteclipse plugin
.target
# Tern plugin
.tern-project
# TeXlipse plugin
.texlipse
# STS (Spring Tool Suite)
.springBeans
# Code Recommenders
.recommenders/
# Annotation Processing
.apt_generated/
.apt_generated_test/
# Scala IDE specific (Scala & Java development for Eclipse)
.cache-main
.scala_dependencies
.worksheet
# Uncomment this line if you wish to ignore the project description file.
# Typically, this file would be tracked if it contains build/dependency configurations:
#.project

+ 17
- 0
server/.project View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>server</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

BIN
server/resource/iconStart.png View File

Before After
Width: 64  |  Height: 64  |  Size: 3.4 KiB

BIN
server/resource/iconStop.png View File

Before After
Width: 64  |  Height: 64  |  Size: 3.4 KiB

+ 34
- 0
server/src/bundle/Bundle.properties View File

@ -0,0 +1,34 @@
clientConnection = Client connected
clientConnectionFail = Client connection failed
clientConnectionStop = Client disconnect
help_argument = Arguments:
help_help = Show this help message.
help_intro = CommandPC is a program for executing commands on a remote machine regardless of the operating system.
help_nogui = Don't start the graphical user interface.
help_port = Set the server port to X.
localPort = Local port
message = Message
messageFail = Error with the reception of the message
quit = Quit
serverEnd = Server stop
serverEndAction = Stop the server
serverStart = Server start
serverStartAction = Start the server
serverStartFail = Server start failed

+ 34
- 0
server/src/bundle/Bundle_fr.properties View File

@ -0,0 +1,34 @@
clientConnection = Client connect\u00E9
clientConnectionFail = \u00C9chec de connexion du client
clientConnectionStop = D\u00E9connexion du client
help_argument = Arguments:
help_help = Affiche ce message d'aide.
help_intro = CommandPC est un programme destin\u00E9 \u00E0 ex\u00E9cuter des commandes sur un serveur distant, quelque soit son syst\u00E8me d'exploitation.
help_nogui = Ne d\u00E9mmare pas l'interface graphique.
help_port = D\u00E9fini le port du serveur.
localPort = Port local
message = Message
messageFail = Erreur lors de la r\u00E9ception du message
quit = Arr\u00EAter
serverEnd = Arr\u00EAt du serveur
serverEndAction = Arr\u00EAter le serveur
serverStart = D\u00E9marrage du serveur
serverStartAction = D\u00E9marrer le serveur
serverStartFail = \u00C9chec du d\u00E9marrage du serveur

+ 57
- 0
server/src/command/ExecuteCommand.java View File

@ -0,0 +1,57 @@
package command;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Serializable;
import generic.Status;
/**
* Executes specified string command by the environment in which the application is running.
*/
public class ExecuteCommand implements Serializable{
private static final long serialVersionUID = 1L;
private Runtime runtime;
private BufferedReader bufferReader;
public ExecuteCommand(){
runtime=Runtime.getRuntime();
}
public Runtime getRuntime() {
return runtime;
}
public void setRuntime(Runtime runtime) {
this.runtime = runtime;
}
/**
* Executes the specified string command.
* @param command a specified system command.
* @return response {@link Status} object.
* @see Runtime#exec(String)
*/
public Status<String,IOException> executeCommand(String command){
Status<String,IOException> result=new Status<String, IOException>(false);
String responseLine;
try {
Process execution=runtime.exec(command);
bufferReader=new BufferedReader(new InputStreamReader(execution.getInputStream()));
while ((responseLine = bufferReader.readLine()) != null){
if(!(responseLine.isEmpty())){
if(result.payload==null || result.payload.isBlank()) {
result.payload=responseLine;
}else {
result.payload=result.payload+"\n"+responseLine;
}
}
}
result.success=true;
} catch (IOException e) {
result.success=false;
result.error=e;
}
return result;
}
}

+ 53
- 0
server/src/generic/Status.java View File

@ -0,0 +1,53 @@
package generic;
/**
* Generic response class.
*/
public class Status<T,E> {
/** Indicate if the operation was a success. By default, <code>false</code>. */
public boolean success;
/** Response message.*/
public String message;
/** Response payload.*/
public T payload;
/** Response error */
public E error;
public Status() {
super();
this.success=false;
}
public Status(boolean success) {
super();
this.success = success;
}
public Status(boolean success, String message) {
super();
this.success = success;
this.message = message;
}
public Status(boolean success, String message, T payload) {
super();
this.success = success;
this.message = message;
this.payload = payload;
}
public Status(boolean success, String message, T payload, E error) {
super();
this.success = success;
this.message = message;
this.payload = payload;
this.error = error;
}
@Override
public String toString() {
String result="Status [success="+success+", message="+message;
if(payload!=null) {
result+=", payload="+payload.toString();
}
if(error!=null) {
result+=", error="+error.toString();
}
return result+"]";
}
}

+ 140
- 0
server/src/gui/GUI.java View File

@ -0,0 +1,140 @@
package gui;
import java.awt.AWTException;
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.net.InetAddress;
import java.util.Locale;
import java.util.ResourceBundle;
import thread.ServerThread;
public class GUI {
private TrayIcon trayIcon;
private Image iconServerStart;
private Image iconServerStop;
private PopupMenu trayPopupMenu;
private MenuItem trayPopupItemQuit;
private MenuItem trayPopupItemToggleServerState;
private boolean serverStarted;
private ServerThread serverThread;
public GUI(ServerThread serverThread) {
serverStarted=false;
this.serverThread=serverThread;
initialize();
}
public void show() {
try {
SystemTray.getSystemTray().add(trayIcon);
} catch (AWTException e) {
e.printStackTrace();
}
}
public void hide() {
SystemTray.getSystemTray().remove(trayIcon);
}
private void initialize() {
if(SystemTray.isSupported()==false) {
return;
}
//Setup TrayIcon image
iconServerStart=Toolkit.getDefaultToolkit().getImage(getClass().getResource("/iconStart.png"));
iconServerStop=Toolkit.getDefaultToolkit().getImage(getClass().getResource("/iconStop.png"));
//Setup popup menu
trayPopupItemQuit=new MenuItem("trayPopupItemQuit");
trayPopupItemQuit.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
serverThread.stopServerSocket();
serverThread.stopClientsSocket();
hide();
}
});
trayPopupItemToggleServerState=new MenuItem("trayPopupItemToggleServerState");
trayPopupItemToggleServerState.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if(serverStarted) {
serverThread.stopServerSocket();
serverThread.stopClientsSocket();
} else {
if(serverThread.isAlive()==false) {
serverThread=new ServerThread(serverThread.getServer(), serverThread.getGui());
serverThread.start();
}
}
}
});
trayPopupMenu=new PopupMenu();
trayPopupMenu.add(trayPopupItemToggleServerState);
trayPopupMenu.addSeparator();
trayPopupMenu.add(trayPopupItemQuit);
trayIcon=new TrayIcon(iconServerStop,"CommandPC",trayPopupMenu);
trayIcon.setImageAutoSize(true);
setText();
}
/**
* Set the correct text for all graphical elements according to the default {@link Locale}.
* @see java.util.Locale#getDefault()
*/
private void setText() {
ResourceBundle resourceBundle=ResourceBundle.getBundle("bundle/Bundle",Locale.getDefault());
trayPopupItemQuit.setLabel(resourceBundle.getString("quit"));
setTextServerState();
}
/**
* Set the correct text related to server state.
*/
private void setTextServerState() {
ResourceBundle resourceBundle=ResourceBundle.getBundle("bundle/Bundle",Locale.getDefault());
if(serverStarted) {
trayPopupItemToggleServerState.setLabel(resourceBundle.getString("serverEndAction"));
} else {
trayPopupItemToggleServerState.setLabel(resourceBundle.getString("serverStartAction"));
}
}
/**
* Indicate to the user that the server has started.
*/
public void displayServerStart() {
serverStarted=true;
trayIcon.setImage(iconServerStart);
setTextServerState();
}
/**
* Indicate to the user that the server has stop.
*/
public void displayServerStop() {
serverStarted=false;
trayIcon.setImage(iconServerStop);
setTextServerState();
}
/**
* Indicate to the user that a new client is connected.
* @param inetAddress client address
* @param port client port
* @param localPort local port
*/
public void displayStartConnection(InetAddress inetAddress, int port, int localPort) {
// TODO displayStartConnection()
}
}

+ 48
- 0
server/src/main/Primary.java View File

@ -0,0 +1,48 @@
package main;
import java.util.Locale;
import java.util.ResourceBundle;
import gui.GUI;
import network.Server;
import thread.ServerThread;
public class Primary {
public static void main(String[] args) {
int port=6200;
boolean showGUI=true;
//Handle user arguments
if(args.length>0){
for(int i=0;i<args.length;i++){
if(args[i].startsWith("port=")){
port=Integer.valueOf(args[i].substring(3));
}
if(args[i].startsWith("nogui")){
showGUI=false;
}
if(args[i].startsWith("help")||args[i].startsWith("?")){
ResourceBundle resourceBundle=ResourceBundle.getBundle("bundle/Bundle",Locale.getDefault());
System.out.println(resourceBundle.getString("help_intro")+"\n");
System.out.println(resourceBundle.getString("help_argument"));
System.out.println("port=X\t"+resourceBundle.getString("help_port"));
System.out.println("nogui\t"+resourceBundle.getString("help_nogui"));
System.out.println("help,?\t"+resourceBundle.getString("help_help"));
return;
}
}
}
Server server=new Server(port);
ServerThread serverThread=new ServerThread(server);
if(showGUI) {
GUI gui=new GUI(serverThread);
gui.show();
serverThread.setGui(gui);
}
serverThread.start();
}
}

+ 159
- 0
server/src/network/Client.java View File

@ -0,0 +1,159 @@
package network;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintStream;
import java.net.Socket;
import generic.Status;
/**
* TCP client allowing to send and receive Objects.
*/
public class Client {
private int serverPort;
private String serverAddress;
private Socket socket;
private BufferedReader is;
private PrintStream os;
private ObjectInputStream ois;
private ObjectOutputStream oos;
public Client(){
serverPort=8033;
serverAddress="127.0.0.1";
}
public Client(int port){
serverPort=port;
serverAddress="127.0.0.1";
}
public Client(int port,String ip){
serverPort=port;
serverAddress=ip;
}
public int getServerPort() {
return serverPort;
}
public void setServerPort(int serverPort) {
this.serverPort = serverPort;
}
public String getServerAddress() {
return serverAddress;
}
public void setServerAddress(String serverAddress) {
this.serverAddress = serverAddress;
}
public Socket getSocket() {
return socket;
}
public void setSocket(Socket socket) {
this.socket = socket;
if(socket!=null) {
try {
setIs(new BufferedReader(new InputStreamReader(socket.getInputStream())));
setOs(new PrintStream(socket.getOutputStream()));
setOos(new ObjectOutputStream(socket.getOutputStream()));
setOis(new ObjectInputStream(socket.getInputStream()));
} catch (Exception e) {
}
}
}
public BufferedReader getIs() {
return is;
}
public void setIs(BufferedReader is) {
this.is = is;
}
public PrintStream getOs() {
return os;
}
public void setOs(PrintStream os) {
this.os = os;
}
public ObjectInputStream getOis() {
return ois;
}
public void setOis(ObjectInputStream ois) {
this.ois = ois;
}
public ObjectOutputStream getOos() {
return oos;
}
public void setOos(ObjectOutputStream oos) {
this.oos = oos;
}
public Status<Object,Exception> connect(){
Status<Object,Exception> result=new Status<Object,Exception>(true);
try {
socket=new Socket(serverAddress,serverPort);
is=new BufferedReader(new InputStreamReader(socket.getInputStream()));
os=new PrintStream(socket.getOutputStream());
ois=new ObjectInputStream(socket.getInputStream());
oos=new ObjectOutputStream(socket.getOutputStream());
result.message=socket.getInetAddress().toString().substring(1)+":"+socket.getPort();
} catch (Exception e) {
result.success=false;
result.error=e;
}
return result;
}
public Status<Object,Exception> sendString(String message){
Status<Object,Exception> result=new Status<Object,Exception>(true);
try {
os.println(message);
} catch (Exception e) {
result.success=false;
result.error=e;
}
return result;
}
public Status<String,Exception> receiveString(){
Status<String,Exception> result=new Status<String,Exception>(true);
try {
result.payload=is.readLine();
} catch (IOException e) {
result.success=false;
result.error=e;
}
return result;
}
public Status<Object,IOException> sendObject(Object object){
Status<Object,IOException> result=new Status<Object,IOException>(true);
try {
oos.writeObject(object);
} catch (IOException e) {
result.success=false;
result.error=e;
}
return result;
}
public Status<Object,Exception> receiveObject(){
Status<Object,Exception> result=new Status<Object,Exception>(true);
try {
result.payload=ois.readObject();
} catch (Exception e) {
result.success=false;
result.error=e;
}
return result;
}
public Status<Object,IOException> close(){
Status<Object,IOException> result=new Status<Object,IOException>(true);
if(this.socket==null) return result;
try {
socket.close();
} catch (IOException e) {
result.success=false;
result.error=e;
}
return result;
}
}

+ 333
- 0
server/src/network/CommunicationManager.java View File

@ -0,0 +1,333 @@
package network;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import generic.Status;
public class CommunicationManager {
private Client client;
/** Key used to initialize the connection. */
private SecretKey masterKey;
/** Communication key between the client and the server. */
private SecretKey communicationKey;
/** If <code>true</code> then the message will be send using the communicationKey. Otherwise, the masterKey will be used.*/
private boolean useCommunicationKey;
public CommunicationManager(Client client) {
this.client=client;
this.masterKey=new SecretKeySpec(new byte[]{-70,-45,-79,-32,-36,108,-117,-7,-97,56,46,-86,-35,-11,-35,-81}, "AES");
this.useCommunicationKey=false;
}
public String getAddress() {
if(client!=null) return client.getServerAddress();
return "";
}
public int getPort() {
if(client!=null) return client.getServerPort();
return 0;
}
/**
* Establish connection with the server set in the {@link #client}.
* If no error, then swap the key from master to communication.
* @return response {@link Status} object.
*/
public Status<Object,Exception> connect() {
Status<Object,Exception> status=new Status<Object,Exception>(true);
//Stop immediately if already connected
if(client!=null && client.getSocket()!=null && client.getSocket().isConnected()) {
return status;
}
//Connect to server
status=client.connect();
if(status.success==false) {
return status;
}
//Change from master key to a communication key
Status<String,Exception> keyStatus=client.receiveString();
if(keyStatus.success==false) {
status.success=keyStatus.success;
status.error=keyStatus.error;
return status;
}
Status<String,Exception> decryptStatus=decryptString(keyStatus.payload, masterKey);
if(decryptStatus.success==false) {
status.success=decryptStatus.success;
status.error=decryptStatus.error;
return status;
}
String[] encodedKeyString=decryptStatus.payload.split(";");
byte[] encodedKey=new byte[encodedKeyString.length];
for(int i=0;i<encodedKeyString.length;i++) {
encodedKey[i]=Byte.parseByte(encodedKeyString[i]);
}
communicationKey=new SecretKeySpec(encodedKey, "AES");
useCommunicationKey=true;
return status;
}
/**
* Establish connection with a server.
* @param address server address
* @param port server remote port
* @return response {@link Status} object.
*/
public Status<Object,Exception> connect(String address,int port) {
if(client.getSocket()!=null && client.getSocket().isConnected()) {
disconnect();
}
this.client=new Client(port, address);
return connect();
}
/**
* Encrypt and send data.
* @param data data to send.
* @return response {@link Status} object.
*/
public Status<Object,Exception> encryptAndSendString(String data) {
String encryptedMessage="";
if(useCommunicationKey) {
encryptedMessage=encryptString(data, communicationKey).payload;
}else {
encryptedMessage=encryptString(data, masterKey).payload;
}
return client.sendString(encryptedMessage);
}
/**
* Receive and decrypt data.
* @return response {@link Status} object.
*/
public Status<String,Exception> receiveAndDecryptString() {
Status<String,Exception> status=client.receiveString();
if(status.success==false) {
status.success=status.success;
status.error=status.error;
return status;
}
if(useCommunicationKey) {
status=decryptString(status.payload, communicationKey);
}else {
status=decryptString(status.payload, masterKey);
}
if(status.success==false) {
status.success=status.success;
status.error=status.error;
}
return status;
}
/**
* Close the communication socket.
* Inform the remote client of the deconnection.
* @return response {@link Status} object.
*/
public Status<Object,IOException> disconnect() {
encryptAndSendString("STOPstopSTOP");
return client.close();
}
/**
* Generate and setup a communication key to replace the master key for future communication.
* Send the new key to the client.
*/
public void setupCommunicationKey() {
communicationKey=generateSecretKey();
String encodedKey="";
for(int i=0;i<communicationKey.getEncoded().length;i++) {
encodedKey+=communicationKey.getEncoded()[i]+";";
}
encryptAndSendString(encodedKey);
useCommunicationKey=true;
}
/**
* Generates a secret key.
* @return secret key.
*/
private SecretKey generateSecretKey() {
KeyGenerator keyGenerator = null;
try {
keyGenerator = KeyGenerator.getInstance("AES");
} catch (NoSuchAlgorithmException e) {
//Never trigger as per the javax.crypto.KeyGenerator documentation:
//Every implementation of the Java platform is required to support
//the following standard KeyGenerator algorithms with the keysizes
//in parentheses: AES (128)
e.printStackTrace();
}
keyGenerator.init(128);
return keyGenerator.generateKey();
}
/**
* Encrypt a message.
* @param data message to encrypt.
* @return response {@link Status} object.
*/
public Status<byte[],Exception> encrypt(byte[] data) {
if(useCommunicationKey) {
return encrypt(data,communicationKey);
}else{
return encrypt(data,masterKey);
}
}
/**
* Encrypt a message with the specified key.
* @param data message to encrypt.
* @param secretKey secret key to use.
* @return response {@link Status} object.
*/
private Status<byte[],Exception> encrypt(byte[] data,SecretKey secretKey) {
Status<byte[],Exception> result=new Status<byte[],Exception>(true);
try {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encrypt=cipher.doFinal(data);
/*
* Base64.getEncoder().encode() is here to
* prevent javax.crypto.IllegalBlockSizeException:
* Input length must be multiple of 16 when decrypting with padded cipher
*/
result.payload=Base64.getEncoder().encode(encrypt);
} catch (Exception e) {
result.success=false;
result.error=e;
}
return result;
}
/**
* Encrypt a message.
* @param data message to encrypt.
* @return response {@link Status} object.
*/
public Status<String,Exception> encryptString(String data) {
if(useCommunicationKey) {
return encryptString(data,communicationKey);
}else{
return encryptString(data,masterKey);
}
}
/**
* Use {@link #encrypt(String, SecretKey)} to encrypt a message.
* @param data message to encrypt.
* @param secretKey secret key to use.
* @return response {@link Status} object.
*/
private Status<String,Exception> encryptString(String data,SecretKey secretKey) {
Status<String,Exception> result=new Status<String,Exception>(true);
Status<byte[],Exception> encryptResult=encrypt(data.getBytes(StandardCharsets.UTF_8),secretKey);
result.message=encryptResult.message;
result.success=encryptResult.success;
result.error=encryptResult.error;
if(encryptResult.success) {
result.payload=new String(encryptResult.payload,StandardCharsets.UTF_8);
}
return result;
}
/*private String padStringForCrypto (String data) {
//Prevent javax.crypto.IllegalBlockSizeException:
//Input length must be multiple of 16 when decrypting with padded cipher
if(data.length()%16!=0) {
System.out.println("data length="+data.length());
double dataLengthNeeded=Math.ceil((double)data.length()/16d)*16;
data=String.format("%-"+(int)dataLengthNeeded+"s", data);
System.out.println("data length="+data.length());
}
return data;
}*/
/**
* Decrypt a message.
* @param data encrypted message.
* @return response {@link Status} object.
*/
public Status<byte[],Exception> decrypt(byte[] data){
if(useCommunicationKey) {
return decrypt(data,communicationKey);
}else{
return decrypt(data,masterKey);
}
}
/**
* Decrypt a message with the specified key.
* @param data encrypted message.
* @param secretKey secret key to use.
* @return response {@link Status} object.
*/
private Status<byte[],Exception> decrypt(byte[] data,SecretKey secretKey) {
Status<byte[],Exception> result=new Status<byte[],Exception>(true);
try {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
/*
* Base64.getDecoder().decode() is here to
* prevent javax.crypto.IllegalBlockSizeException:
* Input length must be multiple of 16 when decrypting with padded cipher
*/
result.payload=cipher.doFinal(Base64.getDecoder().decode(data));
} catch (Exception e) {
result.success=false;
result.error=e;
}
return result;
}
/**
* Decrypt a {@link String}.
* @param data encrypted message.
* @return response {@link Status} object.
*/
public Status<String,Exception> decryptString(String data) {
if(useCommunicationKey) {
return decryptString(data,communicationKey);
}else{
return decryptString(data,masterKey);
}
}
/**
* Use {@link #encrypt(String, SecretKey)} to decrypt a message.
* @param data encrypted message.
* @param secretKey secret key to use.
* @return response {@link Status} object.
*/
private Status<String,Exception> decryptString(String data,SecretKey secretKey) {
Status<String,Exception> result=new Status<String,Exception>(true);
byte[] dataByte=data.getBytes(StandardCharsets.UTF_8);
Status<byte[],Exception> decryptResult=decrypt(dataByte,secretKey);
result.message=decryptResult.message;
result.success=decryptResult.success;
result.error=decryptResult.error;
if(decryptResult.success) {
result.payload=new String(decryptResult.payload,StandardCharsets.UTF_8);
}
return result;
}
}

+ 57
- 0
server/src/network/Server.java View File

@ -0,0 +1,57 @@
package network;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import generic.Status;
/**
* TCP server allowing to send and receive Objects.
*/
public class Server {
private int serverPort;
private ServerSocket serverSocket;
private List<Socket> clientSocket;
public Server(int serverPort) {
super();
this.serverPort = serverPort;
clientSocket=new ArrayList<Socket>();
}
public Status<Integer,IOException> start() {
Status<Integer,IOException> result=new Status<Integer,IOException>(true);
try {
serverSocket=new ServerSocket(serverPort);
result.payload=serverSocket.getLocalPort();
} catch (IOException e) {
result.success=false;
result.error=e;
};
return result;
}
public Status<Socket,IOException> accept() {
Status<Socket,IOException> result=new Status<Socket,IOException>(true);
try {
Socket client=serverSocket.accept();
clientSocket.add(client);
result.payload=client;
} catch (IOException e) {
result.success=false;
result.error=e;
};
return result;
}
public void stop() {
try {
serverSocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

+ 98
- 0
server/src/thread/ClientThread.java View File

@ -0,0 +1,98 @@
package thread;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.util.Locale;
import java.util.ResourceBundle;
import command.ExecuteCommand;
import generic.Status;
import network.Client;
import network.CommunicationManager;
/**
* Thread dedicated to handling a communication with a client.
*/
public class ClientThread extends Thread{
private CommunicationManager communicationManager;
private Client client;
private ExecuteCommand executeCommand;
private boolean run;
private boolean osIsWindows;
public boolean isRun() {
return run;
}
public void setRun(boolean run) {
this.run = run;
}
public ClientThread(Socket socket) {
super();
this.client=new Client();
this.client.setSocket(socket);
this.communicationManager=new CommunicationManager(client);
this.executeCommand=new ExecuteCommand();
this.run=true;
this.osIsWindows=System.getProperty("os.name").toLowerCase().startsWith("windows");
}
private void closeConnection() {
communicationManager.encryptAndSendString("STOPstopSTOP");
client.close();
run=false;
}
@Override
public void run() {
communicationManager.setupCommunicationKey();
Status<String,Exception> status;
while(run) {
status=client.receiveString();
if(status.success && status.payload==null && status.error==null) {
//Client is probably disconnected and client.receiveString() is just "purging" buffer
closeConnection();
}
if(run && status.success) {
status=communicationManager.decryptString(status.payload);
}
if(run) {
if(status.success) {
if(status.payload.compareTo("STOPstopSTOP")==0) {
closeConnection();
}else {
System.out.println(
ResourceBundle.getBundle("bundle/Bundle",Locale.getDefault()).getString("message")
+": "+status.payload
);
Status<String, IOException> resultCommand=executeCommand.executeCommand(status.payload);
if(resultCommand.success) {
if(osIsWindows) {
try {
resultCommand.payload=new String(resultCommand.payload.getBytes(),"Cp850");
} catch (UnsupportedEncodingException e) {
//If encoding fail do nothing as the text will still be mostly legible
}
}
communicationManager.encryptAndSendString(resultCommand.payload);
}else{
communicationManager.encryptAndSendString(resultCommand.error.toString());
}
}
}else {
System.out.println(
ResourceBundle.getBundle("bundle/Bundle",Locale.getDefault()).getString("messageFail")
+": "+status.error.toString()
);
status.error.printStackTrace();
}
}
}
System.out.println(ResourceBundle.getBundle("bundle/Bundle",Locale.getDefault()).getString("clientConnectionStop"));
}
}

+ 114
- 0
server/src/thread/ServerThread.java View File

@ -0,0 +1,114 @@
package thread;
import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
import generic.Status;
import gui.GUI;
import network.Server;
/**
* Thread dedicated to create {@link ClientThread} upon client connection.
*/
public class ServerThread extends Thread{
private Server server;
private Boolean accept;
private GUI gui;
private List<ClientThread> clients;
public Server getServer() {
return server;
}
public void setServer(Server server) {
this.server = server;
}
public GUI getGui() {
return gui;
}
public void setGui(GUI gui) {
this.gui = gui;
}
public Boolean getAccept() {
return accept;
}
public void setAccept(Boolean accept) {
this.accept = accept;
}
public ServerThread(Server server){
super();
this.clients=new ArrayList<ClientThread>();
this.server=server;
this.accept=true;
this.gui=null;
}
public ServerThread(Server server,GUI gui){
super();
this.server=server;
this.accept=true;
this.gui=gui;
}
@Override
public void run(){
accept=true;
Status<Integer,IOException> serverStartStatus=server.start();
if(serverStartStatus.success) {
System.out.println(ResourceBundle.getBundle("bundle/Bundle",Locale.getDefault()).getString("serverStart"));
if(gui!=null) gui.displayServerStart();
while(accept) {
Status<Socket,IOException> connectionAttempt=server.accept();
if(connectionAttempt.success) {
Socket client=connectionAttempt.payload;
System.out.println(
ResourceBundle.getBundle("bundle/Bundle",Locale.getDefault()).getString("clientConnection")
+" "+client.getInetAddress()+":"+client.getPort()
+" ("
+ResourceBundle.getBundle("bundle/Bundle",Locale.getDefault()).getString("localPort")
+": "+client.getLocalPort()
+")"
);
if(gui!=null) gui.displayStartConnection(client.getInetAddress(),client.getPort(),client.getLocalPort());
ClientThread ct=new ClientThread(client);
clients.add(ct);
ct.start();
}else {
if(accept) {
System.out.println(ResourceBundle.getBundle("bundle/Bundle",Locale.getDefault()).getString("clientConnectionFail"));
}
}
}
}else {
System.out.println(ResourceBundle.getBundle("bundle/Bundle",Locale.getDefault()).getString("serverStartFail"));
System.out.println(serverStartStatus.error.toString());
return;
}
if(gui!=null) gui.displayServerStop();
System.out.println(ResourceBundle.getBundle("bundle/Bundle",Locale.getDefault()).getString("serverEnd"));
}
/**
* Stop the current server from accepting new connection.
* Old connection will remain active.
* @see #stopClientsSocket()
*/
public void stopServerSocket() {
server.stop();
accept=false;
}
/**
* Stop all the client connection.
* New connection are still possible.
* @see #stopServerSocket()
*/
public void stopClientsSocket() {
for(int i=0;i<clients.size();i++) {
clients.get(i).setRun(false);
}
}
}

Loading…
Cancel
Save