public SimpleFTPDownloadInterface(SimpleFTP simpleFTP, final DownloadLink link, String filePath) {
    connectionHandler = new ManagedThrottledConnectionHandler();
    final String host =
        SocketConnection.getHostName(simpleFTP.getSocket().getRemoteSocketAddress());
    downloadable =
        new DownloadLinkDownloadable(link) {
          @Override
          public boolean isResumable() {
            return link.getBooleanProperty("RESUME", true);
          }

          @Override
          public void setResumeable(boolean value) {
            link.setProperty("RESUME", value);
            super.setResumeable(value);
          }

          @Override
          public String getHost() {
            return host;
          }
        };
    if (!link.hasProperty(DownloadLink.PROPERTY_RESUMEABLE)) {
      downloadable.setResumeable(true);
    }
    this.filePath = filePath;
    logger = downloadable.getLogger();
    downloadable.setDownloadInterface(this);
    this.simpleFTP = simpleFTP;
  }
 protected void download(String filename, boolean resume)
     throws IOException, PluginException, SkipReasonException {
   final File file = outputPartFile;
   if (!simpleFTP.isBinary()) {
     logger.info("Warning: Download in ASCII mode may fail!");
   }
   final InetSocketAddress pasv = simpleFTP.pasv();
   resumed = false;
   if (resume) {
     final long resumePosition = file.length();
     if (resumePosition > 0) {
       resumed = true;
       totalLinkBytesLoadedLive.set(resumePosition);
       simpleFTP.sendLine("REST " + resumePosition);
       try {
         simpleFTP.readLines(new int[] {350}, "Resume not supported");
         downloadable.setResumeable(true);
       } catch (final IOException e) {
         cleanupDownladInterface();
         if (e.getMessage().contains("Resume not")) {
           file.delete();
           downloadable.setResumeable(false);
           throw new PluginException(LinkStatus.ERROR_RETRY);
         }
         throw e;
       }
     }
   }
   final RandomAccessFile raf;
   try {
     raf = IO.open(file, "rw");
   } catch (final IOException e) {
     throw new SkipReasonException(SkipReason.INVALID_DESTINATION, e);
   }
   Socket dataSocket = null;
   MeteredThrottledInputStream input = null;
   try {
     dataSocket =
         simpleFTP.createSocket(new InetSocketAddress(pasv.getHostName(), pasv.getPort()));
     dataSocket.setSoTimeout(30 * 1000);
     simpleFTP.sendLine("RETR " + filename);
     simpleFTP.readLines(new int[] {150, 125}, null);
     input =
         new MeteredThrottledInputStream(dataSocket.getInputStream(), new AverageSpeedMeter(10));
     connectionHandler.addThrottledConnection(input);
     if (resumed) {
       /* in case we do resume, reposition the writepointer */
       totalLinkBytesLoaded = file.length();
       raf.seek(totalLinkBytesLoaded);
     } else {
       totalLinkBytesLoaded = 0;
     }
     final byte[] buffer = new byte[32767];
     int bytesRead = 0;
     while ((bytesRead = input.read(buffer)) != -1) {
       if (abort.get()) {
         break;
       }
       if (bytesRead > 0) {
         raf.write(buffer, 0, bytesRead);
         totalLinkBytesLoaded += bytesRead;
         totalLinkBytesLoadedLive.addAndGet(bytesRead);
       }
     }
     /* max 10 seks wait for buggy servers */
     simpleFTP.getSocket().setSoTimeout(20 * 1000);
     simpleFTP.shutDownSocket(dataSocket);
     input.close();
     try {
       simpleFTP.readLine();
     } catch (SocketTimeoutException e) {
       LogSource.exception(logger, e);
     }
   } catch (SocketTimeoutException e) {
     LogSource.exception(logger, e);
     error(
         new PluginException(
             LinkStatus.ERROR_DOWNLOAD_INCOMPLETE,
             _JDT.T.download_error_message_networkreset(),
             LinkStatus.VALUE_NETWORK_IO_ERROR));
   } catch (SocketException e) {
     LogSource.exception(logger, e);
     error(
         new PluginException(
             LinkStatus.ERROR_TEMPORARILY_UNAVAILABLE,
             _JDT.T.download_error_message_networkreset(),
             1000l * 60 * 5));
   } catch (ConnectException e) {
     LogSource.exception(logger, e);
     error(
         new PluginException(
             LinkStatus.ERROR_TEMPORARILY_UNAVAILABLE,
             _JDT.T.download_error_message_networkreset(),
             1000l * 60 * 5));
   } finally {
     close(raf);
     close(input);
     close(dataSocket);
     cleanupDownladInterface();
     if (totalLinkBytesLoaded >= 0) {
       downloadable.setDownloadBytesLoaded(totalLinkBytesLoaded);
     }
   }
 }