content: package ciscoroutertool.scanner;
  
  import ciscoroutertool.rules.Rule;
  import ciscoroutertool.scanner.parser.RouterConfigManager;
  import ciscoroutertool.utils.Host;
  import com.jcraft.jsch.Channel;
  import com.jcraft.jsch.JSch;
  import com.jcraft.jsch.Session;
  import net.sf.expectit.Expect;
  import net.sf.expectit.ExpectBuilder;
  
  import java.io.InputStream;
  import java.io.OutputStream;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.concurrent.Callable;
  
  import static net.sf.expectit.matcher.Matchers.contains;
  
  /**
   * Runs the scan for a host and holds the results
   * @version 0.01ALPHA
   * @author Andrew Johnston
   */
  public class Scanner implements Callable<HostReport> {
  
      /**
       * The list of rules to check each host against.
       */
      public static ArrayList<Rule> rules;
      
      /**
       * The list of rules that the host matched.
       */
      private ArrayList<Rule> matched;
      
      /**
       * The port the SSH Server is listening on.
       */
      private static final int SSH_PORT = 22;
      
      /**
       * The command to get the full configuration file from the device.
       */
      private static final String GET_ALL_CONFIG = "show running-config full";
  
      /**
       * The command to enable superuser privileges
       */
      private static final String ENABLE_SUPERUSER = "enable";
  
      /**
       * The command to disable the use of "more" to pipe the config file
       */
      private static final String DISABLE_OUTPUT_BUFFERING = "terminal length 0";
      
      /**
       * The host that the Scanner object will scan.
       */
      private final Host host;
  
      private static final String PASSWORD_PROMPT = "Password:";
  
      /**
       * Initializes the scanner.
       * @param h The host to be scanned
       */
      public Scanner(Host h) {
          host = h;
      }
  
  
      /**
       * Performs the scan and returns a HostReport containing all matched rules.
       * @return The HostReport containing the matched rules.
       * @throws Exception Any Unhandled exception generated during the scan.
       */
      @Override
      public HostReport call()  {
          ArrayList<String> configlines = null;
          try {
              configlines = getConfigFile();
          } catch (Exception e) {
              System.err.println("Failed to connect to host " + host.toString() + ". Please check " +
                  "URL and credentials and rerun.");
              System.err.println("The exact error was: " + e.getMessage());
              //We won't have any data on the host, so construct an empty report and throw it back
              HostReport failedToConnect = new HostReport(host);
              return failedToConnect;
          }
          ArrayList<String> activeLines = 
                  RouterConfigManager.getActiveConfig(configlines);
          return getHostReport(activeLines);
      }
      
      /**
       * Connects to the device and retrieves the configuration file from the 
       * device.
       * @return A ArrayList containing the output from the GET_ALL_CONFIG
       * command 
       */
      private ArrayList<String> getConfigFile()  throws Exception{
          JSch jsch = new JSch();
          InputStream in = null;
          Session session = jsch.getSession(
                  host.getUser(),
                  host.toString(),
                  SSH_PORT);
          session.setPassword(host.getPass());
          //If this line isn't present, every host must be in known_hosts
          session.setConfig("StrictHostKeyChecking", "no");
          session.connect();
          Channel channel = session.openChannel("shell");
          in = channel.getInputStream();
          OutputStream outputStream = channel.getOutputStream();
          Expect expect = new ExpectBuilder()
                  .withOutput(outputStream)
                  .withInputs(channel.getInputStream(), channel.getExtInputStream())
                          //.withEchoOutput(System.out)
                          //.withEchoInput(System.out)
                  .build();
  
          channel.connect();
          if (host.usesEnable()) {
              expect.expect(contains(">"));
              expect.sendLine(ENABLE_SUPERUSER);
              expect.expect(contains(PASSWORD_PROMPT));
              expect.sendLine(host.getEnablePass());
          }
          expect.expect(contains("#")); //#
          expect.sendLine(DISABLE_OUTPUT_BUFFERING); //terminal length 0
          expect.expect(contains("#")); //#
          expect.sendLine(GET_ALL_CONFIG); //show running-config full
          String result = expect.expect(contains("#")).getBefore(); //#
          channel.disconnect();
          session.disconnect();
          expect.close();
          String[] arrLines = result.split("\n");
          ArrayList<String> lines = new ArrayList<>(Arrays.asList(arrLines));
          return lines;
      }
  
      /**
       * Compares the rules to the configuration file and stores the matched ones
       * @param activeConfig The configuration with all shutdown interfaces 
       * removed.
       * @return A HostReport containing all matched rules
       */
      private HostReport getHostReport(ArrayList<String> activeConfig) {
          HostReport report = new HostReport(host);
          for (Rule r : rules) {
              if (r.matchesRule(activeConfig)) {
                  report.addMatchedRule(r);
              }
          }
          return report;
      }
  }
  