/*******************************************************************************
* Copyright (c) 2011 Subgraph.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Subgraph - initial API and implementation
******************************************************************************/
package com.subgraph.vega.impl.scanner;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
import org.apache.http.cookie.Cookie;
import com.subgraph.vega.api.analysis.IContentAnalyzerFactory;
import com.subgraph.vega.api.crawler.IWebCrawlerFactory;
import com.subgraph.vega.api.events.EventListenerManager;
import com.subgraph.vega.api.events.IEvent;
import com.subgraph.vega.api.events.IEventHandler;
import com.subgraph.vega.api.http.requests.IHttpRequestEngine;
import com.subgraph.vega.api.http.requests.IHttpRequestEngineConfig;
import com.subgraph.vega.api.http.requests.IHttpRequestEngineFactory;
import com.subgraph.vega.api.model.IModel;
import com.subgraph.vega.api.model.IWorkspace;
import com.subgraph.vega.api.model.WorkspaceCloseEvent;
import com.subgraph.vega.api.model.WorkspaceOpenEvent;
import com.subgraph.vega.api.model.alerts.IScanInstance;
import com.subgraph.vega.api.scanner.IScanProbeResult;
import com.subgraph.vega.api.scanner.IScanner;
import com.subgraph.vega.api.scanner.IScannerConfig;
import com.subgraph.vega.api.scanner.LockStatusEvent;
import com.subgraph.vega.api.scanner.modules.IBasicModuleScript;
import com.subgraph.vega.api.scanner.modules.IResponseProcessingModule;
import com.subgraph.vega.api.scanner.modules.IScannerModule;
import com.subgraph.vega.api.scanner.modules.IScannerModuleRegistry;
public class Scanner implements IScanner {
private IModel model;
private IScanInstance currentScan;
private IScannerConfig persistentConfig;
private IWebCrawlerFactory crawlerFactory;
private IHttpRequestEngineFactory requestEngineFactory;
private IScannerModuleRegistry moduleRegistry;
private ScannerTask scannerTask;
private Thread scannerThread;
private IWorkspace currentWorkspace;
private IContentAnalyzerFactory contentAnalyzerFactory;
private List<IResponseProcessingModule> responseProcessingModules;
private List<IBasicModuleScript> basicModules;
private List<IScannerModule> allModules;
private volatile ScanProbe currentProbe;
private final Object lock = new Object();
private final EventListenerManager lockStatusEventManager = new EventListenerManager();
private boolean isLocked = false;
protected void activate() {
currentWorkspace = model.addWorkspaceListener(new IEventHandler() {
@Override
public void handleEvent(IEvent event) {
if(event instanceof WorkspaceOpenEvent)
handleWorkspaceOpen((WorkspaceOpenEvent) event);
else if(event instanceof WorkspaceCloseEvent)
handleWorkspaceClose((WorkspaceCloseEvent) event);
}
});
reloadModules();
}
private void reloadModules() {
if(responseProcessingModules == null || basicModules == null) {
responseProcessingModules = moduleRegistry.getResponseProcessingModules();
basicModules = moduleRegistry.getBasicModules();
} else {
responseProcessingModules = moduleRegistry.updateResponseProcessingModules(responseProcessingModules);
basicModules = moduleRegistry.updateBasicModules(basicModules);
}
allModules = new ArrayList<IScannerModule>();
allModules.addAll(responseProcessingModules);
allModules.addAll(basicModules);
}
private void resetModuleTimestamps() {
for(IScannerModule m: allModules) {
m.getRunningTimeProfile().reset();
}
}
private void handleWorkspaceOpen(WorkspaceOpenEvent event) {
this.currentWorkspace = event.getWorkspace();
}
private void handleWorkspaceClose(WorkspaceCloseEvent event) {
this.currentWorkspace = null;
}
protected void deactivate() {
}
IWebCrawlerFactory getCrawlerFactory() {
return crawlerFactory;
}
IModel getModel() {
return model;
}
@Override
public IScannerConfig getScannerConfig() {
return persistentConfig;
}
@Override
public List<IScannerModule> getAllModules() {
reloadModules();
return allModules;
}
@Override
public IScannerConfig createScannerConfig() {
return new ScannerConfig();
}
@Override
public void setScannerConfig(IScannerConfig config) {
persistentConfig = config;
}
@Override
public IScanProbeResult probeTargetURI(URI uri) {
synchronized(lock) {
if(!isLocked) {
throw new IllegalStateException("Scanner must be locked before sending probe requests");
}
}
if(currentProbe != null) {
throw new IllegalStateException("Another target probe is already in progress");
}
final HttpClient client = requestEngineFactory.createUnencodingClient();
final IHttpRequestEngine requestEngine = requestEngineFactory.createRequestEngine(client, requestEngineFactory.createConfig() );
currentProbe = new ScanProbe(uri, requestEngine);
final IScanProbeResult probeResult = currentProbe.runProbe();
currentProbe = null;
return probeResult;
}
@Override
public synchronized void startScanner(IScannerConfig config) {
synchronized(lock) {
if(!isLocked) {
throw new IllegalStateException("Scanner must be locked before starting scan.");
}
}
if(currentScan != null && currentScan.getScanStatus() != IScanInstance.SCAN_COMPLETED && currentScan.getScanStatus() != IScanInstance.SCAN_CANCELLED) {
throw new IllegalStateException("Scanner is already running. Verify scanner is not running with getScannerStatus() before trying to start.");
}
if(config.getBaseURI() == null)
throw new IllegalArgumentException("Cannot start scan because no baseURI was specified");
IHttpRequestEngineConfig requestEngineConfig = requestEngineFactory.createConfig();
if (config.getCookieList() != null) {
CookieStore cookieStore = requestEngineConfig.getCookieStore();
for (Cookie c: config.getCookieList()) {
cookieStore.addCookie(c);
}
}
if(config.getMaxRequestsPerSecond() > 0) {
requestEngineConfig.setRequestsPerMinute(config.getMaxRequestsPerSecond() * 60);
}
requestEngineConfig.setMaxConnections(config.getMaxConnections());
requestEngineConfig.setMaxConnectionsPerRoute(config.getMaxConnections());
requestEngineConfig.setMaximumResponseKilobytes(config.getMaxResponseKilobytes());
final HttpClient client = requestEngineFactory.createUnencodingClient();
final IHttpRequestEngine requestEngine = requestEngineFactory.createRequestEngine(client, requestEngineConfig);
reloadModules();
resetModuleTimestamps();
currentWorkspace.lock();
currentScan = currentWorkspace.getScanAlertRepository().createNewScanInstance();
scannerTask = new ScannerTask(currentScan, this, config, requestEngine,
currentWorkspace, contentAnalyzerFactory.createContentAnalyzer(currentScan),
responseProcessingModules, basicModules);
scannerThread = new Thread(scannerTask);
currentWorkspace.getScanAlertRepository().setActiveScanInstance(currentScan);
currentScan.updateScanStatus(IScanInstance.SCAN_STARTING);
scannerThread.start();
}
@Override
public void stopScanner() {
if(scannerTask != null) {
scannerTask.stop();
}
ScanProbe probe = currentProbe;
if(probe != null) {
probe.abort();
}
}
@Override
public void runDomTests() {
moduleRegistry.runDomTests();
}
@Override
public void addLockStatusListener(IEventHandler listener) {
synchronized (lock) {
lockStatusEventManager.addListener(listener);
listener.handleEvent(new LockStatusEvent(isLocked));
}
}
@Override
public void removeLockStatusListener(IEventHandler listener) {
synchronized (lock) {
lockStatusEventManager.removeListener(listener);
}
}
@Override
public boolean lock() {
synchronized (lock) {
if(isLocked) {
return false;
}
isLocked = true;
lockStatusEventManager.fireEvent(new LockStatusEvent(true));
return true;
}
}
@Override
public synchronized void unlock() {
synchronized(lock) {
isLocked = false;
lockStatusEventManager.fireEvent(new LockStatusEvent(false));
}
}
protected void setCrawlerFactory(IWebCrawlerFactory crawlerFactory) {
this.crawlerFactory = crawlerFactory;
}
protected void unsetCrawlerFactory(IWebCrawlerFactory crawlerFactory) {
this.crawlerFactory = null;
}
protected void setRequestEngineFactory(IHttpRequestEngineFactory factory) {
this.requestEngineFactory = factory;
}
protected void unsetRequestEngineFactory(IHttpRequestEngineFactory factory) {
this.requestEngineFactory = null;
}
protected void setModuleRegistry(IScannerModuleRegistry registry) {
this.moduleRegistry = registry;
}
protected void unsetModuleRegistry(IScannerModuleRegistry registry) {
this.moduleRegistry = null;
}
protected void setModel(IModel model) {
this.model = model;
}
protected void unsetModel(IModel model) {
this.model = null;
}
protected void setContentAnalyzerFactory(IContentAnalyzerFactory factory) {
this.contentAnalyzerFactory = factory;
}
protected void unsetContentAnalyzerFactory(IContentAnalyzerFactory factory) {
this.contentAnalyzerFactory = null;
}
}