package clientP2P; import localException.InternalError; import localException.ProtocolError; import localException.SizeError; import localException.TransmissionError; import localException.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.DatagramSocket; import java.io.IOException; import java.nio.file.Files; import java.io.File; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.Arrays; import java.util.List; import tools.HostItem; import protocolP2P.ProtocolP2PPacketUDP; import protocolP2P.Payload; import protocolP2P.RequestResponseCode; import protocolP2P.FileList; import protocolP2P.FilePart; import protocolP2P.LoadRequest; import protocolP2P.HashAlgorithm; import protocolP2P.HashRequest; import protocolP2P.HashResponse; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import clientP2P.ClientDownloadUDP; /** 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 String partsSubdir; private List hostList; /** 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, List hostList, String partsSubdir) { this.baseDirectory = baseDirectory; this.hostList = hostList; this.partsSubdir = partsSubdir; } /** Implementation of Runnable */ public void run() { try { System.out.println("Enter all servers: type \"stop\" when finished"); Scanner scanner = new Scanner(System.in); 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:"); 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 { ClientDownloadUDP downLoader = new ClientDownloadUDP(filename, hostList, partsSubdir, baseDirectory); Thread t = new Thread(downLoader); t.start(); try { t.join(); if (downLoader.getSuccess()) { byte[] hash512 = downLoader.getHashSum512(); if (!Arrays.equals(hash512, computeHashsum(filename, HashAlgorithm.SHA512))) { System.err.println("Error: Hashsum does not match"); System.err.println("Computed checksum:"); byte[] c = computeHashsum(filename, HashAlgorithm.SHA512); for (byte b: c) { System.err.print(String.format("%02X", b)); } System.err.println(""); System.err.println("Received checksum:"); for (byte b: hash512) { System.err.print(String.format("%02X", b)); } System.err.println(""); throw new InternalError(); } } else { throw new InternalError(); } } catch (InterruptedException e) { throw new InternalError(); } } /** 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 { ProtocolP2PPacketUDP d = new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.LIST_REQUEST)); d.sendRequest((Object)hostList.get(0).getUDPSocket()); try { Payload p = d.receiveResponse().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(); } } /** Compute Hashsum of a file. * @param filename * @return hashsum */ private byte[] computeHashsum(String filename, HashAlgorithm h) { try { MessageDigest md = MessageDigest.getInstance(HashAlgorithm.SHA512.getName()); return md.digest(Files.readAllBytes(Paths.get(baseDirectory + filename))); } catch (NoSuchAlgorithmException e) { System.out.println("Error: " + h.getName() + " not supported"); } catch (IOException e) { System.out.println("Error: cannot read " + filename); } return new byte[0]; } }