diff --git a/.gitignore b/.gitignore
index 36020d3..4da5402 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,108 @@
.DS_Store
*.swp
+*.class
bin
project.geany
+P2P_JAVA_PROJECT_SERVER
+P2P_JAVA_PROJECT_CLIENT
+.classpath
+.project
+
+# Created by https://www.gitignore.io/api/java,eclipse
+# Edit at https://www.gitignore.io/?templates=java,eclipse
+
+### 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/
+
+# Scala IDE specific (Scala & Java development for Eclipse)
+.cache-main
+.scala_dependencies
+.worksheet
+
+### Eclipse Patch ###
+# Eclipse Core
+.project
+
+# JDT-specific (Eclipse Java Development Tools)
+.classpath
+
+# Annotation Processing
+.apt_generated
+
+.sts4-cache/
+
+### 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*
+
+# End of https://www.gitignore.io/api/java,eclipse
diff --git a/doc/classdiagram.xml b/doc/classdiagram.xml
index 04352dc..8c652a6 100644
--- a/doc/classdiagram.xml
+++ b/doc/classdiagram.xml
@@ -1 +1 @@
-7VrbcqM4EP0aV808eMqAr4+x4+zsrjObGk/tbB4VUEAbgVxCjs18/bZA3CzFOAlOdmp4SaGDJKM+3ac7DT1nEe5/42gTXDMP05498PY957Jn29PxFP5KIMmA4WyWAT4nXgZZJbAmP7ACBwrdEg/HtYmCMSrIpg66LIqwK2oY4pzt6tPuGa3/6gb5WAPWLqI6+p14IlDHsicl/hkTP8h/2Rqr84Uon6xOEgfIY7sK5Cx7zoIzJrKrcL/AVNout0u27uqJu8WDcRyJUxb8FXu3qP9HePHF36HF9nf0fbru22P1cCLJT4w9MIAaMi4C5rMI0WWJzjnbRh6W2w5gVM5ZMbYB0ALwXyxEothEW8EACkRI1V28J+IfufzTSI1uK3cu92rndJDkg0jwRC1yRjlwmwKDwTQHysXpqLb6BnMSYoF5AXoX0kVgGLEIZ8gVoVTdz0wj7fGkyRUUsy131ayEUX8pPPfrjv75bZncPXwmq/5IuS7iPhZH+LBKz4CIwgwelyewkGOKBHmsPwhSvu0X80r64UJ5gNkbjj3lI6Jb9UtrzB/BXoc+Eu9ISFFqsnsWidxdJIOIEj+CaxdsJC09h/WCQEBdqBtCOsncDQj1VihhW2mOWCD3IR/NA8bJD9gW5f4Ct7lQ3gQeW52xlisVXxzHMOcmp8c6gK7RvjZxhWKhAJdRijYxuSuOEQJTJJozIVhYcwcD+/KEeF+BdP7UXXuoxCDJVU6Nd6W2WGOFBRVdybHWKZ9olGtkU5ISHQvOHgoRlDa6h1BZMMp4GUA5+xTfCwP3IfG8VELiDXJJ5H9LBaNvlcgqXXjplMhXZQUnVR6BBMpYkpRQdIfpDYuJIEzuz7O58w0jkUhNNZr3RpcpwsWCRXAIRFL6MLC/w9IDDMQejY5mthW7ubg2kTs9E7dTA7dzCTBYJjMUiT581OiGw4mC7gN6n894Fu11coc6uRJisPaepnIcgJ/gyEB4ndg5GHMhkwhQbC9gbJXjd2PdGZ7Geu4dr6HdnNMn75LTy/w8nBTAbS0b/z9z80zPzcZ5lvOeuXmmBfOCEnnWLje3k5uns3punrx3arYsjfIuNzdEx0+Tmy27S87vQPtbJmcz73rB/YWBiA+uZALuxLwlMR85dTG3nRPVPHeQ1qsya9is5nrRUy2wGmu4sgSrt0fsol3yRAV2QjF11JmbS6fJK0sntfRGKkzJcn90+O90fYes9FOLqq2xg30s6/g+2fm0fVI/KE7zCk3Qa7su0Tfp50+T6W29jFvuXbxJrdXp/Xn03hm/od6baXe6oD41qJ/B99sHtTGb653y5V5A+o6P5fQ7ymRIyU6GijlrrDU2Gl+TFG9GGl+TVKuAoiPz8hLg1H5K8Z7upVWBidRXVgqjg0rBsaxPo5cVCxOrcatz1wu6tGS9oGsUIR+HXVeovcQyPSwxZ3picYYG5bEm58osli4+XWZpCpXXZBYjv2drDI0N7FYaQx7bRZQh78NacLA0IEApjlCIP0rNDdJPHuzBFyZURwH2c/YXqckql3LHYgdKYnFJOHYF44nsOlUmdg2odv3L0IEy68ewhb6ysXYxOVi7jYhTi4nWGwLj2WHbZ/bSNH8o/Iat2kvzR74FMXyO0aV5U5rXQvAkh8sXDJv7heYwdc4VpobXP30Y/p2qNEg0lYzkEj72M9FKk8EK5PwXF+4GZ3jWt1fPLAzO9l7f+Mro16z7zkDvyH6zus/8eKZ+0fykEq1SHW43sjaEi5eUh51itOtShh6k0aUmz08hMCy/HM4KjvLza2f5Hw==
\ No newline at end of file
+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
\ No newline at end of file
diff --git a/doc/protocol.md b/doc/protocol.md
new file mode 100644
index 0000000..d5b9171
--- /dev/null
+++ b/doc/protocol.md
@@ -0,0 +1,82 @@
+# P2P-JAVA-PROJECT version 1 (Protocol for step 1)
+All messages begins with `P2P-JAVA-PROJECT VERSION 1.0\n` (this version of the protocol).
+
+## Client messages
+- `LIST\n`: ask the server to list files from server root directory
+- `DOWNLOAD\n\n`: ask the server to download file from server root directory. Only one filename is allowed per request.
+
+## Server responses
+- The response to `LIST` request is in the format `LIST\n\n\n[…]\n\n`
+- The response to `DOWNLOAD` request is `LOAD\n\n\n` or `NOT FOUND\n` if the file doesn't exists.
+- The server send a `PROTOCOL ERROR\n` message if it doesn't understands what the client sent.
+- The server send a `INTERNAL ERROR\n` message if it encounters an internal error.
+
+# P2P-JAVA-PROJECT version 1.1 (Binary protocol for step 1)
+
+All strings in the datagram are utf-8 encoded.
+
+```Datagram format
+
+1 byte: [0-7: VERSION(0x11, first quartet is major version, second is minor)]
+1 byte: [8-15: REQUEST/RESPONSE CODE]
+2 bytes: [16-31: RESERVED FOR FUTURE USE]
+4 bytes: [32-63: PAYLOAD SIZE IN BYTES]
+x bytes: [64-xx: PAYLOAD]
+
+```
+
+## Requests and responses codes
+- REQUESTS (msb is 0):
+ - `LIST` (0x00)
+ - `LOAD` (0x01)
+
+- RESPONSES (msb is 1):
+ - `LIST` (0x80)
+ - `LOAD` (0x81)
+ - `VERSION ERROR` (0xC0)
+ - `PROTOCOL ERROR` (0xC1)
+ - `INTERNAL ERROR` (0xC2)
+ - `EMPTY DIRECTORY` (0xC3)
+ - `NOT FOUND` (0xC4)
+ - `EMPTY FILE` (0xC5)
+
+### List
+Payload size for list request is always zero.
+Payload for list response is filenames separated by `\n`. Payload size for list response is never zero.
+
+#### Empty directory
+When directory is empty.
+Payload size for empty directory is always zero.
+
+
+### Load
+#### Not found
+Response when the file requested is not found on the server.
+Payload size for Not found is zero.
+
+
+#### Load response
+Payload contains
+
+```
+8 bytes: [64-127: OFFSET OF FILE CONTENT IN BYTES]
+8 bytes: [128-191: TOTAL FILESIZE]
+4 bytes: [192-223: FILENAME SIZE] (cannot be > to PAYLOAD_SIZE - 20 or be zero)
+y bytes: []
+z bytes: [FILE CONTENT]
+```
+
+#### Load request
+Payload contains only the name of the file to load.
+
+### Other response code (errors)
+#### Version error
+Response when datagram received use wrong version code.
+
+#### Protocol error
+Response when the request cannot be interpreted (but version is correct).
+Payload size for Protocol error is zero
+
+#### Internal error
+Response in internal failure case.
+Payload size for Internal error is zero.
diff --git a/src/clientP2P/ClientManagementUDP.java b/src/clientP2P/ClientManagementUDP.java
new file mode 100644
index 0000000..20f8eb8
--- /dev/null
+++ b/src/clientP2P/ClientManagementUDP.java
@@ -0,0 +1,179 @@
+package clientP2P;
+import exception.InternalError;
+import exception.ProtocolError;
+import exception.SizeError;
+import exception.TransmissionError;
+import exception.VersionError;
+import remoteException.EmptyFile;
+import remoteException.EmptyDirectory;
+import remoteException.InternalRemoteError;
+import remoteException.NotFound;
+import remoteException.ProtocolRemoteError;
+import remoteException.VersionRemoteError;
+import java.net.UnknownHostException;
+import java.util.Scanner;
+import java.net.InetAddress;
+import java.net.DatagramSocket;
+import java.net.SocketException;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.io.File;
+import protocolP2P.ProtocolP2PDatagram;
+import protocolP2P.Payload;
+import protocolP2P.RequestResponseCode;
+import protocolP2P.FileList;
+import protocolP2P.FilePart;
+import protocolP2P.LoadRequest;
+
+/** Implementation of P2P-JAVA-PROJECT CLIENT
+ * @author Louis Royer
+ * @author Flavien Haas
+ * @author JS Auge
+ * @version 1.0
+ */
+public class ClientManagementUDP implements Runnable {
+ private String baseDirectory;
+ private int UDPPort;
+ private String host;
+ private DatagramSocket socket;
+
+
+
+ /** Constructor for UDP implementation, with baseDirectory and UDPPort parameters.
+ * @param baseDirectory the root directory where files are stored
+ * @param host hostname of the server
+ * @param UDPPort the server will listen on this port
+ */
+ public ClientManagementUDP(String baseDirectory, String host, int UDPPort) {
+ this.baseDirectory = baseDirectory;
+ this.host = host;
+ this.UDPPort = UDPPort;
+ try {
+ socket = new DatagramSocket();
+ } catch (SocketException e) {
+ System.err.println("Error: No socket available.");
+ System.exit(-1);
+ }
+ }
+
+ /** Implementation of Runnable
+ */
+ public void run() {
+ try {
+ String[] list = listDirectory();
+ System.out.println("Files present on the server:");
+ for(String listItem: list) {
+ System.out.println(listItem);
+ }
+ System.out.println("Name of the file to download:");
+ Scanner scanner = new Scanner(System.in);
+ String f = scanner.nextLine();
+ download(f);
+ System.out.println("File sucessfully downloaded");
+ } catch (EmptyDirectory e) {
+ System.err.println("Error: Server has no file in directory");
+ } catch (InternalError e) {
+ System.err.println("Error: Client internal error");
+ } catch (UnknownHostException e) {
+ System.err.println("Error: Server host is unknown");
+ } catch (IOException e) {
+ System.err.println("Error: Request cannot be send or response cannot be received");
+ } catch (TransmissionError e) {
+ System.err.println("Error: Message received is too big");
+ } catch (ProtocolError e) {
+ System.err.println("Error: Cannot decode server’s response");
+ } catch (VersionError e) {
+ System.err.println("Error: Server’s response use bad version of the protocol");
+ } catch (SizeError e) {
+ System.err.println("Error: Cannot handle this packets because of internal representation limitations of numbers on the client");
+ } catch (InternalRemoteError e) {
+ System.err.println("Error: Server internal error");
+ } catch (ProtocolRemoteError e) {
+ System.err.println("Error: Server cannot decode client’s request");
+ } catch (VersionRemoteError e) {
+ System.err.println("Error: Server cannot decode this version of the protocol");
+ } catch (NotFound e) {
+ System.err.println("Error: Server has not this file in directory");
+ } catch (EmptyFile e) {
+ System.err.println("Error: File is empty");
+ }
+ }
+
+ /** Try to download a file
+ * @param filename name of the file to download
+ * @throws NotFound
+ * @throws InternalError
+ * @throws UnknownHostException
+ * @throws IOException
+ * @throws TransmissionError
+ * @throws ProtocolError
+ * @throws VersionError
+ * @throws SizeError
+ * @throws InternalRemoteError
+ * @throws ProtocolRemoteError
+ * @throws VersionRemoteError
+ * @throws EmptyFile
+ */
+ private void download(String filename) throws EmptyFile, NotFound, InternalError, UnknownHostException, IOException, TransmissionError, ProtocolError, VersionError, SizeError, InternalRemoteError, ProtocolRemoteError, VersionRemoteError {
+ ProtocolP2PDatagram d = new ProtocolP2PDatagram((Payload) new LoadRequest(filename));
+ d.send(socket, host, UDPPort);
+ try {
+ Payload p = ProtocolP2PDatagram.receive(socket).getPayload();
+ assert p instanceof FilePart : "This payload must be instance of FilePart";
+ if (!(p instanceof FilePart)) {
+ throw new InternalError();
+ } else {
+ FilePart fp = (FilePart)p;
+ if (!fp.getFilename().equals(filename)) {
+ System.err.println("Error: wrong file received");
+ throw new ProtocolError();
+ }
+ if (fp.getOffset() != 0 || fp.getPartialContent().length != fp.getTotalSize()) {
+ System.err.println("offset: " + fp.getOffset() + " ; content.length: " + fp.getPartialContent().length + " ; totalSize: " + fp.getTotalSize());
+ System.err.println("Error: cannot handle partial files (not implemented)");
+ throw new InternalError();
+ }
+ try {
+ Files.write(new File(baseDirectory + filename).toPath(), fp.getPartialContent());
+ } catch (IOException e) {
+ System.err.println("Error: cannot write file (" + baseDirectory + filename + ")");
+ }
+ }
+ } catch (EmptyDirectory e) {
+ throw new ProtocolError();
+ }
+
+ }
+
+ /** list server’s directory content
+ * @return list of files
+ * @throws InternalError
+ * @throws UnknowHostException
+ * @throws IOException
+ * @throws TransmissionError
+ * @throws ProtocolError
+ * @throws VersionError
+ * @throws SizeError
+ * @throws EmptyDirectory
+ * @throws InternalRemoteError
+ * @throws ProtocolRemoteError
+ * @throws VersionRemoteError
+ */
+ private String[] listDirectory() throws EmptyDirectory, InternalError, UnknownHostException, IOException, TransmissionError, ProtocolError, VersionError, SizeError, InternalRemoteError, ProtocolRemoteError, VersionRemoteError {
+ ProtocolP2PDatagram d = new ProtocolP2PDatagram(new Payload(RequestResponseCode.LIST_REQUEST));
+ d.send(socket, host, UDPPort);
+ try {
+ Payload p = ProtocolP2PDatagram.receive(socket).getPayload();
+ assert p instanceof FileList : "This payload must be instance of Filelist";
+ if (!(p instanceof FileList)) {
+ throw new InternalError();
+ } else {
+ return ((FileList)p).getFileList();
+ }
+ } catch (NotFound e) {
+ throw new ProtocolError();
+ } catch (EmptyFile e) {
+ throw new ProtocolError();
+ }
+ }
+}
diff --git a/src/clientP2P/ClientP2P.java b/src/clientP2P/ClientP2P.java
new file mode 100644
index 0000000..8ee0186
--- /dev/null
+++ b/src/clientP2P/ClientP2P.java
@@ -0,0 +1,25 @@
+package clientP2P;
+import clientP2P.ClientManagementUDP;
+import tools.Directories;
+
+
+public class ClientP2P {
+ private String host;
+ private int port;
+ private Directories directories;
+ public ClientP2P() {
+ directories = new Directories("P2P_JAVA_PROJECT_CLIENT");
+ host = "localhost";
+ port = 40001;
+ System.out.println("Client will try to contact server at " + host + " on port " + port + ". It will save files in " + directories.getDataHomeDirectory());
+ directories.askOpenDataHomeDirectory();
+ }
+
+ public static void main(String [] args) {
+ ClientP2P c = new ClientP2P();
+ ClientManagementUDP cm = new ClientManagementUDP(c.directories.getDataHomeDirectory(), c.host, c.port);
+ Thread t = new Thread(cm);
+ t.setName("client P2P-JAVA-PROJECT");
+ t.start();
+ }
+}
diff --git a/src/exception/InternalError.java b/src/exception/InternalError.java
new file mode 100644
index 0000000..9a20633
--- /dev/null
+++ b/src/exception/InternalError.java
@@ -0,0 +1,4 @@
+package exception;
+public class InternalError extends Exception {
+ private static final long serialVersionUID = 11L;
+}
diff --git a/src/exception/ProtocolError.java b/src/exception/ProtocolError.java
new file mode 100644
index 0000000..d5e4811
--- /dev/null
+++ b/src/exception/ProtocolError.java
@@ -0,0 +1,4 @@
+package exception;
+public class ProtocolError extends Exception {
+ private static final long serialVersionUID = 11L;
+}
diff --git a/src/exception/SizeError.java b/src/exception/SizeError.java
new file mode 100644
index 0000000..d0cb07d
--- /dev/null
+++ b/src/exception/SizeError.java
@@ -0,0 +1,5 @@
+package exception;
+/** Used on reception side when size as set in datagram is too big, and we cant store this in a int/long as usual. */
+public class SizeError extends Exception {
+ private static final long serialVersionUID = 11L;
+}
diff --git a/src/exception/TransmissionError.java b/src/exception/TransmissionError.java
new file mode 100644
index 0000000..d95dd4c
--- /dev/null
+++ b/src/exception/TransmissionError.java
@@ -0,0 +1,4 @@
+package exception;
+public class TransmissionError extends Exception {
+ private static final long serialVersionUID = 11L;
+}
diff --git a/src/exception/VersionError.java b/src/exception/VersionError.java
new file mode 100644
index 0000000..f84c811
--- /dev/null
+++ b/src/exception/VersionError.java
@@ -0,0 +1,4 @@
+package exception;
+public class VersionError extends Exception {
+ private static final long serialVersionUID = 11L;
+}
diff --git a/src/protocolP2P/CodeType.java b/src/protocolP2P/CodeType.java
new file mode 100644
index 0000000..e38de49
--- /dev/null
+++ b/src/protocolP2P/CodeType.java
@@ -0,0 +1,13 @@
+package protocolP2P;
+
+/** Request/Response code's type enum.
+ * @author Louis Royer
+ * @author Flavien Haas
+ * @author JS Auge
+ * @version 1.0
+ */
+public enum CodeType {
+ REQUEST,
+ RESPONSE,
+ ERROR
+}
diff --git a/src/protocolP2P/FileList.java b/src/protocolP2P/FileList.java
new file mode 100644
index 0000000..1dc0887
--- /dev/null
+++ b/src/protocolP2P/FileList.java
@@ -0,0 +1,109 @@
+package protocolP2P;
+import java.util.Arrays;
+import protocolP2P.Payload;
+import protocolP2P.RequestResponseCode;
+import exception.TransmissionError;
+import exception.ProtocolError;
+import exception.InternalError;
+import exception.SizeError;
+import java.io.UnsupportedEncodingException;
+
+/** Representation of payload for list response.
+ * @author Louis Royer
+ * @author Flavien Haas
+ * @author JS Auge
+ * @version 1.0
+ */
+public class FileList extends Payload {
+ private String[] fileList;
+
+ /** Constructor (typically used by the server) with an ArrayList parameter containing
+ * filenames.
+ * @param fileList a list of files. Must not be empty.
+ * @throws InternalError
+ */
+ public FileList(String[] fileList) throws InternalError {
+ super(RequestResponseCode.LIST_RESPONSE);
+ /* assert to help debugging */
+ assert fileList.length != 0 : "Payload size of FileList must not be empty, use EmptyDirectory from Payload instead";
+ if (fileList.length == 0) {
+ throw new InternalError();
+ }
+ this.fileList = fileList;
+ }
+
+ /** Constructor (typically used by client) with a byte[] parameter containing the datagram received.
+ * @param datagram the full datagram received
+ * @throws SizeError
+ * @throws InternalError
+ * @throws ProtocolError
+ * @throws TransmissionError
+ */
+ protected FileList(byte[] datagram) throws TransmissionError, SizeError, ProtocolError, InternalError {
+ super(datagram);
+ /* assert to help debugging */
+ assert requestResponseCode == RequestResponseCode.LIST_RESPONSE : "FileList subclass is incompatible with this datagram, request/response code must be checked before using this constructor";
+ /* InternalErrorException */
+ if (requestResponseCode!= RequestResponseCode.LIST_RESPONSE) {
+ throw new InternalError();
+ }
+ int size = getPayloadSize(datagram);
+ try {
+ fileList = (new String(datagram, 8, size, "UTF-8")).split("\n");
+ } catch (UnsupportedEncodingException e) {
+ throw new InternalError();
+ }
+ }
+
+ /** Returns a byte[] containing datagram with padding.
+ * This datagram is still incomplete and should not be send directly.
+ * ProtocolP2PDatagram will use this method to generate the complete datagram.
+ * @return datagram with padding
+ * @throws InternalError
+ */
+ protected byte[] toDatagram() throws InternalError {
+ // compute size
+ int size = 8;
+ for (String s : fileList) {
+ size += s.length();
+ size += 1;
+ }
+ size -=1;
+ byte[] datagram = new byte[size]; // java initialize all to zero
+ // set request/response code
+ datagram[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
+ // bits 16-31 are reserved for future use
+ setPayloadSize(size - 8, datagram);
+ // Write fileList
+ int bCount = 8;
+ for(String s : fileList) {
+ if (bCount != 8) { // not on first iteration
+ try {
+ datagram[bCount] = "\n".getBytes("UTF-8")[0]; // separator
+ } catch (UnsupportedEncodingException e) {
+ throw new InternalError();
+ }
+ bCount += 1;
+ }
+ // Copy filename
+ try {
+ byte[] sb = s.getBytes("UTF-8");
+ for(byte b : sb) {
+ datagram[bCount] = b;
+ bCount += 1;
+ }
+ } catch (UnsupportedEncodingException e) {
+ throw new InternalError();
+ }
+
+ }
+ return datagram;
+ }
+
+ /** fileList getter.
+ * @return fileList
+ */
+ public String[] getFileList() {
+ return fileList;
+ }
+}
diff --git a/src/protocolP2P/FilePart.java b/src/protocolP2P/FilePart.java
new file mode 100644
index 0000000..f8b35b3
--- /dev/null
+++ b/src/protocolP2P/FilePart.java
@@ -0,0 +1,199 @@
+package protocolP2P;
+import protocolP2P.Payload;
+import protocolP2P.RequestResponseCode;
+import exception.ProtocolError;
+import exception.InternalError;
+import exception.SizeError;
+import exception.TransmissionError;
+import tools.BytesArrayTools;
+import java.util.Arrays;
+import java.io.UnsupportedEncodingException;
+
+/** Representation of payload for load response.
+ * @author Louis Royer
+ * @author Flavien Haas
+ * @author JS Auge
+ * @version 1.0
+ */
+public class FilePart extends Payload {
+ private String filename;
+ private long totalSize;
+ private long offset;
+ private byte[] partialContent;
+
+ /** Constructor (typically used by server) with informations about file part to send as parameters.
+ * @param filename name of the file to send
+ * @param totalSize total size of the file to send
+ * @param offset where in the file begins the part we are sending
+ * @param partialContent content of the file we send
+ * @throws InternalError
+ */
+ public FilePart(String filename, long totalSize, long offset, byte[] partialContent) throws InternalError {
+ super(RequestResponseCode.LOAD_RESPONSE);
+ /* asserts to help debugging */
+ assert totalSize >= 0 : "totalSize cannot be negative";
+ assert partialContent.length != 0 : "partialContent.length cannot be zero, see RRCode.EMPTY_FILE";
+ assert totalSize >= partialContent.length : "totalSize must be greater than partialContent.length";
+ assert offset >= 0 : "offset cannot be negative";
+ assert filename != null : "filename is required";
+ if (totalSize < 0 || partialContent.length == 0 || totalSize < partialContent.length
+ || offset < 0 || filename == null) {
+ throw new InternalError();
+ }
+ this.filename = filename;
+ this.totalSize = totalSize;
+ this.offset = offset;
+ this.partialContent = partialContent;
+ }
+
+ /** Constructor (typically used by client) with datagram received as parameter.
+ * @param datagram the full datagram received
+ * @throws SizeError
+ * @throws InternalError
+ * @throws TransmissionError
+ */
+ protected FilePart(byte[] datagram) throws TransmissionError, SizeError, ProtocolError, InternalError {
+ super(datagram);
+ /* assert to help debugging */
+ assert requestResponseCode == RequestResponseCode.LOAD_RESPONSE : "FilePart subclass is incompatible with this datagram, request/response code must be checked before using this constructor";
+ /* InternalErrorException */
+ if (requestResponseCode != RequestResponseCode.LOAD_RESPONSE) {
+ throw new InternalError();
+ }
+ setOffset(datagram); // this can throw SizeError
+ setTotalSize(datagram); // this can throw SizeError
+ setFilename(datagram); // this can throw ProtocolError, SizeError
+ setPartialContent(datagram); // this can throw SizeError
+ }
+
+ /** Returns a byte[] containing datagram with padding.
+ * This datagram is still incomplete and should not be send directly.
+ * ProtocolP2PDatagram will use this method to generate the complete datagram.
+ * @return datagram with padding
+ * @throws InternalError
+ */
+ protected byte[] toDatagram() throws InternalError {
+ // compute payload size
+ int size = 28 + filename.length() + partialContent.length;
+ byte[] datagram = new byte[size]; // java initialize all to zero
+ // set request/response code
+ datagram[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
+ // bits 16-31 are reserved for future use
+ setPayloadSize(size - 8, datagram);
+ // write offset to datagram (Byte 8)
+ BytesArrayTools.write(datagram, 8, offset);
+ // write totalSize to datagram (Byte 16)
+ BytesArrayTools.write(datagram, 16, totalSize);
+ // write filename’s size to datagram
+ BytesArrayTools.write(datagram, 24, filename.length());
+ // write filename to datagram
+ try {
+ byte[] bFilename = filename.getBytes("UTF-8");
+ int i = filename.length() + 24;
+ for (byte b : bFilename) {
+ datagram[i] = b;
+ i += 1;
+ }
+ // write partialContent to datagram
+ for (byte b: partialContent) {
+ datagram[i] = b;
+ i += 1;
+ }
+ return datagram;
+ } catch (UnsupportedEncodingException e) {
+ throw new InternalError();
+ }
+ }
+
+ /** Write from bytes 8 to 15 of datagram into offset.
+ * @param datagram received datagram
+ * @throws SizeError
+ */
+ private void setOffset(byte[] datagram) throws SizeError {
+ offset = BytesArrayTools.readLong(datagram, 8);
+ }
+ /** Write from bytes 16 to 23 of datagram into totalSize.
+ * @param datagram received datagram
+ * @throws SizeError
+ */
+ private void setTotalSize(byte[] datagram) throws SizeError {
+ totalSize = BytesArrayTools.readLong(datagram, 16);
+ }
+
+ /** Read filename’s size from bytes 24 to 27 of datagram.
+ * @param datagram received datagram
+ * @throws ProtocolError
+ * @throws SizeError
+ * @return filename’s size
+ */
+ private int getFilenameSize(byte[] datagram) throws SizeError, ProtocolError {
+ int size = BytesArrayTools.readInt(datagram, 24); // this can throw SizeError
+ // filename size cannot be zero
+ if (size == 0) {
+ throw new ProtocolError();
+ }
+ // offset (8B) + totalSize (8B) + filenameSize (4B) = 20B
+ if ((20 + size) > getPayloadSize(datagram)) {
+ throw new ProtocolError();
+ }
+ return size;
+ }
+
+ /** Write filename from byte 28 to byte (28 + (filenameSize - 1)) of datagram.
+ * @param datagram received datagram
+ * @throws ProtocolError
+ * @throws SizeError
+ * @throws InternalError
+ */
+ private void setFilename(byte[] datagram) throws ProtocolError, SizeError, InternalError {
+ int filenameSize = getFilenameSize(datagram); // this can throw ProtocolError or SizeError
+ try {
+ filename = new String(datagram, 28, filenameSize, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new InternalError();
+ }
+ }
+
+ /** Write partialContent from byte (28 + filenameSize) to byte (8 + payloadSize) of datagram.
+ * @param datagram received datagram
+ * @throws SizeError
+ * @throws ProtocolError
+ */
+ private void setPartialContent(byte[] datagram) throws ProtocolError, SizeError {
+ int start = 28 + getFilenameSize(datagram); // this can throw SizeError or ProtocolError
+ int end = 8 + getPayloadSize(datagram); // this can throw SizeError
+ try {
+ partialContent = Arrays.copyOfRange(datagram, start, end);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new ProtocolError();
+ }
+ }
+
+ /** partialContent getter.
+ * @return partialcontent
+ */
+ public byte[] getPartialContent() {
+ return partialContent;
+ }
+
+ /** filename getter.
+ * @return String
+ */
+ public String getFilename() {
+ return filename;
+ }
+
+ /** offset getter.
+ * @return offset
+ */
+ public long getOffset() {
+ return offset;
+ }
+
+ /** totalSize getter.
+ * @return totalSize
+ */
+ public long getTotalSize() {
+ return totalSize;
+ }
+}
diff --git a/src/protocolP2P/LoadRequest.java b/src/protocolP2P/LoadRequest.java
new file mode 100644
index 0000000..af3082f
--- /dev/null
+++ b/src/protocolP2P/LoadRequest.java
@@ -0,0 +1,90 @@
+package protocolP2P;
+import protocolP2P.Payload;
+import protocolP2P.RequestResponseCode;
+import exception.TransmissionError;
+import exception.ProtocolError;
+import exception.InternalError;
+import exception.SizeError;
+import java.io.UnsupportedEncodingException;
+
+/** Representation of payload for load request.
+ * @author Louis Royer
+ * @author Flavien Haas
+ * @author JS Auge
+ * @version 1.0
+ */
+public class LoadRequest extends Payload {
+ private String filename;
+
+ /** Constructor (typically used by the server) with a filename parameter.
+ * @param filename name of the file to download. Must not be empty.
+ * @throws InternalError
+ */
+ public LoadRequest(String filename) throws InternalError {
+ super(RequestResponseCode.LOAD_REQUEST);
+ /* assert to help debugging */
+ assert filename.length() != 0 : "Payload size of LoadRequest must not be empty";
+ if (filename.length() == 0) {
+ throw new InternalError();
+ }
+ this.filename = filename;
+ }
+
+ /** Constructor (typically used by client) with a byte[] parameter containing the datagram received.
+ * @param datagram the full datagram received
+ * @throws SizeError
+ * @throws InternalError
+ * @throws ProtocolError
+ * @throws TransmissionError
+ */
+ protected LoadRequest(byte[] datagram) throws TransmissionError, SizeError, ProtocolError, InternalError {
+ super(datagram);
+ /* assert to help debugging */
+ assert requestResponseCode == RequestResponseCode.LOAD_REQUEST : "LoadRequest subclass is incompatible with this datagram, request/response code must be checked before using this constructor";
+ /* InternalErrorException */
+ if (requestResponseCode!= RequestResponseCode.LOAD_REQUEST) {
+ throw new InternalError();
+ }
+ int size = getPayloadSize(datagram);
+ try {
+ filename = new String(datagram, 8, size, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new InternalError();
+ }
+ }
+
+ /** Returns a byte[] containing datagram with padding.
+ * This datagram is still incomplete and should not be send directly.
+ * ProtocolP2PDatagram will use this method to generate the complete datagram.
+ * @return datagram with padding
+ * @throws InternalError
+ */
+ protected byte[] toDatagram() throws InternalError {
+ // compute size
+ int size = 8 + filename.length();
+ byte[] datagram = new byte[size]; // java initialize all to zero
+ // set request/response code
+ datagram[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
+ // bits 16-31 are reserved for future use
+ setPayloadSize(size - 8, datagram);
+ // Write filename
+ int bCount = 8;
+ try {
+ byte[] sb = filename.getBytes("UTF-8");
+ for(byte b : sb) {
+ datagram[bCount] = b;
+ bCount += 1;
+ }
+ } catch (UnsupportedEncodingException e) {
+ throw new InternalError();
+ }
+ return datagram;
+ }
+
+ /** filename getter.
+ * @return filename
+ */
+ public String getFilename() {
+ return filename;
+ }
+}
diff --git a/src/protocolP2P/Payload.java b/src/protocolP2P/Payload.java
new file mode 100644
index 0000000..c3638f6
--- /dev/null
+++ b/src/protocolP2P/Payload.java
@@ -0,0 +1,116 @@
+package protocolP2P;
+import protocolP2P.RequestResponseCode;
+import protocolP2P.FilePart;
+import protocolP2P.FileList;
+import protocolP2P.LoadRequest;
+import exception.ProtocolError;
+import exception.InternalError;
+import exception.TransmissionError;
+import exception.SizeError;
+import tools.BytesArrayTools;
+/** Representation of payload. If payload has a size, use subclasses instead.
+ * @author Louis Royer
+ * @author Flavien Haas
+ * @author JS Auge
+ * @version 1.0
+ */
+public class Payload {
+ protected RequestResponseCode requestResponseCode;
+ protected final static int PAYLOAD_SIZE_POSITION = 4;
+ protected final static int PAYLOAD_START_POSITION = 8;
+
+ /** Consructor used to create Payload with a payload size of zero using a RRCode.
+ * @param requestResponseCode Request/Response code associated with the payload
+ * @throws InternalError
+ */
+ public Payload(RequestResponseCode requestResponseCode) throws InternalError {
+ /* asserts to help debugging */
+ assert requestResponseCode != RequestResponseCode.LIST_RESPONSE || (this instanceof FileList) : "LIST_RESPONSE must use FilePart class";
+ assert requestResponseCode != RequestResponseCode.LOAD_RESPONSE || (this instanceof FilePart) : "LOAD_RESPONSE must use FileList class";
+ assert requestResponseCode != RequestResponseCode.LOAD_REQUEST || (this instanceof LoadRequest) : "LOAD_REQUEST must use LoadRequest class";
+ this.requestResponseCode = requestResponseCode;
+ checkRequestResponseCode(); // this can throw InternalError
+ }
+
+ /** Constructor used to create a Payload (when no more specific subclasses exists) using datagram as parameter.
+ * If payload size is not empty, using subclass is required.
+ * @param datagram the full datagram received
+ * @throws ProtocolError
+ * @throws InternalError
+ * @throws TransmissionError
+ * @throws SizeError
+ */
+ protected Payload(byte[] datagram) throws SizeError, ProtocolError, InternalError, TransmissionError {
+ /* asserts to help debugging */
+ assert getPayloadSize(datagram) + 8 <= datagram.length : "Payload is truncated";
+ if (datagram.length < getPayloadSize(datagram) + 8) {
+ throw new TransmissionError();
+ }
+ assert RequestResponseCode.fromCode(datagram[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LIST_RESPONSE || (this instanceof FileList) : "LIST_RESPONSE must use FilePart class";
+ assert RequestResponseCode.fromCode(datagram[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LOAD_RESPONSE || (this instanceof FilePart) : "LOAD_RESPONSE must use FileList class";
+ assert RequestResponseCode.fromCode(datagram[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LOAD_REQUEST || (this instanceof LoadRequest) : "LOAD_REQUEST must use LoadRequest class";
+ requestResponseCode = RequestResponseCode.fromCode(datagram[RequestResponseCode.RRCODE_POSITION]);
+ checkRequestResponseCode(); // this can throw InternalError
+ }
+
+ /** Used to check RRCode used is compatible with this class use, or if a more specific subclass is required.
+ * @throws InternalError
+ */
+ private void checkRequestResponseCode() throws InternalError {
+ /* Incorrect use cases (use subclasses instead) */
+ if ((requestResponseCode == RequestResponseCode.LIST_RESPONSE && !(this instanceof FileList))
+ || (requestResponseCode == RequestResponseCode.LOAD_RESPONSE && !(this instanceof FilePart))
+ || (requestResponseCode == RequestResponseCode.LOAD_REQUEST && !(this instanceof LoadRequest))) {
+ throw new InternalError();
+ }
+ }
+
+ /** Returns a byte[] containing datagram with padding.
+ * This datagram is still incomplete and should not be send directly.
+ * ProtocolP2PDatagram will use this method to generate the complete datagram.
+ * @return datagram with padding
+ * @throws InternalError
+ */
+ protected byte[] toDatagram() throws InternalError {
+ // InternalError is impossible in this method on Payload class but still on subclasses
+ byte [] datagram = new byte[8]; // java initialize all to zero
+ // size is zero (and this is the default)
+ // set request/response code
+ datagram[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
+ // bits 16-31 are reserved for future use
+ // payload size is 0 (this is what java have initialized datagram)
+ return datagram;
+ }
+
+ /** Set payload’s size in a datagram.
+ * @param size integer representing payload size
+ * @param datagram datagram to be completed
+ * @throws InternalError
+ */
+ protected static void setPayloadSize(int size, byte[] datagram) throws InternalError {
+ /* assert to help debugging */
+ assert size >= 0: "Payload size cannot be negative";
+ if (size < 0) {
+ // We don't throw SizeError
+ // because this is only for reception side
+ throw new InternalError();
+ }
+ BytesArrayTools.write(datagram, PAYLOAD_SIZE_POSITION, size);
+ }
+
+ /** Get payload’s size from a datagram.
+ * @param datagram the full datagram received
+ * @return integer representing payload size
+ * @throws SizeError
+ */
+ protected static int getPayloadSize(byte[] datagram) throws SizeError {
+ return BytesArrayTools.readInt(datagram, PAYLOAD_SIZE_POSITION);
+ }
+
+ /** RRCode getter.
+ * @return Request/Response code
+ */
+ public RequestResponseCode getRequestResponseCode() {
+ return requestResponseCode;
+ }
+}
diff --git a/src/protocolP2P/ProtocolP2PDatagram.java b/src/protocolP2P/ProtocolP2PDatagram.java
new file mode 100644
index 0000000..1c31073
--- /dev/null
+++ b/src/protocolP2P/ProtocolP2PDatagram.java
@@ -0,0 +1,243 @@
+package protocolP2P;
+import exception.InternalError;
+import exception.ProtocolError;
+import exception.SizeError;
+import exception.TransmissionError;
+import exception.VersionError;
+import remoteException.EmptyDirectory;
+import remoteException.InternalRemoteError;
+import remoteException.NotFound;
+import remoteException.ProtocolRemoteError;
+import remoteException.VersionRemoteError;
+import remoteException.EmptyFile;
+import protocolP2P.Payload;
+import protocolP2P.RequestResponseCode;
+import protocolP2P.LoadRequest;
+import protocolP2P.FileList;
+import protocolP2P.FilePart;
+import java.util.ArrayList;
+import java.lang.Byte;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.io.IOException;
+import java.net.UnknownHostException;
+
+/** Representation of datagram.
+ * @author Louis Royer
+ * @author Flavien Haas
+ * @author JS Auge
+ * @version 1.0
+ */
+public class ProtocolP2PDatagram {
+ private final static byte PROTOCOL_VERSION = 0x11;
+ private final static int VERSION_POSITON = 0;
+ private byte version;
+ private Payload payload;
+ private InetAddress hostR;
+ private int portR;
+
+ /** Constructor with payload parameter (typically used when sending datagram).
+ * @param payload the payload associated with the datagram to send
+ */
+ public ProtocolP2PDatagram(Payload payload) {
+ version = PROTOCOL_VERSION;
+ this.payload = payload;
+ }
+
+ /** Send datagram on socket (from client)
+ * @param socket DatagramSocket used to send datagram.
+ * @param host host to send datagram (null if this is a response)
+ * @param port port to send datagram (null if this is a response)
+ * @throws InternalError
+ * @throws UnknownHostException
+ * @throws IOException
+ */
+ public void send(DatagramSocket socket, String host, int port) throws InternalError, UnknownHostException, IOException {
+ InetAddress dst = InetAddress.getByName(host);
+ byte[] datagram = toDatagram();
+ // generate DatagramPacket
+ DatagramPacket datagramPacket = new DatagramPacket(datagram, datagram.length, dst, port);
+ // send it
+ socket.send(datagramPacket);
+ }
+
+ /** Send datagram on socket (from server, as a response)
+ * @param socket DatagramSocket used to send datagram.
+ * @param received datagram to respond (aka request)
+ * @throws InternalError
+ * @throws IOException
+ */
+ public void send(DatagramSocket socket, ProtocolP2PDatagram received) throws InternalError, IOException {
+ assert received.getPortR() != 0 && received.getHostR() != null : "This method should be used only as response to a request";
+ if (received.getPortR() == 0 || received.getHostR() == null) {
+ throw new InternalError();
+ }
+ byte[] datagram = toDatagram();
+ // generate DatagramPacket
+ DatagramPacket datagramPacket = new DatagramPacket(datagram, datagram.length, received.getHostR(), received.getPortR());
+ socket.send(datagramPacket);
+ }
+
+ /** Send a response.
+ * @param socket DatagramSocket used to send response
+ * @param host host to send response
+ * @param port port to send response
+ * @throws InternalError
+ * @throws IOException
+ */
+ protected void sendResponse(DatagramSocket socket, InetAddress host, int port) throws InternalError, IOException {
+ assert port != 0 && host != null : "This method should be used only as response to a request";
+ if (port == 0 || host == null) {
+ throw new InternalError();
+ }
+ byte[] datagram = toDatagram();
+ DatagramPacket datagramPacket = new DatagramPacket(datagram, datagram.length, host, port);
+ socket.send(datagramPacket);
+ }
+
+ /** Receive datagram on socket
+ * @param socket DatagramSocket used to receive datagram
+ * @throws TransmissionError
+ * @throws ProtocolError
+ * @throws VersionError
+ * @throws InternalError
+ * @throws SizeError
+ * @throws ProtocolRemoteError
+ * @throws VersionRemoteError
+ * @throws InternalRemoteError
+ * @throws EmptyDirectory
+ * @throws NotFound
+ * @throws IOException
+ * @throws EmptyFile
+ */
+ public static ProtocolP2PDatagram receive(DatagramSocket socket) throws EmptyFile, NotFound, EmptyDirectory, InternalRemoteError, VersionRemoteError, ProtocolRemoteError, TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException {
+ // reception
+ byte[] datagram = new byte[4096];
+ DatagramPacket reception = new DatagramPacket(datagram, datagram.length);
+ socket.receive(reception);
+ // contruction
+ try {
+ ProtocolP2PDatagram p = new ProtocolP2PDatagram(datagram);
+ p.setHostR(reception.getAddress());
+ p.setPortR(reception.getPort());
+ Payload payload = p.getPayload();
+ switch (payload.getRequestResponseCode()) {
+ case PROTOCOL_ERROR :
+ throw new ProtocolRemoteError();
+ case VERSION_ERROR :
+ throw new VersionRemoteError();
+ case INTERNAL_ERROR :
+ throw new InternalRemoteError();
+ case EMPTY_DIRECTORY :
+ throw new EmptyDirectory();
+ case NOT_FOUND :
+ throw new NotFound();
+ case EMPTY_FILE:
+ throw new EmptyFile();
+ default :
+ return p;
+ }
+ } catch (TransmissionError e) {
+ (new ProtocolP2PDatagram(new Payload(RequestResponseCode.INTERNAL_ERROR))).sendResponse(socket, reception.getAddress(), reception.getPort());
+ throw e;
+ } catch (ProtocolError e) {
+ (new ProtocolP2PDatagram(new Payload(RequestResponseCode.PROTOCOL_ERROR))).sendResponse(socket, reception.getAddress(), reception.getPort());
+ throw e;
+ } catch (VersionError e) {
+ (new ProtocolP2PDatagram(new Payload(RequestResponseCode.VERSION_ERROR))).sendResponse(socket, reception.getAddress(), reception.getPort());
+ throw e;
+ } catch (InternalError e) {
+ (new ProtocolP2PDatagram(new Payload(RequestResponseCode.INTERNAL_ERROR))).sendResponse(socket, reception.getAddress(), reception.getPort());
+ throw e;
+ } catch (SizeError e) {
+ (new ProtocolP2PDatagram(new Payload(RequestResponseCode.INTERNAL_ERROR))).sendResponse(socket, reception.getAddress(), reception.getPort());
+ throw e;
+ }
+ }
+ /** Private constructor with datagram as byte[] parameter (typically used when receiving datagram).
+ * @param datagram the full datagram received
+ * @throws TransmissionError
+ * @throws ProtocolError
+ * @throws VersionError
+ * @throws InternalError
+ * @throws SizeError
+ */
+ private ProtocolP2PDatagram(byte[] datagram) throws TransmissionError, ProtocolError, VersionError, InternalError, SizeError {
+ // unwrap version
+ version = datagram[VERSION_POSITON];
+ checkProtocolVersion(); // this can throw VersionError
+ RequestResponseCode r = RequestResponseCode.fromCode(datagram[RequestResponseCode.RRCODE_POSITION]); // this can throw ProtocolError
+ switch (r) {
+ case LIST_RESPONSE:
+ payload = (Payload) new FileList(datagram);
+ break;
+ case LOAD_RESPONSE:
+ payload = (Payload) new FilePart(datagram);
+ break;
+ case LOAD_REQUEST:
+ payload = (Payload) new LoadRequest(datagram);
+ break;
+ default:
+ payload = new Payload(datagram); // this can throw TransmissionError
+ break;
+ }
+ }
+
+ /** Returns a byte[] containing full datagram (typically used when sending datagram).
+ * This datagram is still complete and ready to be send.
+ * @return the full datagram to send
+ * @throws InternalError
+ */
+ protected byte[] toDatagram() throws InternalError {
+ byte[] datagram = payload.toDatagram();
+ datagram[VERSION_POSITON] = version;
+ return datagram;
+
+ }
+
+ /** Returns Payload associated with the datagram.
+ * @return payload associated with the datagram
+ */
+ public Payload getPayload() {
+ return payload;
+ }
+
+ /** Used to check protocol version when a datagram is constructed from bytes[].
+ * @throws VersionError
+ */
+ private void checkProtocolVersion() throws VersionError {
+ if (PROTOCOL_VERSION != version) {
+ throw new VersionError();
+ }
+ }
+
+ /** portR getter.
+ * @return portR
+ */
+ protected int getPortR() {
+ return portR;
+ }
+
+ /** portR setter.
+ * @param portR portR
+ */
+ protected void setPortR(int portR) {
+ this.portR = portR;
+ }
+
+ /** hostR getter.
+ * @return hostR
+ */
+ protected InetAddress getHostR() {
+ return hostR;
+ }
+
+ /** hostH setter.
+ * @param hostR hostR
+ */
+ protected void setHostR(InetAddress hostR) {
+ this.hostR = hostR;
+ }
+}
+
diff --git a/src/protocolP2P/RequestResponseCode.java b/src/protocolP2P/RequestResponseCode.java
new file mode 100644
index 0000000..8b4e20f
--- /dev/null
+++ b/src/protocolP2P/RequestResponseCode.java
@@ -0,0 +1,65 @@
+package protocolP2P;
+import protocolP2P.CodeType;
+import exception.ProtocolError;
+import java.util.HashMap;
+import java.util.Map;
+import java.lang.Byte;
+
+/** Request/Response code enum.
+ * @author Louis Royer
+ * @author Flavien Haas
+ * @author JS Auge
+ * @version 1.0
+ */
+public enum RequestResponseCode {
+ LIST_REQUEST(CodeType.REQUEST, (byte)0x00),
+ LOAD_REQUEST(CodeType.REQUEST, (byte)0x01),
+ LIST_RESPONSE(CodeType.RESPONSE, (byte)0x80),
+ LOAD_RESPONSE(CodeType.RESPONSE, (byte)0x81),
+ VERSION_ERROR(CodeType.ERROR, (byte)0xC0),
+ PROTOCOL_ERROR(CodeType.ERROR, (byte)0xC1),
+ INTERNAL_ERROR(CodeType.ERROR, (byte)0xC2),
+ EMPTY_DIRECTORY(CodeType.ERROR, (byte)0xC3),
+ NOT_FOUND(CodeType.ERROR, (byte)0xC4),
+ EMPTY_FILE(CodeType.ERROR, (byte)0xC5);
+
+ public final CodeType codeType;
+ public final byte codeValue;
+ protected final static int RRCODE_POSITION = 1;
+
+ /* To be able to convert code to enum */
+ private static final Map BY_CODE = new HashMap<>();
+ /* Initialization of HashMap */
+ static {
+ for (RequestResponseCode r: values()) {
+ assert !BY_CODE.containsKey(Byte.valueOf(r.codeValue)) : "Duplicate in " + RequestResponseCode.class.getCanonicalName();
+ BY_CODE.put(Byte.valueOf(r.codeValue), r);
+ }
+ }
+
+ /** Private constructor
+ * @param codeType type of code (request or response)
+ * @param codeValue value of the element in datagram
+ * @return enum element
+ */
+ private RequestResponseCode(CodeType codeType, byte codeValue) {
+ this.codeType = codeType;
+ this.codeValue = codeValue;
+ }
+
+ /** Gives enum from datagram code.
+ * @param code value of the element in datagram
+ * @return enum element
+ */
+ protected static RequestResponseCode fromCode(byte code) throws ProtocolError {
+ RequestResponseCode r= BY_CODE.get(Byte.valueOf(code));
+ if (r == null) {
+ throw new ProtocolError();
+ }
+ return r;
+ }
+
+
+}
+
+
diff --git a/src/remoteException/EmptyDirectory.java b/src/remoteException/EmptyDirectory.java
new file mode 100644
index 0000000..39146fd
--- /dev/null
+++ b/src/remoteException/EmptyDirectory.java
@@ -0,0 +1,4 @@
+package remoteException;
+public class EmptyDirectory extends Exception {
+ private static final long serialVersionUID = 11L;
+}
diff --git a/src/remoteException/EmptyFile.java b/src/remoteException/EmptyFile.java
new file mode 100644
index 0000000..a05cdd7
--- /dev/null
+++ b/src/remoteException/EmptyFile.java
@@ -0,0 +1,4 @@
+package remoteException;
+public class EmptyFile extends Exception {
+ private static final long serialVersionUID = 11L;
+}
diff --git a/src/remoteException/InternalRemoteError.java b/src/remoteException/InternalRemoteError.java
new file mode 100644
index 0000000..9ca91ea
--- /dev/null
+++ b/src/remoteException/InternalRemoteError.java
@@ -0,0 +1,4 @@
+package remoteException;
+public class InternalRemoteError extends Exception {
+ private static final long serialVersionUID = 11L;
+}
diff --git a/src/remoteException/NotFound.java b/src/remoteException/NotFound.java
new file mode 100644
index 0000000..40c9507
--- /dev/null
+++ b/src/remoteException/NotFound.java
@@ -0,0 +1,4 @@
+package remoteException;
+public class NotFound extends Exception {
+ private static final long serialVersionUID = 11L;
+}
diff --git a/src/remoteException/ProtocolRemoteError.java b/src/remoteException/ProtocolRemoteError.java
new file mode 100644
index 0000000..6199f8f
--- /dev/null
+++ b/src/remoteException/ProtocolRemoteError.java
@@ -0,0 +1,4 @@
+package remoteException;
+public class ProtocolRemoteError extends Exception {
+ private static final long serialVersionUID = 11L;
+}
diff --git a/src/remoteException/VersionRemoteError.java b/src/remoteException/VersionRemoteError.java
new file mode 100644
index 0000000..4c372c9
--- /dev/null
+++ b/src/remoteException/VersionRemoteError.java
@@ -0,0 +1,4 @@
+package remoteException;
+public class VersionRemoteError extends Exception {
+ private static final long serialVersionUID = 11L;
+}
diff --git a/src/serverP2P/ServerManagementUDP.java b/src/serverP2P/ServerManagementUDP.java
new file mode 100644
index 0000000..b7e1cbb
--- /dev/null
+++ b/src/serverP2P/ServerManagementUDP.java
@@ -0,0 +1,160 @@
+package serverP2P;
+import java.util.Vector;
+import java.io.File;
+import java.io.IOException;
+import java.net.DatagramSocket;
+import java.net.SocketException;
+import java.nio.file.Paths;
+import java.nio.file.Files;
+import protocolP2P.ProtocolP2PDatagram;
+import protocolP2P.RequestResponseCode;
+import protocolP2P.Payload;
+import protocolP2P.LoadRequest;
+import protocolP2P.FileList;
+import protocolP2P.FilePart;
+import exception.InternalError;
+import exception.ProtocolError;
+import exception.SizeError;
+import exception.TransmissionError;
+import exception.VersionError;
+import remoteException.EmptyDirectory;
+import remoteException.InternalRemoteError;
+import remoteException.NotFound;
+import remoteException.ProtocolRemoteError;
+import remoteException.VersionRemoteError;
+import remoteException.EmptyFile;
+import java.util.Arrays;
+
+
+/** Implementation of P2P-JAVA-PROJECT VERSION 1.0 protocol for UDP.
+ * @author Louis Royer
+ * @author Flavien Haas
+ * @author JS Auge
+ * @version 1.0
+ */
+public class ServerManagementUDP implements Runnable {
+
+ private String[] fileList;
+ private String baseDirectory;
+ private int UDPPort;
+ private DatagramSocket socket;
+
+ /** Constructor for UDP implementation, with baseDirectory and UDPPort parameters.
+ * @param baseDirectory the root directory where files are stored
+ * @param UDPPort the server will listen on this port
+ */
+ public ServerManagementUDP(String baseDirectory, int UDPPort) {
+ this.baseDirectory = baseDirectory;
+ this.UDPPort = UDPPort;
+ initFileList();
+ try {
+ socket = new DatagramSocket(UDPPort);
+ } catch (SocketException e) {
+ System.err.println("Error: cannot listen on port " + UDPPort);
+ System.exit(-1);
+ }
+ }
+
+ /** Implementation of runnable. This methods allows to run the server.
+ */
+ public void run() {
+ while(true) {
+ try {
+ ProtocolP2PDatagram pd = ProtocolP2PDatagram.receive(socket);
+ Payload p = pd.getPayload();
+ switch (p.getRequestResponseCode()) {
+ case LOAD_REQUEST:
+ System.out.println("Received LOAD_REQUEST");
+ assert p instanceof LoadRequest : "payload must be an instance of LoadRequest";
+ if (!(p instanceof LoadRequest)) {
+ sendInternalError(pd);
+ } else {
+ String filename = ((LoadRequest)p).getFilename();
+ try {
+ byte[] load = Files.readAllBytes(Paths.get(baseDirectory + filename));
+ if (Arrays.binarySearch(fileList, filename) >= 0) {
+ try {
+ if (load.length == 0) {
+ (new ProtocolP2PDatagram(new Payload(RequestResponseCode.EMPTY_FILE))).send(socket, pd);
+ } else {
+ (new ProtocolP2PDatagram((Payload)(new FilePart(filename, load.length, 0, load)))).send(socket, pd);
+ }
+ } catch (Exception e2) {
+ System.err.println(e2);
+ }
+ } else {
+ throw new IOException(); // to send a NOT_FOUND in the catch block
+ }
+ } catch (IOException e) {
+ try {
+ (new ProtocolP2PDatagram(new Payload(RequestResponseCode.NOT_FOUND))).send(socket, pd);
+ } catch (Exception e2) {
+ System.err.println(e2);
+ }
+ }
+ }
+ break;
+ case LIST_REQUEST:
+ System.out.println("Received LIST_REQUEST");
+ try {
+ if (fileList.length == 0) {
+ System.err.println("Sending EMPTY_DIRECTORY");
+ (new ProtocolP2PDatagram(new Payload(RequestResponseCode.EMPTY_DIRECTORY))).send(socket, pd);
+ } else {
+ System.out.println("Sending LIST_RESPONSE");
+ (new ProtocolP2PDatagram((Payload)(new FileList(fileList)))).send(socket, pd);
+ }
+ } catch (Exception e2) {
+ System.err.println(e2);
+ }
+ break;
+ default:
+ sendInternalError(pd);
+ }
+ } catch (NotFound e) {
+ } catch (EmptyDirectory e) {
+ } catch (InternalRemoteError e) {
+ } catch (VersionRemoteError e) {
+ } catch (ProtocolRemoteError e) {
+ } catch (IOException e) {
+ } catch (TransmissionError e) {
+ } catch (ProtocolError e) {
+ } catch (VersionError e) {
+ } catch (InternalError e) {
+ } catch (SizeError e) {
+ } catch (EmptyFile e) {
+ }
+ }
+ }
+
+ /** Initialize local list of all files allowed to be shared.
+ */
+ private void initFileList() {
+ File folder = new File(baseDirectory);
+ Vector v = new Vector();
+ File[] files = folder.listFiles();
+ /* Add non-recursively files's names to fileList */
+ for (File f : files) {
+ if (f.isFile()) {
+ v.add(f.getName());
+ }
+ }
+ fileList = new String[v.size()];
+ v.toArray(fileList);
+ }
+
+
+
+ /** Send an internal error message.
+ * @param pd ProtocolP2PDatagram to respond
+ */
+ private void sendInternalError(ProtocolP2PDatagram pd) {
+ try {
+ (new ProtocolP2PDatagram(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(socket, pd);
+ } catch (Exception e) {
+ System.err.println(e);
+ }
+ }
+
+}
+
diff --git a/src/serverP2P/ServerP2P.java b/src/serverP2P/ServerP2P.java
new file mode 100644
index 0000000..b22c715
--- /dev/null
+++ b/src/serverP2P/ServerP2P.java
@@ -0,0 +1,23 @@
+package serverP2P;
+import serverP2P.ServerManagementUDP;
+import tools.Directories;
+
+public class ServerP2P {
+ private int port;
+ private Directories directories;
+
+ public ServerP2P() {
+ directories = new Directories("P2P_JAVA_PROJECT_SERVER");
+ port = 40001;
+ System.out.println("Server will listen on port " + port + " and serve files from " + directories.getDataHomeDirectory());
+ directories.askOpenDataHomeDirectory();
+ }
+ public static void main(String [] args) {
+ ServerP2P s = new ServerP2P();
+ ServerManagementUDP sm = new ServerManagementUDP(s.directories.getDataHomeDirectory(), s.port);
+ Thread t = new Thread(sm);
+ t.setName("server P2P-JAVA-PROJECT");
+ t.start();
+ }
+
+}
diff --git a/src/tools/BytesArrayTools.java b/src/tools/BytesArrayTools.java
new file mode 100644
index 0000000..3813349
--- /dev/null
+++ b/src/tools/BytesArrayTools.java
@@ -0,0 +1,66 @@
+package tools;
+import exception.SizeError;
+/** Helper to manipulate byte[].
+ * @author Louis Royer
+ * @author Flavien Haas
+ * @author JS Auge
+ * @version 1.0
+ */
+public class BytesArrayTools {
+ /** Write int in a bytearray
+ * @param array the array to write
+ * @param start where to begin writting
+ * @param value int to write
+ */
+ public static void write(byte[] array, int start, int value) {
+ for(int i=0;i<4;i++) {
+ array[start + i] = (byte) ((value >> (8 * (3 - i))) & 0xFF);
+ }
+ }
+ /** Write long in a bytearray
+ * @param array the array to write
+ * @param start where to begin writting
+ * @param value long to write
+ */
+ public static void write(byte[] array, int start, long value) {
+ for(int i=0;i<8;i++) {
+ array[start + i] = (byte) ((value >> (8 * (7 - i))) & 0xFF);
+ }
+ }
+
+ /** Read int from a bytearray
+ * @param array the array to read
+ * @param start where to begin reading
+ * @return value read as int
+ */
+ public static int readInt(byte[] array, int start) throws SizeError {
+ int size = 0;
+ for(int i=0;i<4;i++) {
+ size |= ((int)array[start + i]) << (8* (3 -i));
+ }
+ if (size < 0) {
+ // Size in array is probably correct
+ // but we cannot store it into a int (or this will be negative)
+ throw new SizeError();
+ }
+ return size;
+ }
+ /** Read long from a bytearray
+ * @param array the array to read
+ * @param start where to begin reading
+ * @return value read as long
+ */
+ public static long readLong(byte[] array, int start) throws SizeError {
+ long size = 0;
+ for(int i=0;i<8;i++) {
+ size |= ((int)array[start + i]) << (8* (7 - i));
+ }
+ if (size < 0) {
+ // Size in array is probably correct
+ // but we cannot store it into a int (or this will be negative)
+ throw new SizeError();
+ }
+ return size;
+ }
+
+}
diff --git a/src/tools/Directories.java b/src/tools/Directories.java
new file mode 100644
index 0000000..da6363f
--- /dev/null
+++ b/src/tools/Directories.java
@@ -0,0 +1,89 @@
+package tools;
+import java.util.Scanner;
+import java.io.File;
+import java.lang.Runtime;
+import java.io.IOException;
+
+
+/** Helper to get application directories.
+ * @author Louis Royer
+ * @author Flavien Haas
+ * @author JS Auge
+ * @version 1.0
+ */
+public class Directories {
+ private String projectName;
+ private String dataHomeDirectory;
+ private String os;
+
+ /** Constructor with projectName parameter.
+ * @param projectName name of the project
+ */
+ public Directories(String projectName) {
+ this.projectName = projectName;
+ os = System.getProperty("os.name");
+ setDataHomeDirectory();
+ }
+
+ /** Setter for dataHomeDirectory. Will create the directory if not already exists.
+ */
+ private void setDataHomeDirectory() {
+ /* Follow XDG Base Directory Specification
+ * https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
+ */
+ if (os.equals("Linux")) {
+ dataHomeDirectory = System.getenv().get("XDG_DATA_HOME");
+ if (dataHomeDirectory == null || dataHomeDirectory.equals("")) {
+ dataHomeDirectory = System.getProperty("user.home") + "/.local/share";
+ }
+ } else if (os.equals("Mac")||os.equals("Mac OS X")) {
+ /* Apple MacOS X User Data Directory
+ * https://developer.apple.com/library/archive/qa/qa1170/_index.html */
+ dataHomeDirectory = System.getProperty("user.home") + "/Library";
+ } else {
+ dataHomeDirectory = ".";
+ }
+ dataHomeDirectory += "/" + projectName + "/";
+ // create directory if not already exists
+ new File(dataHomeDirectory).mkdirs();
+ }
+
+ /** Getter for dataHomeDirectory.
+ * @return path to the application home directory
+ */
+ public String getDataHomeDirectory() {
+ return dataHomeDirectory;
+ }
+
+ /** Opens dataHomeDirectory if supported.
+ */
+ private void openDataHomeDirectory() {
+ try {
+ if (os.equals("Linux")) {
+ Runtime runtime = Runtime.getRuntime();
+ runtime.exec(new String[] { "xdg-open", dataHomeDirectory });
+ } else if (os.equals("Mac")||os.equals("Mac OS X")) {
+ Runtime runtime = Runtime.getRuntime();
+ runtime.exec(new String[] { "open", dataHomeDirectory });
+ }
+ } catch (IOException e) {
+ System.out.println("Error encountered while trying to open directory");
+ }
+ }
+
+ /** Asks the user to choose opening dataHomeDirectory or not.
+ */
+ public void askOpenDataHomeDirectory() {
+ if (os.equals("Linux") || os.equals("Mac") || os.equals("Mac OS X")) {
+ System.out.println("Do you want to open this directory? (y/N)");
+ Scanner scanner = new Scanner(System.in);
+ String resp = scanner.nextLine();
+ if (resp.equals("y") || resp.equals("Y")) {
+ System.out.println("Openning");
+ openDataHomeDirectory();
+ }
+ }
+ }
+
+
+}