ExtractionController(
     ExtractionExtension extractionExtension, Archive archiv, IExtraction extractor) {
   this.archive = archiv;
   logger = LogController.CL(false);
   logger.setAllowTimeoutFlush(false);
   logger.info("Extraction of" + archive);
   this.extractor = extractor;
   extractionProgress = new ExtractionProgress(this, 0, 0, null);
   extractor.setArchiv(archiv);
   extractor.setExtractionController(this);
   extension = extractionExtension;
   extractor.setLogger(logger);
   passwordList = new CopyOnWriteArrayList<String>();
   archive.onControllerAssigned(this);
 }
  private boolean checkPassword(String pw, boolean optimized) throws ExtractionException {
    logger.info("Check Password: " + pw);
    if (StringUtils.isEmpty(pw)) {
      return false;
    }

    fireEvent(ExtractionEvent.Type.PASSWORT_CRACKING);
    return extractor.findPassword(this, pw, optimized);
  }
 /**
  * Returns the current password finding process.
  *
  * @return
  */
 public int getCrackProgress() {
   return extractor.getCrackProgress();
 }
  @Override
  public Void run() {
    // let's write an info file. and delete if after extraction. this wy we have infosfiles if the
    // extraction crashes jd
    crashLog =
        new ExtractLogFileWriter(
            archive.getName(),
            archive.getFirstArchiveFile().getFilePath(),
            archive.getFactory().getID()) {
          @Override
          public void write(String string) {
            super.write(string);
            logger.info(string);
          }
        };
    try {
      fireEvent(ExtractionEvent.Type.START);
      archive.onStartExtracting();
      crashLog.write("Date: " + new Date());
      crashLog.write("Start Extracting");
      crashLog.write("Extension Setup: \r\n" + extension.getSettings().toString());

      crashLog.write("Archive Setup: \r\n" + JSonStorage.toString(archive.getSettings()));
      extractor.setCrashLog(crashLog);
      logger.info("Start unpacking of " + archive.getFirstArchiveFile().getFilePath());

      for (ArchiveFile l : archive.getArchiveFiles()) {
        if (!new File(l.getFilePath()).exists()) {
          crashLog.write("File missing: " + l.getFilePath());
          logger.info("Could not find archive file " + l.getFilePath());
          archive.addCrcError(l);
        }
      }
      if (archive.getCrcError().size() > 0) {
        fireEvent(ExtractionEvent.Type.FILE_NOT_FOUND);
        crashLog.write("Failed");
        return null;
      }

      if (gotKilled()) {
        return null;
      }
      crashLog.write("Prepare");
      if (extractor.prepare()) {
        extractToFolder = extension.getFinalExtractToFolder(archive);
        crashLog.write("Extract To: " + extractToFolder);
        if (archive.isProtected()) {
          crashLog.write("Archive is Protected");
          if (!StringUtils.isEmpty(archive.getFinalPassword())
              && !checkPassword(archive.getFinalPassword(), false)) {
            /* open archive with found pw */
            logger.info(
                "Password " + archive.getFinalPassword() + " is invalid, try to find correct one");
            archive.setFinalPassword(null);
          }
          if (StringUtils.isEmpty(archive.getFinalPassword())) {
            crashLog.write("Try to find password");
            /* pw unknown yet */
            List<String> spwList = archive.getSettings().getPasswords();
            if (spwList != null) {
              passwordList.addAll(spwList);
            }
            passwordList.addAll(archive.getFactory().getGuessedPasswordList(archive));
            passwordList.add(archive.getName());
            java.util.List<String> pwList = extractor.config.getPasswordList();
            if (pwList == null) {
              pwList = new ArrayList<String>();
            }
            passwordList.addAll(pwList);
            fireEvent(ExtractionEvent.Type.START_CRACK_PASSWORD);
            logger.info("Start password finding for " + archive);
            String correctPW = null;
            for (String password : passwordList) {
              if (password == null) {
                continue;
              }
              if (gotKilled()) {
                return null;
              }
              crashLog.write("Try Password: "******"Found password: \"" + password + "\"");
                break;
              } else {
                // try trimmed password
                String trimmed = password.trim();
                if (trimmed.length() != password.length()) {
                  password = trimmed;
                  if (checkPassword(
                      password, extension.getSettings().isPasswordFindOptimizationEnabled())) {
                    correctPW = password;
                    crashLog.write("Found password: \"" + password + "\"");
                    break;
                  }
                }
              }
            }

            if (correctPW == null) {
              fireEvent(ExtractionEvent.Type.PASSWORD_NEEDED_TO_CONTINUE);
              crashLog.write("Ask for password");
              logger.info("Found no password in passwordlist " + archive);
              if (gotKilled()) {
                return null;
              }
              if (!checkPassword(archive.getFinalPassword(), false)) {
                fireEvent(ExtractionEvent.Type.EXTRACTION_FAILED);
                logger.info("No password found for " + archive);
                crashLog.write("No password found or given");
                crashLog.write("Failed");
                return null;
              }
            }
            fireEvent(ExtractionEvent.Type.PASSWORD_FOUND);
            logger.info("Found password for " + archive + "->" + archive.getFinalPassword());
          }
          if (StringUtils.isNotEmpty(archive.getFinalPassword())) {
            extension.addPassword(archive.getFinalPassword());
          }
        }
        final DiskSpaceReservation extractReservation =
            new DiskSpaceReservation() {

              @Override
              public long getSize() {
                final long completeSize =
                    Math.max(getCompleteBytes(), archive.getContentView().getTotalSize());
                long ret = completeSize - getProcessedBytes();
                return ret;
              }

              @Override
              public File getDestination() {
                return getExtractToFolder();
              }
            };
        DISKSPACERESERVATIONRESULT reservationResult =
            DownloadWatchDog.getInstance()
                .getSession()
                .getDiskSpaceManager()
                .checkAndReserve(extractReservation, this);
        try {
          switch (reservationResult) {
            case FAILED:
              logger.info(
                  "Not enough harddisk space for unpacking archive "
                      + archive.getFirstArchiveFile().getFilePath());
              crashLog.write("Diskspace Problem: " + reservationResult);
              crashLog.write("Failed");
              fireEvent(ExtractionEvent.Type.NOT_ENOUGH_SPACE);
              return null;
            case INVALIDDESTINATION:
              logger.warning("Could use create subpath");
              crashLog.write("Could use create subpath: " + getExtractToFolder());
              crashLog.write("Failed");
              fireEvent(ExtractionEvent.Type.EXTRACTION_FAILED);
              return null;
          }
          fireEvent(ExtractionEvent.Type.OPEN_ARCHIVE_SUCCESS);
          if (!getExtractToFolder().exists()) {
            if (!FileCreationManager.getInstance().mkdir(getExtractToFolder())) {
              logger.warning("Could not create subpath");
              crashLog.write("Could not create subpath: " + getExtractToFolder());
              crashLog.write("Failed");
              fireEvent(ExtractionEvent.Type.EXTRACTION_FAILED);
              return null;
            }
          }
          logger.info("Execute unpacking of:" + archive);
          logger.info("Extract to " + getExtractToFolder());
          crashLog.write(
              "Use Password: "******"|PW Protected:"
                  + archive.isProtected()
                  + ":"
                  + archive.isPasswordRequiredToOpen());
          ScheduledExecutorService scheduler = null;
          try {
            crashLog.write("Start Extracting " + extractor);
            scheduler = DelayedRunnable.getNewScheduledExecutorService();
            timer =
                scheduler.scheduleWithFixedDelay(
                    new Runnable() {
                      public void run() {
                        fireEvent(ExtractionEvent.Type.EXTRACTING);
                      }
                    },
                    1,
                    1,
                    TimeUnit.SECONDS);
            extractor.extract(this);
          } finally {
            crashLog.write("Extractor Returned");
            if (timer != null) {
              timer.cancel(false);
            }
            if (scheduler != null) {
              scheduler.shutdown();
            }
            extractor.close();
            if (extractor.getLastAccessedArchiveFile() != null) {
              crashLog.write("Last used File: " + extractor.getLastAccessedArchiveFile());
            }
            fireEvent(ExtractionEvent.Type.EXTRACTING);
          }
        } finally {
          DownloadWatchDog.getInstance()
              .getSession()
              .getDiskSpaceManager()
              .free(extractReservation, this);
        }
        if (gotKilled()) {
          return null;
        }
        if (extractor.getException() != null) {
          exception = extractor.getException();
          logger.log(exception);
        }
        if (exception != null) {
          crashLog.write("Exception occured: \r\n" + Exceptions.getStackTrace(exception));
        }

        crashLog.write("ExitCode: " + archive.getExitCode());
        switch (archive.getExitCode()) {
          case ExtractionControllerConstants.EXIT_CODE_SUCCESS:
            logger.info("Unpacking successful for " + archive);
            archive
                .getSettings()
                .setExtractionInfo(new ExtractionInfo(getExtractToFolder(), archive));
            crashLog.write(
                "Info: \r\n"
                    + JSonStorage.serializeToJson(
                        new ExtractionInfo(getExtractToFolder(), archive)));
            crashLog.write("Successful");
            successful = true;
            fireEvent(ExtractionEvent.Type.FINISHED);
            logger.clear();
            break;
          case ExtractionControllerConstants.EXIT_CODE_INCOMPLETE_ERROR:
            logger.warning("Archive seems to be incomplete " + archive);
            crashLog.write("Incomplete Archive");
            crashLog.write("Failed");
            fireEvent(ExtractionEvent.Type.FILE_NOT_FOUND);
            break;
          case ExtractionControllerConstants.EXIT_CODE_CRC_ERROR:
            logger.warning("A CRC error occurred when unpacking " + archive);
            crashLog.write("CRC Error occured");
            crashLog.write("Failed");
            fireEvent(ExtractionEvent.Type.EXTRACTION_FAILED_CRC);
            break;
          case ExtractionControllerConstants.EXIT_CODE_USER_BREAK:
            logger.info("User interrupted unpacking of " + archive);
            crashLog.write("Interrupted by User");
            crashLog.write("Failed");
            fireEvent(ExtractionEvent.Type.EXTRACTION_FAILED);
            break;
          case ExtractionControllerConstants.EXIT_CODE_CREATE_ERROR:
            logger.warning("Could not create Outputfile for" + archive);
            crashLog.write("Could not create Outputfile");
            crashLog.write("Failed");
            fireEvent(ExtractionEvent.Type.EXTRACTION_FAILED);
            break;
          case ExtractionControllerConstants.EXIT_CODE_WRITE_ERROR:
            logger.warning("Unable to write unpacked data on harddisk for " + archive);
            this.exception = new ExtractionException("Write to disk error");
            crashLog.write("Harddisk write Error");
            crashLog.write("Failed");
            fireEvent(ExtractionEvent.Type.EXTRACTION_FAILED);
            break;
          case ExtractionControllerConstants.EXIT_CODE_FATAL_ERROR:
            logger.warning("A unknown fatal error occurred while unpacking " + archive);
            crashLog.write("Unknown Fatal Error");
            crashLog.write("Failed");
            fireEvent(ExtractionEvent.Type.EXTRACTION_FAILED);
            break;
          case ExtractionControllerConstants.EXIT_CODE_WARNING:
            logger.warning("Non fatal error(s) occurred while unpacking " + archive);
            crashLog.write("Unknown Non Fatal Error");
            crashLog.write("Failed");
            fireEvent(ExtractionEvent.Type.EXTRACTION_FAILED);
            break;
          default:
            crashLog.write("Failed...unknown reason");
            crashLog.write("Failed");
            fireEvent(ExtractionEvent.Type.EXTRACTION_FAILED);
            break;
        }
        return null;
      } else {
        crashLog.write("Failed");
        fireEvent(ExtractionEvent.Type.EXTRACTION_FAILED);
      }
    } catch (Exception e) {
      logger.log(e);
      this.exception = e;
      crashLog.write("Exception occured: \r\n" + Exceptions.getStackTrace(e));
      crashLog.write("Failed");
      fireEvent(ExtractionEvent.Type.EXTRACTION_FAILED);
    } finally {
      crashLog.close();
      if (!CFG_EXTRACTION.CFG.isWriteExtractionLogEnabled()) {
        crashLog.delete();
      }
      try {
        if (gotKilled()) {
          logger.info("ExtractionController has been killed");
          logger.clear();
        }
        try {
          extractor.close();
        } catch (final Throwable e) {
        }
        fireEvent(ExtractionEvent.Type.CLEANUP);
        archive.onCleanUp();
      } finally {
        logger.close();
      }
    }
    return null;
  }