Skip to content
Snippets Groups Projects
Select Git revision
  • c88cd315993c83d175bacb3a16de9b8823dea5f8
  • main default protected
  • version_pour_projet_2
  • dev
  • LIVRABLE_3
  • LIVRABLE_2
  • LIVRABLE_1
7 results

FTPResource.java

Blame
  • FTPResource.java 19.92 KiB
    package fil.sr2.flopbox;
    
    import jakarta.ws.rs.*;
    import jakarta.ws.rs.core.*;
    
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.URI;
    import java.util.List;
    import java.util.zip.ZipEntry;
    import java.util.zip.ZipInputStream;
    import java.util.zip.ZipOutputStream;
    
    import org.apache.commons.net.ftp.FTP;
    import org.apache.commons.net.ftp.FTPClient;
    import org.apache.commons.net.ftp.FTPFile;
    
    @Path("/ftps")
    public class FTPResource {
    
        // ======== FTP Servers ========
        // GET /ftps - liste des serveurs FTP enregistrés
        @GET
        @Produces(MediaType.APPLICATION_JSON)
        public Response listFTPServers() {
            System.out.println("listFTPServers()");
            List<FTPServerConfig> servers = FTPServerRepository.getInstance().getAllServers();
            return Response.ok(servers).build();
        }
    
        // POST /ftps - enregistre un nouveau serveur FTP
        @POST
        @Consumes(MediaType.APPLICATION_JSON)
        public Response addFTPServer(FTPServerConfig config, @Context UriInfo uriInfo) {
            System.out.println("addFTPServer()");
            boolean created = FTPServerRepository.getInstance().addServer(config);
            if (!created) {
                return Response.status(Response.Status.CONFLICT).entity("Alias déjà utilisé").build();
            }
            UriBuilder builder = uriInfo.getAbsolutePathBuilder();
            builder.path(config.getAlias());
            return Response.created(builder.build()).build();
        }
    
        @DELETE
        @Path("/{alias}")
        public Response removeFTPServer(@PathParam("alias") String alias) {
            System.out.println("removeFTPServer()");
            boolean removed = FTPServerRepository.getInstance().removeServer(alias);
            return removed ? Response.noContent().build() : Response.status(Response.Status.NOT_FOUND).build();
        }
    
        @PUT
        @Path("/{alias}")
        @Consumes(MediaType.APPLICATION_JSON)
        public Response updateFTPServer(@PathParam("alias") String alias, FTPServerConfig newConfig) {
            System.out.println("updateFTPServer()");
            boolean updated = FTPServerRepository.getInstance().updateServer(alias, newConfig);
            return updated ? Response.ok(newConfig).build() : Response.status(Response.Status.NOT_FOUND).build();
        }
        // =============================
    
        // ========= Resources =========
        @GET
        @Path("/{alias}/{path: .+}")
        @Produces({ MediaType.APPLICATION_OCTET_STREAM, "application/zip" })
        public Response getFTPResource(
                @PathParam("alias") String alias,
                @PathParam("path") String path,
                @HeaderParam("X-FTP-User") String user,
                @HeaderParam("X-FTP-Pass") String pass) {
    
            FTPServerConfig config = FTPServerRepository.getInstance().getServer(alias);
            if (config == null) {
                return Response.status(Response.Status.NOT_FOUND)
                        .entity("Serveur FTP non trouvé").build();
            }
            FTPClient ftpClient = new FTPClient();
            try {
                ftpClient.connect(config.getHost(), config.getPort());
                if (!ftpClient.login(user, pass)) {
                    return Response.status(Response.Status.UNAUTHORIZED)
                            .entity("Authentification FTP échouée").build();
                }
                ftpClient.enterLocalPassiveMode();
                ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
    
                // Normaliser le chemin
                if (path.endsWith("/")) {
                    path = path.substring(0, path.length() - 1);
                }
    
                // Vérifier si c'est un dossier
                boolean isDirectory = ftpClient.changeWorkingDirectory(path);
                if (isDirectory) {
                    String currentDirectory = ftpClient.printWorkingDirectory();
                    System.out.println("Répertoire courant : " + currentDirectory);
    
                    // Créer un ZIP du dossier
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    ZipOutputStream zos = new ZipOutputStream(baos);
                    addDirectoryToZip(ftpClient, currentDirectory, "", zos);
                    zos.close();
                    return Response.ok(baos.toByteArray())
                            .type("application/zip")
                            .header("Content-Disposition", "attachment; filename=\"" + getFileName(path) + ".zip\"")
                            .build();
                } else {
                    // Télécharger le fichier
                    InputStream is = ftpClient.retrieveFileStream(path);
                    if (is != null) {
                        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                        byte[] buffer = new byte[4096];
                        int bytesRead;
                        while ((bytesRead = is.read(buffer)) != -1) {
                            outputStream.write(buffer, 0, bytesRead);
                        }
                        is.close();
                        ftpClient.completePendingCommand();
                        return Response.ok(outputStream.toByteArray())
                                .type(determineContentType(path))
                                .header("Content-Disposition", "attachment; filename=\"" + getFileName(path) + "\"")
                                .build();
                    } else {
                        return Response.status(Response.Status.NOT_FOUND)
                                .entity("Ressource non trouvée").build();
                    }
                }
            } catch (IOException e) {
                return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                        .entity("Erreur FTP: " + e.getMessage()).build();
            } finally {
                try {
                    if (ftpClient.isConnected()) {
                        ftpClient.logout();
                        ftpClient.disconnect();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        private String determineContentType(String path) {
            if (path.endsWith(".txt"))
                return MediaType.TEXT_PLAIN;
            if (path.endsWith(".html"))
                return MediaType.TEXT_HTML;
            if (path.endsWith(".json"))
                return MediaType.APPLICATION_JSON;
            return MediaType.APPLICATION_OCTET_STREAM;
        }
    
        private void addDirectoryToZip(FTPClient ftpClient, String remotePath, String basePath, ZipOutputStream zos)
                throws IOException {
            FTPFile[] files = ftpClient.listFiles(remotePath);
            if (files == null || files.length == 0) {
                System.out.println("Aucun fichier trouvé dans le dossier : " + remotePath);
                return;
            }
    
            for (FTPFile file : files) {
                String filePath = remotePath.isEmpty() ? file.getName() : remotePath + "/" + file.getName();
                String zipEntryPath = basePath.isEmpty() ? file.getName() : basePath + "/" + file.getName();
    
                if (file.isDirectory()) {
                    // Ajouter une entrée de dossier au ZIP
                    zos.putNextEntry(new ZipEntry(zipEntryPath + "/"));
                    zos.closeEntry();
                    // Explorer le sous-dossier récursivement
                    addDirectoryToZip(ftpClient, filePath, zipEntryPath, zos);
                } else {
                    // Ajouter un fichier au ZIP
                    ZipEntry entry = new ZipEntry(zipEntryPath);
                    zos.putNextEntry(entry);
                    InputStream is = ftpClient.retrieveFileStream(filePath);
                    if (is == null) {
                        System.out.println("Impossible de lire le fichier : " + filePath);
                        continue;
                    }
                    byte[] buffer = new byte[4096];
                    int bytesRead;
                    while ((bytesRead = is.read(buffer)) != -1) {
                        zos.write(buffer, 0, bytesRead);
                    }
                    is.close();
                    ftpClient.completePendingCommand(); // Important pour finaliser la lecture
                    zos.closeEntry();
                }
            }
        }
    
        @GET
        @Path("/list/{alias}/{path: .+}")
        @Produces(MediaType.APPLICATION_JSON)
        public Response listDirectory(
                @PathParam("alias") String alias,
                @PathParam("path") String path,
                @HeaderParam("X-FTP-User") String user,
                @HeaderParam("X-FTP-Pass") String pass) {
    
            FTPServerConfig config = FTPServerRepository.getInstance().getServer(alias);
            if (config == null) {
                return Response.status(Response.Status.NOT_FOUND)
                        .entity("Serveur FTP non trouvé").build();
            }
            FTPClient ftpClient = new FTPClient();
            try {
                ftpClient.connect(config.getHost(), config.getPort());
                if (!ftpClient.login(user, pass)) {
                    return Response.status(Response.Status.UNAUTHORIZED)
                            .entity("Authentification FTP échouée").build();
                }
                ftpClient.enterLocalPassiveMode();
                ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
    
                if (path.endsWith("/")) {
                    path = path.substring(0, path.length() - 1);
                }
    
                FTPFile[] files = ftpClient.listFiles(path);
                if (files == null || files.length == 0) {
                    return Response.status(Response.Status.NOT_FOUND)
                            .entity("Ressource non trouvée: " + path).build();
                }
    
                FtpNode root = buildFtpTree(ftpClient, path);
                return Response.ok(root).build();
            } catch (IOException e) {
                return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                        .entity("Erreur FTP: " + e.getMessage()).build();
            } finally {
                try {
                    if (ftpClient.isConnected()) {
                        ftpClient.logout();
                        ftpClient.disconnect();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        // Classe représentant un nœud JSON (fichier ou dossier)
        static class FtpNode {
            public String name;
            public boolean isDirectory;
            public List<FtpNode> children;
    
            public FtpNode(String name, boolean isDirectory) {
                this.name = name;
                this.isDirectory = isDirectory;
                if (isDirectory) {
                    this.children = new java.util.ArrayList<>();
                }
            }
        }
    
        // Fonction récursive pour construire l'arborescence FTP
        private FtpNode buildFtpTree(FTPClient ftpClient, String path) throws IOException {
            FTPFile[] files = ftpClient.listFiles(path);
            FtpNode node = new FtpNode(getFileName(path), true); // Root est un dossier
            if (files != null) {
                for (FTPFile file : files) {
                    String fullPath = path + "/" + file.getName();
                    if (file.isDirectory()) {
                        node.children.add(buildFtpTree(ftpClient, fullPath)); // Récursif pour les dossiers
                    } else {
                        node.children.add(new FtpNode(file.getName(), false)); // Ajouter fichier
                    }
                }
            }
            return node;
        }
    
        @PUT
        @Path("/{alias}/{path: .+}")
        @Consumes(MediaType.APPLICATION_OCTET_STREAM)
        public Response uploadFile(
                @PathParam("alias") String alias,
                @PathParam("path") String path,
                @HeaderParam("X-FTP-User") String user,
                @HeaderParam("X-FTP-Pass") String pass,
                @HeaderParam("X-Zip-Extract") Boolean extractZip,
                InputStream fileStream) {
    
            FTPServerConfig config = FTPServerRepository.getInstance().getServer(alias);
            if (config == null) {
                return Response.status(Response.Status.NOT_FOUND).entity("Serveur non trouvé").build();
            }
            FTPClient ftp = new FTPClient();
            try {
                ftp.connect(config.getHost(), config.getPort());
                ftp.login(user, pass);
                ftp.enterLocalPassiveMode();
                ftp.setFileType(FTP.BINARY_FILE_TYPE);
    
                if (extractZip != null && extractZip) {
                    // Créer le dossier cible et ses parents si nécessaire
                    createDirectories(ftp, path);
                    // Extraire le contenu du ZIP dans le dossier cible
                    ZipInputStream zis = new ZipInputStream(fileStream);
                    ZipEntry entry;
                    while ((entry = zis.getNextEntry()) != null) {
                        String fullPath = path + "/" + entry.getName();
                        if (entry.isDirectory()) {
                            createDirectories(ftp, fullPath);
                        } else {
                            String parentDir = fullPath.substring(0, fullPath.lastIndexOf('/'));
                            createDirectories(ftp, parentDir);
                            ftp.storeFile(fullPath, zis);
                        }
                        zis.closeEntry();
                    }
                    zis.close();
                    return Response.ok().build();
                } else {
                    // Créer les répertoires parents si nécessaire
                    String parentDir = path.substring(0, path.lastIndexOf('/'));
                    createDirectories(ftp, parentDir);
                    // Upload du fichier
                    boolean success = ftp.storeFile(path, fileStream);
                    return success ? Response.ok().build() : Response.status(500).entity("Erreur d'upload").build();
                }
            } catch (IOException e) {
                return Response.serverError().entity("Erreur FTP: " + e.getMessage()).build();
            } finally {
                try {
                    if (ftp.isConnected()) {
                        ftp.logout();
                        ftp.disconnect();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        @POST
        @Path("/{alias}/{path: .+}")
        @Consumes({ MediaType.APPLICATION_OCTET_STREAM, MediaType.TEXT_PLAIN })
        public Response createResource(
                @PathParam("alias") String alias,
                @PathParam("path") String path,
                @HeaderParam("X-FTP-User") String user,
                @HeaderParam("X-FTP-Pass") String pass,
                @HeaderParam("X-Resource-Type") String resourceType, // Nouveau header
                InputStream inputStream) {
    
            System.out.println("createResource()");
            FTPClient ftp = new FTPClient();
            try {
                FTPServerConfig config = FTPServerRepository.getInstance().getServer(alias);
                ftp.connect(config.getHost(), config.getPort());
                ftp.login(user, pass);
                // Déterminer le type de ressource via le header
                if ("directory".equalsIgnoreCase(resourceType)) {
                    boolean dirCreated = ftp.makeDirectory(path);
                    return dirCreated
                            ? Response.created(URI.create(path)).build()
                            : Response.status(400).entity("Erreur création répertoire").build();
                } else {
                    // Créer un fichier (même avec un flux vide)
                    boolean fileCreated = ftp.storeFile(path, inputStream);
                    return fileCreated
                            ? Response.created(URI.create(path)).build()
                            : Response.status(400).entity("Erreur création fichier").build();
                }
            } catch (IOException e) {
                return Response.serverError().entity("Erreur FTP: " + e.getMessage()).build();
            } finally {
                try {
                    if (ftp.isConnected()) {
                        ftp.logout();
                        ftp.disconnect();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        private void createDirectories(FTPClient ftp, String path) throws IOException {
            String[] dirs = path.split("/");
            StringBuilder sb = new StringBuilder();
            for (String dir : dirs) {
                if (dir.isEmpty())
                    continue;
                if (sb.length() == 0) {
                    sb.append(dir);
                } else {
                    sb.append("/").append(dir);
                }
                String currentDir = sb.toString();
                if (!ftp.changeWorkingDirectory(currentDir)) {
                    if (ftp.makeDirectory(currentDir)) {
                        ftp.changeWorkingDirectory(currentDir);
                    } else {
                        throw new IOException("Échec de création du dossier: " + currentDir);
                    }
                }
            }
        }
    
        // delete the file or the content of a folder recursively
        @DELETE
        @Path("/{alias}/{path: .+}")
        public Response deleteResource(
                @PathParam("alias") String alias,
                @PathParam("path") String path,
                @HeaderParam("X-FTP-User") String user,
                @HeaderParam("X-FTP-Pass") String pass) {
    
            System.out.println("deleteResource()");
            FTPClient ftp = new FTPClient();
            try {
                FTPServerConfig config = FTPServerRepository.getInstance().getServer(alias);
                ftp.connect(config.getHost(), config.getPort());
                ftp.login(user, pass);
                // Appel à la méthode récursive pour supprimer la ressource
                boolean deleted = deleteRecursive(ftp, path);
                return deleted
                        ? Response.noContent().build()
                        : Response.status(Response.Status.NOT_FOUND).entity("Ressource non trouvée").build();
            } catch (IOException e) {
                return Response.serverError().entity("Erreur FTP : " + e.getMessage()).build();
            } finally {
                try {
                    if (ftp.isConnected()) {
                        ftp.logout();
                        ftp.disconnect();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        private boolean deleteRecursive(FTPClient ftp, String path) throws IOException {
            // Tente de supprimer le chemin en tant que fichier
            if (ftp.deleteFile(path)) {
                return true;
            }
            // Si ce n'est pas un fichier, vérifie si c'est un répertoire
            FTPFile[] files = ftp.listFiles(path);
            if (files == null) {
                // Le chemin n'existe pas
                return false;
            }
            // Supprime récursivement le contenu du répertoire
            for (FTPFile file : files) {
                String fullPath = path + "/" + file.getName();
                if (file.isDirectory()) {
                    if (!deleteRecursive(ftp, fullPath)) {
                        return false;
                    }
                } else {
                    if (!ftp.deleteFile(fullPath)) {
                        return false;
                    }
                }
            }
            // Supprime le répertoire lui-même
            return ftp.removeDirectory(path);
        }
    
        @POST
        @Path("/{alias}/{path: .+}/rename")
        @Consumes(MediaType.TEXT_PLAIN)
        public Response renameResource(
                @PathParam("alias") String alias,
                @PathParam("path") String oldPath,
                @HeaderParam("X-FTP-User") String user,
                @HeaderParam("X-FTP-Pass") String pass,
                String newPath) {
    
            System.out.println("renameResource()");
            FTPClient ftp = new FTPClient();
            try {
                FTPServerConfig config = FTPServerRepository.getInstance().getServer(alias);
                ftp.connect(config.getHost(), config.getPort());
                ftp.login(user, pass);
                System.out.print(oldPath + " " + newPath);
                boolean success = ftp.rename(oldPath, newPath);
                return success
                        ? Response.ok().build()
                        : Response.status(Response.Status.BAD_REQUEST).entity("Échec du renommage").build();
            } catch (IOException e) {
                return Response.serverError().entity("Erreur FTP : " + e.getMessage()).build();
            } finally {
                try {
                    if (ftp.isConnected()) {
                        ftp.logout();
                        ftp.disconnect();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        private String getFileName(String path) {
            return path.substring(path.lastIndexOf('/') + 1);
        }
        // =============================
    }