package serverP2P; import java.util.Vector; import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.net.SocketException; import java.nio.file.Paths; import java.nio.file.Files; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.net.ServerSocket; import java.net.Socket; import protocolP2P.ProtocolP2PPacketTCP; import protocolP2P.ProtocolP2PPacket; import protocolP2P.RequestResponseCode; import protocolP2P.Payload; import protocolP2P.LoadRequest; import protocolP2P.FileList; import protocolP2P.FilePart; import localException.InternalError; import localException.ProtocolError; import localException.SizeError; import localException.TransmissionError; import localException.VersionError; import localException.SocketClosed; import remoteException.EmptyDirectory; import remoteException.InternalRemoteError; import remoteException.NotFound; import remoteException.ProtocolRemoteError; import remoteException.VersionRemoteError; import remoteException.EmptyFile; import java.util.Arrays; import tools.Logger; import tools.LogLevel; import java.util.HashMap; import java.util.Map; import protocolP2P.HashAlgorithm; import protocolP2P.HashRequest; import protocolP2P.HashResponse; /** Implementation of P2P-JAVA-PROJECT VERSION 1.0 protocol for TCP. * @author Louis Royer * @author Flavien Haas * @author JS Auge * @version 1.0 */ public class ServerManagementTCP implements Runnable { private String[] fileList; private Map sha512 = new HashMap<>(); private String baseDirectory; private int TCPPort; private ServerSocket socket; private Logger logger; /** Constructor for TCP implementation, with baseDirectory and TCPPort parameters. * @param baseDirectory the root directory where files are stored * @param TCPPort the server will listen on this port */ public ServerManagementTCP(String baseDirectory, int TCPPort, Logger logger) { this.logger = logger; this.baseDirectory = baseDirectory; this.TCPPort = TCPPort; initFileList(); initSha512(); try { socket = new ServerSocket(TCPPort); } catch (SocketException e) { logger.writeTCP("Error: cannot listen on port " + TCPPort, LogLevel.Error); System.exit(-1); } catch (IOException e) { logger.writeTCP("Error: cannot openning socket", LogLevel.Error); System.exit(-2); } } /** Implementation of runnable. This methods allows to run the server. */ public void run() { logger.writeTCP("Server sucessfully started", LogLevel.Info); do { try { Socket s = socket.accept(); ClientHandler c = new ClientHandler(s); (new Thread(c)).start(); } catch (IOException e) { logger.writeTCP("Error while accepting new connection", LogLevel.Warning); } } while(true); } /** Private runnable class allowing to serve one client. */ private class ClientHandler implements Runnable { private Socket s; private String addr; /** Constructor with a socket. * @param s Socket of this client */ public ClientHandler(Socket s) { this.s = s; this.addr = "[" +s.getInetAddress().getHostAddress() + "]:" + s.getPort() + " "; } /** Implementation of runnable. This method allow to serve one client. */ public void run() { boolean end = false; logger.writeTCP(addr + "New connection", LogLevel.Action); do { end = handleRequest(); } while(!end); logger.writeTCP(addr + "End of connection", LogLevel.Action); } /** Respond to next request incomming on socket s. * @param s Socket used to read request and send response * @return true if cannot expect another request (ie, socket is closed) */ private boolean handleRequest() { try { ProtocolP2PPacketTCP pd = new ProtocolP2PPacketTCP((Object)s); Payload p = pd.getPayload(); switch (p.getRequestResponseCode()) { case LOAD_REQUEST: logger.writeTCP(addr + "LOAD_REQUEST", LogLevel.Action); sendLoadResponse(pd); break; case LIST_REQUEST: logger.writeTCP(addr + "LIST_REQUEST", LogLevel.Action); sendListResponse(pd); break; case HASH_REQUEST: logger.writeTCP(addr + "HASH_REQUEST", LogLevel.Action); sendHashResponse(pd); break; default: logger.writeTCP(addr + "Received grabbage", LogLevel.Action); sendInternalError(pd); } } catch (IOException e) { return true; } catch (SocketClosed e) { return true; } catch (TransmissionError e) {} catch (ProtocolError e) {} catch (VersionError e) {} catch (InternalError e) {} catch (SizeError e) {} return false; } } /** 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); Arrays.sort(fileList); } /** Init sha512 map. */ private void initSha512() { for(String f: fileList) { try { MessageDigest md = MessageDigest.getInstance(HashAlgorithm.SHA512.getName()); sha512.put(f, md.digest(Files.readAllBytes(Paths.get(baseDirectory + f)))); md.reset(); } catch (NoSuchAlgorithmException e) { logger.writeTCP("sha512 not supported", LogLevel.Error); } catch (IOException e) { logger.writeTCP("cannot read " + f, LogLevel.Warning); } } } /** Send an internal error message. * @param pd ProtocolP2PPacketTCP to respond */ private void sendInternalError(ProtocolP2PPacketTCP pd) { logger.writeTCP("Internal Error", LogLevel.Warning); try { pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.INTERNAL_ERROR))); } catch (Exception e) { logger.writeTCP(e, LogLevel.Error); } } /** Send response to list request * @param pd Request received */ private void sendListResponse(ProtocolP2PPacketTCP pd) { try { if (fileList.length == 0) { logger.writeTCP("Sending EMPTY_DIRECTORY", LogLevel.Action); pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.EMPTY_DIRECTORY))); } else { logger.writeTCP("Sending LIST_RESPONSE", LogLevel.Action); pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP((Payload)(new FileList(fileList)))); } } catch (Exception e2) { logger.writeTCP(e2, LogLevel.Error); } } /** Send hash response to hash request * @param pd Request received */ private void sendHashResponse(ProtocolP2PPacketTCP pd) { Payload p = pd.getPayload(); assert p instanceof HashRequest : "payload must be an instance of HashRequest"; if (!(p instanceof HashRequest)) { sendInternalError(pd); } else { String filename = ((HashRequest)p).getFilename(); if (Arrays.binarySearch(fileList, filename) >= 0) { Map hashes = new HashMap<>(); for (HashAlgorithm h : ((HashRequest)p).getAlgoList()) { switch (h) { case SHA512: hashes.put(h, sha512.get(filename)); break; case MD5: default: hashes.put(h, new byte[0]); break; } } try { pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP((Payload)(new HashResponse(filename, hashes)))); } catch (Exception e) { logger.writeTCP(e, LogLevel.Error); } } else { // file not found try { pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.NOT_FOUND))); } catch (Exception e) { logger.writeTCP(e, LogLevel.Error); } } } } /** Send response to load request * @param pd Request received */ private void sendLoadResponse(ProtocolP2PPacketTCP pd) { Payload p = pd.getPayload(); assert p instanceof LoadRequest : "payload must be an instance of LoadRequest"; if (!(p instanceof LoadRequest)) { sendInternalError(pd); } else { String filename = ((LoadRequest)p).getFilename(); long offset = ((LoadRequest)p).getOffset(); long maxSizePartialContent = ((LoadRequest)p).getMaxSizePartialContent(); try { byte[] fullLoad = Files.readAllBytes(Paths.get(baseDirectory + filename)); long sizeToSend = 0; if (fullLoad.length - offset < maxSizePartialContent) { logger.writeTCP("Sending last partialContent", LogLevel.Debug); sizeToSend = fullLoad.length - offset; } else { sizeToSend = maxSizePartialContent; } logger.writeTCP("maxSizePartialContent: " + maxSizePartialContent, LogLevel.Debug); logger.writeTCP("Sending " + filename + " from " + offset + " to " + (offset + sizeToSend), LogLevel.Debug); byte[] load = Arrays.copyOfRange(fullLoad, (int)offset, (int)(offset + sizeToSend)); if (Arrays.binarySearch(fileList, filename) >= 0) { try { if (load.length == 0) { pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.EMPTY_FILE))); } else { pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP((Payload)(new FilePart(filename, fullLoad.length, offset, load)))); } } catch (Exception e2) { logger.writeTCP(e2, LogLevel.Error); } } else { logger.writeTCP("File requested not found: `" + filename + "` " + Arrays.binarySearch(fileList, filename), LogLevel.Debug); logger.writeTCP("File list:", LogLevel.Debug); for (String f: fileList) { logger.writeTCP("- " + f, LogLevel.Debug); } throw new IOException(); // to send a NOT_FOUND in the catch block } } catch (IOException e) { try { pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.NOT_FOUND))); } catch (Exception e2) { logger.writeTCP(e2, LogLevel.Debug); } } } } }