VirusScanningResult 
   CustomVirusScanner::Scan(const String &executablePath, int virusReturnCode, const String &sFilename)
   {
      LOG_DEBUG("Running custom virus scanner...");


      String sPath = FileUtilities::GetFilePath(sFilename);

      String sCommandLine;

      if (executablePath.Find(_T("%FILE%")) >= 0)
      {
         sCommandLine = executablePath;
         sCommandLine.Replace(_T("%FILE%"), sFilename);
      }
      else
         sCommandLine.Format(_T("%s %s"), executablePath, sFilename);

      unsigned int exitCode = 0;
      ProcessLauncher launcher(sCommandLine, sPath);
      launcher.SetErrorLogTimeout(20000);
      if (!launcher.Launch(exitCode))
      {
         return VirusScanningResult("CustomVirusScanner::Scan", "Unable to launch executable.");
      }

      String sDebugMessage = Formatter::Format("Scanner: {0}. Return code: {1}", sCommandLine, exitCode);
      LOG_DEBUG(sDebugMessage);

      if (exitCode == virusReturnCode)
         return VirusScanningResult(VirusScanningResult::VirusFound, "Unknown");
      else
         return VirusScanningResult(VirusScanningResult::NoVirusFound, Formatter::Format("Return code: {0}", exitCode));

   }
   VirusScanningResult 
   ClamWinVirusScanner::Scan(const String &scannerExecutable, const String &databasePath, const String &sFilename)
   {
      LOG_DEBUG("Running ClamWin");

      String sPath = FileUtilities::GetFilePath(sFilename);
      String sFileToScan = FileUtilities::GetFileNameFromFullPath(sFilename);
      String sTempDir = Utilities::GetWin32TempDirectory();

      String sCommandLine;
      sCommandLine.Format(_T("%s --database=\"%s\" \"%s\" --tempdir=\"%s\""), scannerExecutable, databasePath, sFileToScan, sTempDir);

      unsigned int exitCode = 0;
      ProcessLauncher launcher(sCommandLine, sPath);
      launcher.SetErrorLogTimeout(20000);
      if (!launcher.Launch(exitCode))
      {
         return VirusScanningResult("ClamWinVirusScanner::Scan", "Unable to launch executable.");
      }

      String sDebugMessage = Formatter::Format("ClamWin: {0}. Return code: {1}", sCommandLine, exitCode);
      LOG_DEBUG(sDebugMessage);

      if (exitCode == 1)
         return VirusScanningResult(VirusScanningResult::VirusFound, "Unknown");
      else
         return VirusScanningResult(VirusScanningResult::NoVirusFound, Formatter::Format("Return code: {0}", exitCode));

   }
   VirusScanningResult
   VirusScanner::_ScanFile(const String &fileName)
   {
      AntiVirusConfiguration &antiVirusConfig = Configuration::Instance()->GetAntiVirusConfiguration();

      if (antiVirusConfig.ClamWinEnabled())
      {
         VirusScanningResult result = ClamWinVirusScanner::Scan(fileName);

         if (result.GetVirusFound())
            return result;
         else if (result.GetErrorOccured())
            _ReportScanningError(result);
      }

      if (antiVirusConfig.GetCustomScannerEnabled())
      {
         VirusScanningResult result = CustomVirusScanner::Scan(fileName);

         if (result.GetVirusFound())
            return result;
         else if (result.GetErrorOccured())
            _ReportScanningError(result);
      }

      if (antiVirusConfig.GetClamAVEnabled())
      {
         VirusScanningResult result = ClamAVVirusScanner::Scan(fileName);
         
         if (result.GetVirusFound())
            return result;
         else if (result.GetErrorOccured())
            _ReportScanningError(result);
      }

      return VirusScanningResult(VirusScanningResult::NoVirusFound, "");
   }
   VirusScanningResult
   ClamAVVirusScanner::Scan(const String &hostName, int primaryPort, const String &sFilename)
   {
      LOG_DEBUG("Connecting to ClamAV virus scanner...");

      int streamPort = 0;

      TimeoutCalculator calculator;

      SynchronousConnection commandConnection(calculator.Calculate(IniFileSettings::Instance()->GetClamMinTimeout(), IniFileSettings::Instance()->GetClamMaxTimeout()));
      if (!commandConnection.Connect(hostName, primaryPort))
      {
         return VirusScanningResult(_T("ClamAVVirusScanner::Scan"), 
            Formatter::Format("Unable to connect to ClamAV server at {0}:{1}.", hostName, primaryPort));
      }

      if (!commandConnection.Write("STREAM\r\n"))
         return VirusScanningResult("ClamAVVirusScanner::Scan", "Unable to write STREAM command.");

      AnsiString readData;
      if (!commandConnection.ReadUntil("\n", readData))
         return VirusScanningResult("ClamAVVirusScanner::Scan", "Unable to read STREAM command response.");

      if (!readData.StartsWith("PORT"))
         return VirusScanningResult("ClamAVVirusScanner::Scan", Formatter::Format("Protocol error. Unexpected response: {0}.", readData));
      
      readData.TrimRight("\n");

      // Determine port.
      std::string portString = readData.Mid(5);
      
      if (!StringParser::TryParseInt(portString, streamPort))
         return VirusScanningResult("ClamAVVirusScanner::Scan", Formatter::Format("Protocol error. Unexpected response: {0} (Unable to parse port).", readData));

      LOG_DEBUG("Connecting to ClamAV stream port...");
      SynchronousConnection streamConnection(15);
      if (!streamConnection.Connect(hostName, streamPort))
         return VirusScanningResult("ClamAVVirusScanner::Scan", Formatter::Format("Unable to connect to ClamAV stream port at {0}:{1}.", hostName, streamPort));

      // Send the file on the stream socket.
      File oFile;
      if (!oFile.Open(sFilename, File::OTReadOnly))
      {
         String sErrorMsg = Formatter::Format("Could not send file {0} via socket since it does not exist.", sFilename);
         return VirusScanningResult("ClamAVVirusScanner::Scan", sErrorMsg);
      }

      const int STREAM_BLOCK_SIZE = 4096;
      const int maxIterations = 100000;
      for (int i = 0; i < maxIterations; i++)
      {
         std::shared_ptr<ByteBuffer> pBuf = oFile.ReadChunk(STREAM_BLOCK_SIZE);

         if (!pBuf)
            break;

         // Send the request.
         if (!streamConnection.Write(*pBuf))
            return VirusScanningResult("ClamAVVirusScanner::Scan", "Unable to write data to stream port.");
      }

      streamConnection.Close();

      if (!commandConnection.ReadUntil("\n", readData))
         return VirusScanningResult("ClamAVVirusScanner::Scan", "Unable to read response (after streaming).");

      readData.TrimRight("\n");

      // Parse the response and see if a virus was reported.
      try
      {
         const regex expression("^stream.*: (.*) FOUND$"); 
         cmatch what; 
         if(regex_match(readData.c_str(), what, expression)) 
         {
            LOG_DEBUG("Virus detected: " + what[1]);
            return VirusScanningResult(VirusScanningResult::VirusFound, String(what[1]));
         }
         else
         {
            LOG_DEBUG("No virus detected: " + readData);
            return VirusScanningResult(VirusScanningResult::NoVirusFound, Formatter::Format("Result: {0}", readData));
         }
      }
      catch (std::runtime_error &) // regex_match will throw runtime_error if regexp is too complex.
      {
         return VirusScanningResult("ClamAVVirusScanner::Scan", "Unable to parse regular expression.");
      }

      
   }