package clientP2P; import tools.HostItem; import tools.Logger; import tools.LogLevel; import java.util.Scanner; import java.util.List; import localException.ProtocolError; import tools.ServeErrors; import protocolP2P.RequestResponseCode; import protocolP2P.FileList; import protocolP2P.ProtocolP2PPacket; import protocolP2P.DiscoverRequest; import protocolP2P.DiscoverResponse; import protocolP2P.Payload; import protocolP2P.HashAlgorithm; import localException.InternalError; import localException.ProtocolError; import localException.SizeError; import localException.TransmissionError; import localException.VersionError; import localException.SocketClosed; import remoteException.EmptyFile; import remoteException.EmptyDirectory; import remoteException.InternalRemoteError; import remoteException.NotFound; import remoteException.ProtocolRemoteError; import remoteException.VersionRemoteError; import remoteException.NotATracker; import remoteException.UnknownHost; import java.io.IOException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.net.UnknownHostException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Arrays; /** Implementation of P2P-JAVA-PROJECT CLIENT * @author Louis Royer * @author Flavien Haas * @author JS Auge * @version 1.0 */ public abstract class ClientManagement extends ServeErrors implements Runnable { protected String baseDirectory; protected String partsSubdir; protected List hostList; protected HostItem tracker; protected HostItem client; protected Logger logger; protected Scanner scanner; protected ClientDownload downLoader; /** Constructor with baseDirectory, tracker, partsSubdir, logger, and scanner parameters. * @param baseDirectory the root directory where files are stored * @param tracker Tracker hostItem * @param partsSubdir subdirectory to store file parts * @param logger Loggger * @param scanner Scanner used to read input * @param client HostItem of the application */ public ClientManagement(String baseDirectory, HostItem tracker, String partsSubdir, Logger logger, Scanner scanner, HostItem client) { this.scanner = scanner; this.baseDirectory = baseDirectory; this.tracker = tracker; this.partsSubdir = partsSubdir; this.logger = logger; this.client = client; try { initHostList(); } catch (InternalError e) { System.exit(-1); } catch (ProtocolError e) { System.exit(-2); } } /** Getter for tracker socket */ protected abstract Object getTrackerSocket(); /** Initialize hostList from tracker * @throws ProtocolError * @throws InternalError */ private void initHostList() throws ProtocolError, InternalError { ProtocolP2PPacket d = createProtocolP2PPacket(new DiscoverRequest(null)); try { d.sendRequest(getTrackerSocket()); Payload p = d.receiveResponse().getPayload(); assert p instanceof DiscoverResponse : "This payload must be instance of Filelist"; if (!(p instanceof DiscoverResponse)) { throw new InternalError(); } else { hostList = ((DiscoverResponse)p).getHostList(); } } catch (SocketClosed e){ writeLog("listDirectory : SocketClosed", LogLevel.Error); throw new ProtocolError(); } catch (NotATracker e) { writeLog(e, LogLevel.Error); throw new ProtocolError(); } catch (Exception e) { writeLog(e, LogLevel.Error); throw new ProtocolError(); } } /** Compute Hashsum of a file. * @param filename * @return hashsum */ protected 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) { writeLog(h.getName() + " not supported", LogLevel.Error); } catch (IOException e) { writeLog("cannot read " + filename, LogLevel.Error); } return new byte[0]; } /** Getter for HostItem socket * @param hostItem HostItem */ protected abstract Object getHostItemSocket(HostItem hostItem); /** 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 */ protected String[] listDirectory() throws EmptyDirectory, InternalError, UnknownHostException, IOException, TransmissionError, ProtocolError, VersionError, SizeError, InternalRemoteError, ProtocolRemoteError, VersionRemoteError { ProtocolP2PPacket d = createProtocolP2PPacket(new Payload(RequestResponseCode.LIST_REQUEST)); try { d.sendRequest(getHostItemSocket(hostList.get(0))); 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) { writeLog(e, LogLevel.Error); throw new ProtocolError(); } catch (EmptyFile e) { writeLog(e, LogLevel.Error); throw new ProtocolError(); } catch (SocketClosed e){ writeLog("listDirectory : SocketClosed", LogLevel.Error); throw new ProtocolError(); } catch (NotATracker e) { writeLog(e, LogLevel.Error); throw new ProtocolError(); } catch (UnknownHost e) { writeLog(e, LogLevel.Error); throw new ProtocolError(); } } /** Initialize downloader * @param filename Name of the file to download */ protected abstract void initDownloader(String filename); /** 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 { initDownloader(filename); 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))) { writeLog("Hashsum does not match", LogLevel.Error); String line = "Computed checksum:\n"; byte[] c = computeHashsum(filename, HashAlgorithm.SHA512); for (byte b: c) { line += String.format("%02X", b); } line += "\nReceived checksum:\n"; for (byte b: hash512) { line += String.format("%02X", b); } line += "\n"; writeLog(line, LogLevel.Info); throw new InternalError(); } else { downLoader.sendRatioUpdate(); writeLog("Ratio updates sent.", LogLevel.Info); } } else { throw new InternalError(); } } catch (InterruptedException e) { throw new InternalError(); } } /** 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:"); String f = scanner.nextLine(); download(f); System.out.println("File " + f + " sucessfully downloaded"); writeLog("File " + f + " sucessfully downloaded", LogLevel.Info); } catch (EmptyDirectory e) { writeLog("Server has no file in directory", LogLevel.Error); } catch (InternalError e) { writeLog("Client internal error", LogLevel.Error); } catch (UnknownHostException e) { writeLog("Server host is unknown", LogLevel.Error); } catch (IOException e) { writeLog("Request cannot be send or response cannot be received", LogLevel.Error); } catch (TransmissionError e) { writeLog("Message received is too big", LogLevel.Error); } catch (ProtocolError e) { writeLog("Cannot decode server’s response", LogLevel.Error); } catch (VersionError e) { writeLog("Server’s response use bad version of the protocol", LogLevel.Error); } catch (SizeError e) { writeLog("Cannot handle this packets because of internal representation limitations of numbers on the client", LogLevel.Error); } catch (InternalRemoteError e) { writeLog("Server internal error", LogLevel.Error); } catch (ProtocolRemoteError e) { writeLog("Server cannot decode client’s request", LogLevel.Error); } catch (VersionRemoteError e) { writeLog("Server cannot decode this version of the protocol", LogLevel.Error); } catch (NotFound e) { writeLog("Server has not this file in directory", LogLevel.Error); } catch (EmptyFile e) { writeLog("File is empty", LogLevel.Error); } } }