// Path: api/src/main/java/xyz/docbleach/api/BleachSession.java
// public class BleachSession implements Serializable {
// 
//   private static final Logger LOGGER = LoggerFactory.getLogger(BleachSession.class);
//   private static final int MAX_ONGOING_TASKS = 10;
//   private final transient Bleach bleach;
//   private final Collection<Threat> threats = new ArrayList<>();
//   /**
//    * Counts ongoing tasks, to prevent fork bombs when handling zip archives for instance)
//    */
//   private int ongoingTasks = 0;
// 
//   public BleachSession(Bleach bleach) {
//     this.bleach = bleach;
//   }
// 
//   /**
//    * The BleachSession is able to record threats encountered by the bleach.
//    *
//    * @param threat The removed threat object, containing the threat type and more information
//    */
//   public void recordThreat(Threat threat) {
//     if (threat == null) {
//       return;
//     }
//     LOGGER.trace("Threat recorded: " + threat);
//     threats.add(threat);
//   }
// 
//   public Collection<Threat> getThreats() {
//     return threats;
//   }
// 
//   public int threatCount() {
//     return threats.size();
//   }
// 
//   /**
//    * @return The bleach used in this session
//    */
//   public Bleach getBleach() {
//     return bleach;
//   }
// 
//   public void sanitize(InputStream is, OutputStream os) throws BleachException {
//     try {
//       if (ongoingTasks++ >= MAX_ONGOING_TASKS) {
//         throw new RecursionBleachException(ongoingTasks);
//       }
//       if (!bleach.handlesMagic(is)) {
//         return;
//       }
//       bleach.sanitize(is, os, this);
//     } finally {
//       ongoingTasks--;
//     }
//   }
// }
// 
// Path: api/src/main/java/xyz/docbleach/api/bleach/DefaultBleach.java
// public class DefaultBleach extends CompositeBleach {
// 
//   public DefaultBleach() {
//     super(getDefaultBleaches());
//   }
// 
//   /**
//    * Finds all statically loadable bleaches
//    *
//    * @return ordered list of statically loadable bleaches
//    */
//   private static Bleach[] getDefaultBleaches() {
//     ServiceLoader<Bleach> services = ServiceLoader.load(Bleach.class);
// 
//     Collection<Bleach> list = new ArrayList<>();
//     services.forEach(list::add);
// 
//     return list.toArray(new Bleach[0]);
//   }
// }
// 
// Path: api/src/main/java/xyz/docbleach/api/exception/BleachException.java
// public class BleachException extends Exception {
// 
//   public BleachException(Throwable e) {
//     super(e);
//   }
// 
//   public BleachException(String message) {
//     super(message);
//   }
// }


