package clientP2P; import java.util.Arrays; import java.util.List; import java.util.ArrayList; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.net.UnknownHostException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import localException.ProtocolError; import localException.InternalError; import localException.ProtocolError; import localException.SizeError; import localException.TransmissionError; import localException.VersionError; import localException.SocketClosed; import protocolP2P.RequestResponseCode; import protocolP2P.FileList; import protocolP2P.ProtocolP2PPacket; import protocolP2P.DiscoverRequest; import protocolP2P.DiscoverResponse; import protocolP2P.Payload; import protocolP2P.HashAlgorithm; 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 exception.RemoteException; import exception.LocalException; import tools.ServeErrors; import tools.HostItem; import tools.Logger; import tools.LogLevel; import java.net.SocketException; /** Implementation of P2P-JAVA-PROJECT CLIENT * @author Louis Royer * @author Flavien Haas * @author JS Auge * @version 1.0 */ public abstract class ClientManagement extends ServeErrors { protected String baseDirectory; protected String partsSubdir; protected List hostList = new ArrayList<>(); protected HostItem tracker; protected HostItem client; protected Logger logger; 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 client HostItem of the application */ public ClientManagement(String baseDirectory, HostItem tracker, String partsSubdir, Logger logger, HostItem client) { this.baseDirectory = baseDirectory; this.tracker = tracker; this.partsSubdir = partsSubdir; this.logger = logger; this.client = client; } /** Getter for tracker socket * @return Tracker's socket * @throws SocketException * @throws UnknownHostException * @throws IOException */ protected abstract Object getTrackerSocket() throws SocketException, UnknownHostException, IOException; /** Close Tracker socket */ protected abstract void closeTrackerSocket(); /** Initialize hostList from tracker * @throws ProtocolError * @throws InternalError * @throws SocketException * @throws UnknownHostException * @throws IOException */ public void initHostList() throws ProtocolError, InternalError, SocketException, UnknownHostException, IOException { 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(); } closeTrackerSocket(); } catch (SocketClosed e){ writeLog("listDirectory : SocketClosed", LogLevel.Error); throw new ProtocolError(); } catch (LocalException e) { writeLog(e, LogLevel.Error); throw new ProtocolError(); } catch (RemoteException e) { writeLog(e, LogLevel.Error); 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) { 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); /** Close HostItem socket * @param hostItem HostItem */ protected abstract void closeHostItemSocket(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 */ public String[] listDirectory() throws EmptyDirectory, InternalError, UnknownHostException, IOException, TransmissionError, ProtocolError, VersionError, SizeError, InternalRemoteError, ProtocolRemoteError, VersionRemoteError { if (hostList.size() == 0) { return new String[0]; } ProtocolP2PPacket d = createProtocolP2PPacket(new Payload(RequestResponseCode.LIST_REQUEST)); try { d.sendRequest(getHostItemSocket(hostList.get(0))); Payload p = d.receiveResponse().getPayload(); closeHostItemSocket(hostList.get(0)); 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 */ public 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 writeLog * @param text Text to log * @param logLevel level of logging */ protected abstract void writeLog(String text, LogLevel logLevel); /** Implementation of writeLog * @param e exception to log * @param logLevel level of logging */ protected abstract void writeLog(Exception e, LogLevel logLevel); }