@Override
 public void runCaptchaDDosProtection(String id) throws InterruptedException {
   final TimeTracker tracker = ChallengeResponseController.getInstance().getTracker(id);
   final TrackerJob trackerJob =
       new TrackerJob(1) {
         @Override
         public void waitForNextSlot(long waitFor) throws InterruptedException {
           while (waitFor > 0 && !isAbort()) {
             synchronized (this) {
               if (waitFor <= 0) {
                 return;
               }
               if (waitFor > 1000) {
                 wait(1000);
               } else {
                 this.wait(waitFor);
               }
               waitFor -= 1000;
             }
           }
           if (isAbort()) {
             throw new InterruptedException("PluginForDecrypt is aborting");
           }
         };
       };
   tracker.wait(trackerJob);
 }
 protected <ReturnType> ReturnType handleCaptchaChallenge(Challenge<ReturnType> c)
     throws CaptchaException, InterruptedException, DecrypterException {
   if (c instanceof ImageCaptchaChallenge) {
     final File captchaFile = ((ImageCaptchaChallenge) c).getImageFile();
     cleanUpCaptchaFiles.add(captchaFile);
   }
   if (Thread.currentThread() instanceof SingleDownloadController) {
     logger.severe("PluginForDecrypt.getCaptchaCode inside SingleDownloadController!?");
   }
   c.setTimeout(getCaptchaTimeout());
   invalidateLastChallengeResponse();
   final BlacklistEntry blackListEntry = CaptchaBlackList.getInstance().matches(c);
   if (blackListEntry != null) {
     logger.warning("Cancel. Blacklist Matching");
     throw new CaptchaException(blackListEntry);
   }
   try {
     ChallengeResponseController.getInstance().handle(c);
   } catch (InterruptedException e) {
     LogSource.exception(logger, e);
     throw e;
   } catch (SkipException e) {
     LogSource.exception(logger, e);
     switch (e.getSkipRequest()) {
       case BLOCK_ALL_CAPTCHAS:
         CaptchaBlackList.getInstance().add(new BlockAllCrawlerCaptchasEntry(getCrawler()));
         break;
       case BLOCK_HOSTER:
         CaptchaBlackList.getInstance()
             .add(new BlockCrawlerCaptchasByHost(getCrawler(), getHost()));
         break;
       case BLOCK_PACKAGE:
         CaptchaBlackList.getInstance()
             .add(new BlockCrawlerCaptchasByPackage(getCrawler(), getCurrentLink()));
         break;
       case REFRESH:
         // refresh is not supported from the pluginsystem right now.
         return c.getRefreshTrigger();
       case STOP_CURRENT_ACTION:
         if (Thread.currentThread() instanceof LinkCrawlerThread) {
           LinkCollector.getInstance().abort();
           // Just to be sure
           CaptchaBlackList.getInstance().add(new BlockAllCrawlerCaptchasEntry(getCrawler()));
         }
         break;
       default:
         break;
     }
     throw new CaptchaException(e.getSkipRequest());
   }
   if (!c.isSolved()) {
     throw new DecrypterException(DecrypterException.CAPTCHA);
   }
   return c.getResult().getValue();
 }