package serverP2P; import java.util.Vector; import java.io.File; import java.io.IOException; import java.net.DatagramSocket; 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 protocolP2P.ProtocolP2PPacketUDP; import protocolP2P.ProtocolP2PPacket; 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 exception.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 UDP. * @author Louis Royer * @author Flavien Haas * @author JS Auge * @version 1.0 */ public class ServerManagementUDP implements Runnable { private String[] fileList; private Map sha512 = new HashMap<>(); private String baseDirectory; private int UDPPort; private DatagramSocket socket; private Logger logger; /** 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, Logger logger) { this.logger = logger; this.baseDirectory = baseDirectory; this.UDPPort = UDPPort; initFileList(); initSha512(); try { socket = new DatagramSocket(UDPPort); } catch (SocketException e) { logger.writeUDP("Error: cannot listen on port " + UDPPort, LogLevel.Error); System.exit(-1); } } /** Implementation of runnable. This methods allows to run the server. */ public void run() { logger.writeUDP("Server sucessfully started", LogLevel.Info); while(true) { try { ProtocolP2PPacketUDP pd = new ProtocolP2PPacketUDP((Object)socket); Payload p = pd.getPayload(); switch (p.getRequestResponseCode()) { case LOAD_REQUEST: logger.writeUDP("Received LOAD_REQUEST", LogLevel.Action); 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.writeUDP("Sending last partialContent", LogLevel.Debug); sizeToSend = fullLoad.length - offset; } else { sizeToSend = maxSizePartialContent; } logger.writeUDP("maxSizePartialContent: " + maxSizePartialContent, LogLevel.Debug); logger.writeUDP("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 ProtocolP2PPacketUDP(new Payload(RequestResponseCode.EMPTY_FILE))); } else { pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP((Payload)(new FilePart(filename, fullLoad.length, offset, load)))); } } catch (Exception e2) { logger.writeUDP(e2, LogLevel.Error); } } else { logger.writeUDP("File requested not found: `" + filename + "` " + Arrays.binarySearch(fileList, filename), LogLevel.Debug); logger.writeUDP("File list:", LogLevel.Debug); for (String f: fileList) { logger.writeUDP("- " + f, LogLevel.Debug); } throw new IOException(); // to send a NOT_FOUND in the catch block } } catch (IOException e) { try { pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.NOT_FOUND))); } catch (Exception e2) { logger.writeUDP(e2, LogLevel.Error); } } } break; case LIST_REQUEST: logger.writeUDP("Received LIST_REQUEST", LogLevel.Action); try { if (fileList.length == 0) { logger.writeUDP("Sending EMPTY_DIRECTORY", LogLevel.Action); pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.EMPTY_DIRECTORY))); } else { logger.writeUDP("Sending LIST_RESPONSE", LogLevel.Action); pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP((Payload)(new FileList(fileList)))); } } catch (Exception e2) { logger.writeUDP(e2, LogLevel.Error); } break; case HASH_REQUEST: logger.writeUDP("Received HASH_REQUEST", LogLevel.Action); sendHashResponse(pd); break; default: sendInternalError(pd); } } catch (IOException e) { } catch (SocketClosed e) { } catch (TransmissionError e) { } catch (ProtocolError e) { } catch (VersionError e) { } catch (InternalError e) { } catch (SizeError 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); 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.writeUDP("sha512 not supported", LogLevel.Error); } catch (IOException e) { logger.writeUDP("cannot read " + f, LogLevel.Warning); } } } /** Send an internal error message. * @param pd ProtocolP2PPacketUDP to respond */ private void sendInternalError(ProtocolP2PPacketUDP pd) { logger.writeUDP("Internal Error", LogLevel.Warning); try { pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.INTERNAL_ERROR))); } catch (Exception e) { logger.writeUDP(e, LogLevel.Error); } } /** Send hash response to hash request * @param pd Request received */ private void sendHashResponse(ProtocolP2PPacketUDP 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 ProtocolP2PPacketUDP((Payload)(new HashResponse(filename, hashes)))); } catch (Exception e) { logger.writeUDP(e, LogLevel.Error); } } else { // file not found try { pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.NOT_FOUND))); } catch (Exception e) { logger.writeUDP(e, LogLevel.Error); } } } } }