import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
import io.vertx.core.Handler;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.ext.web.FileUpload;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xyz.docbleach.api.BleachSession;
import xyz.docbleach.api.bleach.DefaultBleach;
import xyz.docbleach.api.exception.BleachException;

  private void sendFile(RoutingContext routingContext, String fileName, File saneFile) {
    HttpServerResponse response = routingContext.response();
    response.putHeader("Content-Description", "File Transfer");
    response.putHeader("Content-Type", "application/octet-stream");
    response.putHeader(
        "Content-Disposition", "attachment; filename=" + fileName); // @TODO: don't trust this name?
    response.putHeader("Content-Transfer-Encoding", "binary");
    response.putHeader("Expires", "0");
    response.putHeader("Pragma", "Public");
    response.putHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
    response.putHeader("Content-Length", "" + saneFile.length());

    response.sendFile(saneFile.getAbsolutePath());
  }

  private void removeFiles(File... files) {
    vertx.executeBlocking(
        promise -> {
          for (File f : files) {
            if (!f.delete()) {
              LOGGER.warn("Could not delete file{} ", f.getAbsolutePath());
            }
          }
        },
        __ -> {
        });
  }

  private File sanitize(String uploadedFileName) throws IOException, BleachException {
// Path: api/src/main/java/xyz/docbleach/api/BleachSession.java
// public class BleachSession implements Serializable {
// 
//   private static final Logger LOGGER = LoggerFactory.getLogger(BleachSession.class);
//   private static final int MAX_ONGOING_TASKS = 10;
//   private final transient Bleach bleach;
//   private final Collection<Threat> threats = new ArrayList<>();
//   /**
//    * Counts ongoing tasks, to prevent fork bombs when handling zip archives for instance)
//    */
//   private int ongoingTasks = 0;
// 
//   public BleachSession(Bleach bleach) {
//     this.bleach = bleach;
//   }
// 
//   /**
//    * The BleachSession is able to record threats encountered by the bleach.
//    *
//    * @param threat The removed threat object, containing the threat type and more information
//    */
//   public void recordThreat(Threat threat) {
//     if (threat == null) {
//       return;
//     }
//     LOGGER.trace("Threat recorded: " + threat);
//     threats.add(threat);
//   }
// 
//   public Collection<Threat> getThreats() {
//     return threats;
//   }
// 
//   public int threatCount() {
//     return threats.size();
//   }
// 
//   /**
//    * @return The bleach used in this session
//    */
//   public Bleach getBleach() {
//     return bleach;
//   }
// 
//   public void sanitize(InputStream is, OutputStream os) throws BleachException {
//     try {
//       if (ongoingTasks++ >= MAX_ONGOING_TASKS) {
//         throw new RecursionBleachException(ongoingTasks);
//       }
//       if (!bleach.handlesMagic(is)) {
//         return;
//       }
//       bleach.sanitize(is, os, this);
//     } finally {
//       ongoingTasks--;
//     }
//   }
// }
// 
// Path: api/src/main/java/xyz/docbleach/api/bleach/DefaultBleach.java
// public class DefaultBleach extends CompositeBleach {
// 
//   public DefaultBleach() {
//     super(getDefaultBleaches());
//   }
// 
//   /**
//    * Finds all statically loadable bleaches
//    *
//    * @return ordered list of statically loadable bleaches
//    */
//   private static Bleach[] getDefaultBleaches() {
//     ServiceLoader<Bleach> services = ServiceLoader.load(Bleach.class);
// 
//     Collection<Bleach> list = new ArrayList<>();
//     services.forEach(list::add);
// 
//     return list.toArray(new Bleach[0]);
//   }
// }
// 
// Path: api/src/main/java/xyz/docbleach/api/exception/BleachException.java
// public class BleachException extends Exception {
// 
//   public BleachException(Throwable e) {
//     super(e);
//   }
// 
//   public BleachException(String message) {
//     super(message);
//   }
// }


// Path: http_server/src/main/java/xyz/docbleach/http_server/Main.java
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
import io.vertx.core.Handler;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.ext.web.FileUpload;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xyz.docbleach.api.BleachSession;
import xyz.docbleach.api.bleach.DefaultBleach;
import xyz.docbleach.api.exception.BleachException;


  private void sendFile(RoutingContext routingContext, String fileName, File saneFile) {
    HttpServerResponse response = routingContext.response();
    response.putHeader("Content-Description", "File Transfer");
    response.putHeader("Content-Type", "application/octet-stream");
    response.putHeader(
        "Content-Disposition", "attachment; filename=" + fileName); // @TODO: don't trust this name?
    response.putHeader("Content-Transfer-Encoding", "binary");
    response.putHeader("Expires", "0");
    response.putHeader("Pragma", "Public");
    response.putHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
    response.putHeader("Content-Length", "" + saneFile.length());

    response.sendFile(saneFile.getAbsolutePath());
  }

  private void removeFiles(File... files) {
    vertx.executeBlocking(
        promise -> {
          for (File f : files) {
            if (!f.delete()) {
              LOGGER.warn("Could not delete file{} ", f.getAbsolutePath());
            }
          }
        },
        __ -> {
        });
  }

  private File sanitize(String uploadedFileName) throws IOException, BleachException {
    BleachSession session = new BleachSession(new DefaultBleach());