Index: /trunk/autoquest-plugin-php/.classpath
===================================================================
--- /trunk/autoquest-plugin-php/.classpath	(revision 910)
+++ /trunk/autoquest-plugin-php/.classpath	(revision 910)
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" output="target/classes" path="src/main/java">
+		<attributes>
+			<attribute name="optional" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="src" output="target/test-classes" path="src/test/java">
+		<attributes>
+			<attribute name="optional" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry including="**/*.java" kind="src" path="src/main/resources"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>
Index: /trunk/autoquest-plugin-php/.project
===================================================================
--- /trunk/autoquest-plugin-php/.project	(revision 910)
+++ /trunk/autoquest-plugin-php/.project	(revision 910)
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>quest-plugin-php</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.m2e.core.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.m2e.core.maven2Nature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
Index: /trunk/autoquest-plugin-php/.settings/org.eclipse.jdt.core.prefs
===================================================================
--- /trunk/autoquest-plugin-php/.settings/org.eclipse.jdt.core.prefs	(revision 910)
+++ /trunk/autoquest-plugin-php/.settings/org.eclipse.jdt.core.prefs	(revision 910)
@@ -0,0 +1,5 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.source=1.6
Index: /trunk/autoquest-plugin-php/.settings/org.eclipse.m2e.core.prefs
===================================================================
--- /trunk/autoquest-plugin-php/.settings/org.eclipse.m2e.core.prefs	(revision 910)
+++ /trunk/autoquest-plugin-php/.settings/org.eclipse.m2e.core.prefs	(revision 910)
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
Index: /trunk/autoquest-plugin-php/data/robotfilter.txt
===================================================================
--- /trunk/autoquest-plugin-php/data/robotfilter.txt	(revision 910)
+++ /trunk/autoquest-plugin-php/data/robotfilter.txt	(revision 910)
@@ -0,0 +1,66 @@
+findlinks
+discobot
+Googlebot
+Slurp
+YandexBot
+Spider
+ScholarUniverse
+Baiduspider
+Exabot
+Robot
+MetaGer-Bot
+YandexImages
+Gigabot
+SiteBot
+bingbot
+Ezooms
+Jeeves/Teoma
+msnbot
+DotBot
+changedetection.com/bot.html
+FAST Enterprise Crawler 6
+psbot
+http://ws.daum.net/aboutWebSearch.html
+NerdByNature.Bot
+Sogou web spider
+ssearch_bot
+Purebot
+http://www.icjobs.de
+scoutjet
+Netcraft Web Server Survey
+TurnitinBot
+ia_archiver
+MJ12bot
+Domnutch-Bot
+Eurobot
+GarlikCrawler
+CMS Crawler
+MSIECrawler
+NaverBot
+80legs
+AhrefsBot
+SISTRIX Crawler
+NetcraftSurveyAgent
+Search17Bot
+Semager
+YandexFavicons
+heritrix
+suggybot
+Netluchs
+Ocelli
+PHPCrawl
+SolomonoBot
+Sosospider
+Xerka WebBot
+YahooCacheSystem
+Xenu Link Sleuth
+cmsworldmap
+suchen.de
+amaredo.com/de/suche.html
+ibot
+w3af.sourceforge.net
+w3af.sf.net
+yacybot
+larbin2
+t-h-u-n-d-e-r-s-t-o-n-e
+sqlmap
Index: /trunk/autoquest-plugin-php/pom.xml
===================================================================
--- /trunk/autoquest-plugin-php/pom.xml	(revision 910)
+++ /trunk/autoquest-plugin-php/pom.xml	(revision 910)
@@ -0,0 +1,58 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>de.ugoe.cs.quest</groupId>
+        <artifactId>quest</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+	<modelVersion>4.0.0</modelVersion>
+	<artifactId>quest-plugin-php</artifactId>
+	<name>quest-plugin-php</name>
+	<scm>
+		<url>${quest-scm-trunk-dir}/quest-plugin-php</url>
+	</scm>
+	<dependencies>
+		<dependency>
+			<groupId>de.ugoe.cs</groupId>
+			<artifactId>java-utils</artifactId>
+			<version>0.0.1-SNAPSHOT</version>
+		</dependency>
+		<dependency>
+			<groupId>de.ugoe.cs.quest</groupId>
+			<artifactId>quest-plugin-core</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>de.ugoe.cs.quest</groupId>
+			<artifactId>quest-core-events</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>de.ugoe.cs.quest</groupId>
+			<artifactId>quest-misc</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+	</dependencies>
+	<build>
+		<plugins>
+			<plugin>
+				<artifactId>maven-assembly-plugin</artifactId>
+				<version>2.2-beta-2</version>
+				<configuration>
+					<descriptors>
+						<descriptor>src/main/assembly/config.xml</descriptor>
+					</descriptors>
+				</configuration>
+				<executions>
+					<execution>
+						<id>make-assembly</id>
+						<phase>package</phase>
+						<goals>
+							<goal>single</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+	</build>
+</project>
Index: /trunk/autoquest-plugin-php/src/main/assembly/config.xml
===================================================================
--- /trunk/autoquest-plugin-php/src/main/assembly/config.xml	(revision 910)
+++ /trunk/autoquest-plugin-php/src/main/assembly/config.xml	(revision 910)
@@ -0,0 +1,19 @@
+<assembly
+    xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
+  <id>config</id>
+  <formats>
+    <format>zip</format>
+  </formats>
+  <includeBaseDirectory>false</includeBaseDirectory>
+  <fileSets>
+    <fileSet>
+      <directory>data</directory>
+      <outputDirectory></outputDirectory>
+      <includes>
+        <include>*</include>
+      </includes>
+    </fileSet>
+  </fileSets>
+</assembly>
Index: /trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/quest/plugin/php/PHPPlugin.java
===================================================================
--- /trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/quest/plugin/php/PHPPlugin.java	(revision 910)
+++ /trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/quest/plugin/php/PHPPlugin.java	(revision 910)
@@ -0,0 +1,45 @@
+package de.ugoe.cs.quest.plugin.php;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import de.ugoe.cs.quest.plugin.QuestPlugin;
+
+/**
+ * <p>
+ * Identifier class for the QUEST PHP plug-in.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class PHPPlugin implements QuestPlugin {
+
+	/**
+	 * <p>
+	 * The command packages of this plug-in.
+	 * </p>
+	 */
+	private final static String[] commandPackages = new String[] { "de.ugoe.cs.quest.plugin.php.commands" };
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.quest.plugin.QuestPlugin#getTitle()
+	 */
+	@Override
+	public String getTitle() {
+		return "GUITAR-Plugin";
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.quest.plugin.QuestPlugin#getCommandPackages()
+	 */
+	@Override
+	public List<String> getCommandPackages() {
+	        return Collections.unmodifiableList(Arrays.asList(commandPackages));
+	}
+}
Index: /trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/quest/plugin/php/WeblogParser.java
===================================================================
--- /trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/quest/plugin/php/WeblogParser.java	(revision 910)
+++ /trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/quest/plugin/php/WeblogParser.java	(revision 910)
@@ -0,0 +1,507 @@
+package de.ugoe.cs.quest.plugin.php;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+
+import de.ugoe.cs.quest.eventcore.Event;
+import de.ugoe.cs.quest.eventcore.IEventTarget;
+import de.ugoe.cs.quest.eventcore.IEventType;
+import de.ugoe.cs.quest.plugin.php.eventcore.PHPEventTarget;
+import de.ugoe.cs.quest.plugin.php.eventcore.PHPEventType;
+import de.ugoe.cs.quest.plugin.php.eventcore.WebRequest;
+import de.ugoe.cs.util.FileTools;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * Provides functionality to parse log files with web request.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class WeblogParser {
+
+	/**
+	 * <p>
+	 * Timeout between two sessions in milliseconds.
+	 * </p>
+	 */
+	private long timeout;
+
+	/**
+	 * <p>
+	 * Minimal length of a session. All shorter sessions will be pruned.<br>
+	 * Default: 2
+	 * </p>
+	 */
+	private int minLength = 2;
+
+	/**
+	 * <p>
+	 * Maximal length of a session. All longer sessions will be pruned.<br>
+	 * Default: 100
+	 * </p>
+	 */
+	private int maxLength = 100;
+
+	/**
+	 * <p>
+	 * URL of the server that generated the log that is currently parser; null
+	 * of URL is not available.<br>
+	 * Default: null
+	 * </p>
+	 */
+	private String url = null;
+
+	/**
+	 * <p>
+	 * Collection of generated sequences.
+	 * </p>
+	 */
+	private List<List<Event>> sequences;
+
+	/**
+	 * <p>
+	 * List that stores the users (identified through their cookie id) to each
+	 * sequence.
+	 * </p>
+	 */
+	private List<String> users;
+
+	/**
+	 * <p>
+	 * List that stores the frequent users (identified through their cookie id)
+	 * to each sequence.
+	 * </p>
+	 */
+	private List<String> frequentUsers;
+
+	/**
+	 * <p>
+	 * Sequences for all frequent users.
+	 * </p>
+	 */
+	private List<Collection<List<Event>>> sequencesFrequentUsers;
+
+	/**
+	 * <p>
+	 * Threshold that defines how many sessions of a user are require to deem
+	 * the user frequent. Note, that only sessions whose lengths is in range if
+	 * {@link #minLength} and {@link #maxLength} are counted.
+	 * </p>
+	 */
+	private int frequentUsersThreshold = -1;
+
+	/**
+	 * <p>
+	 * Name and path of the robot filter.
+	 * </p>
+	 */
+	private static final String ROBOTFILTERFILE = "misc/robotfilter.txt";
+
+	/**
+	 * <p>
+	 * Field that contains a regular expression that matches all robots
+	 * contained in {@link #ROBOTFILTERFILE}.
+	 * </p>
+	 */
+	private String robotRegex = null;
+
+	/**
+	 * <p>
+	 * Constructor. Creates a new WeblogParser with a default timeout of
+	 * 3,600,000 milliseconds (1 hour).
+	 * </p>
+	 */
+	public WeblogParser() {
+		this(3600000);
+	}
+
+	/**
+	 * <p>
+	 * Constructor. Creates a new WeblogParser.
+	 * </p>
+	 * 
+	 * @param timeout
+	 *            session timeout
+	 */
+	public WeblogParser(long timeout) {
+		this.timeout = timeout;
+	}
+
+	/**
+	 * <p>
+	 * Returns the generated event sequences.
+	 * </p>
+	 * 
+	 * @return generated event sequences
+	 */
+	public Collection<List<Event>> getSequences() {
+		return sequences;
+	}
+
+	/**
+	 * <p>
+	 * Sets the session timeout.
+	 * </p>
+	 * 
+	 * @param timeout
+	 *            new session timeout
+	 */
+	public void setTimeout(long timeout) {
+		this.timeout = timeout;
+	}
+
+	/**
+	 * <p>
+	 * Sets the minimal length of a session. All sessions that contain less
+	 * events will be pruned.
+	 * </p>
+	 * 
+	 * @param minLength
+	 *            new minimal length
+	 */
+	public void setMinLength(int minLength) {
+		this.minLength = minLength;
+	}
+
+	/**
+	 * <p>
+	 * Sets the maximal length of a session. All sessions that contain more
+	 * events will be pruned.
+	 * </p>
+	 * 
+	 * @param maxLength
+	 *            new maximal length
+	 */
+	public void setMaxLength(int maxLength) {
+		this.maxLength = maxLength;
+	}
+
+	/**
+	 * <p>
+	 * Sets the URL of the server from which this log was generated. Often
+	 * required for replay generation
+	 * </p>
+	 * 
+	 * @param url
+	 *            URL of the server
+	 */
+	public void setUrl(String url) {
+		this.url = url;
+	}
+
+	/**
+	 * <p>
+	 * Sets the threshold for frequent users.
+	 * </p>
+	 * 
+	 * @param threshold
+	 *            threshold value; if the value is &lt;1, the sessions of the
+	 *            frequent users will not be determined
+	 */
+	public void setFrequentUserThreshold(int threshold) {
+		this.frequentUsersThreshold = threshold;
+	}
+
+	/**
+	 * <p>
+	 * Returns the IDs of all frequent users.
+	 * </p>
+	 * 
+	 * @return IDs of the frequent users
+	 */
+	public List<String> getFrequentUsers() {
+		return frequentUsers;
+	}
+
+	/**
+	 * <p>
+	 * Returns the sequences of all frequent users.
+	 * </p>
+	 * </p>
+	 * 
+	 * @return list of the sequences of all frequent users
+	 */
+	public List<Collection<List<Event>>> getFrequentUserSequences() {
+		return sequencesFrequentUsers;
+	}
+
+	/**
+	 * <p>
+	 * Parses a web log file.
+	 * </p>
+	 * 
+	 * @param filename
+	 *            name and path of the log file
+	 * @throws IOException
+	 *             thrown if there is a problem with reading the log file
+	 * @throws FileNotFoundException
+	 *             thrown if the log file is not found
+	 * @throws ParseException
+	 *             thrown the date format is invalid
+	 */
+	public void parseFile(String filename) throws IOException,
+			FileNotFoundException, ParseException {
+		String[] lines = FileTools.getLinesFromFile(filename);
+
+		Map<String, List<Integer>> cookieSessionMap = new HashMap<String, List<Integer>>();
+		Map<String, Long> cookieTimestampMap = new HashMap<String, Long>();
+		
+		int lastId = -1;
+
+		SimpleDateFormat dateFormat = new SimpleDateFormat(
+				"yyyy-MM-dd HH:mm:ss");
+		loadRobotRegex();
+
+		sequences = new ArrayList<List<Event>>();
+		users = new ArrayList<String>();
+
+		int lineCounter = 0;
+		for (String line : lines) {
+			lineCounter++;
+			String[] values = line.substring(1, line.length() - 1).split(
+					"\" \"");
+
+			// use cookie as session identifier
+			int cookieStart = values[0].lastIndexOf('.');
+			String cookie = values[0].substring(cookieStart + 1);
+			String dateString = values[1];
+			long timestamp = dateFormat.parse(dateString).getTime();
+			String uriString = values[2];
+			// String ref = values[3]; // referer is not yet used!
+			String agent;
+			if (values.length > 4) {
+				agent = values[4];
+			} else {
+				agent = "noagent";
+			}
+
+			List<String> postedVars = new ArrayList<String>();
+			if (values.length == 6) { // post vars found
+				for (String postVar : values[5].trim().split(" ")) {
+					if (!isBrokenVariable(postVar)) {
+						postedVars.add(postVar);
+					}
+				}
+			}
+			if (!isRobot(agent)) {
+				try {
+					URI uri = new URI(uriString);
+					String path = uri.getPath();
+					List<String> getVars = extractGetVarsFromUri(uri);
+					
+					IEventType type = new PHPEventType(path, postedVars, getVars);
+					IEventTarget target = new PHPEventTarget(path);
+					Event event = new Event(type, target);
+					event.addReplayable(new WebRequest(url, path, postedVars, getVars));
+
+					// find session and add event
+					List<Integer> sessionIds = cookieSessionMap.get(cookie);
+					if (sessionIds == null) {
+						sessionIds = new ArrayList<Integer>();
+						// start new session
+						sessionIds.add(++lastId);
+						cookieSessionMap.put(cookie, sessionIds);
+						sequences.add(new LinkedList<Event>());
+						users.add(cookie);
+					}
+					Integer lastSessionIndex = sessionIds
+							.get(sessionIds.size() - 1);
+					List<Event> lastSession = sequences
+							.get(lastSessionIndex);
+					long lastEventTime = timestamp;
+					if (!lastSession.isEmpty()) {
+						lastEventTime = cookieTimestampMap.get(cookie);
+					}
+					if (timestamp - lastEventTime > timeout) {
+						sessionIds.add(++lastId);
+						List<Event> newSession = new LinkedList<Event>();
+						newSession.add(event);
+						sequences.add(newSession);
+						users.add(cookie);
+					} else {
+						lastSession.add(event);
+					}
+					cookieTimestampMap.put(cookie, timestamp);
+				} catch (URISyntaxException e) {
+					Console.traceln(Level.FINE, "Ignored line " + lineCounter + ": "
+							+ e.getMessage());
+				}
+			}
+		}
+		Console.traceln(Level.INFO, "" + sequences.size() + " user sequences found");
+		pruneSequences();
+		Console.traceln(Level.INFO, "" + sequences.size()
+				+ " remaining after pruning of sequences shorter than "
+				+ minLength);
+		Set<String> uniqueUsers = new HashSet<String>(users);
+		Console.traceln(Level.INFO, "" + uniqueUsers.size() + " unique users");
+		if (frequentUsersThreshold > 0) {
+			generateFrequentUserSequences(uniqueUsers);
+		}
+	}
+
+	/**
+	 * <p>
+	 * Generates the frequent user sequences, according to the threshold
+	 * {@link #frequentUsersThreshold}.
+	 * </p>
+	 * 
+	 * @param uniqueUsers
+	 *            set with all user IDs
+	 */
+	private void generateFrequentUserSequences(Set<String> uniqueUsers) {
+		frequentUsers = new ArrayList<String>();
+		sequencesFrequentUsers = new ArrayList<Collection<List<Event>>>();
+		for (String user : uniqueUsers) {
+			List<String> tmp = new ArrayList<String>();
+			tmp.add(user);
+			List<String> usersCopy = new LinkedList<String>(users);
+			usersCopy.retainAll(tmp);
+			int size = usersCopy.size();
+			if (size >= frequentUsersThreshold) {
+				frequentUsers.add(user);
+				Collection<List<Event>> sequencesUser = new ArrayList<List<Event>>();
+				for (int i = 0; i < sequences.size(); i++) {
+					if (users.get(i).equals(user)) {
+						sequencesUser.add(sequences.get(i));
+					}
+				}
+				sequencesFrequentUsers.add(sequencesUser);
+
+			}
+		}
+		Console.traceln(Level.INFO, "" + frequentUsers.size() + " users with more than "
+				+ frequentUsersThreshold + " sequences");
+	}
+
+	/**
+	 * <p>
+	 * Prunes sequences shorter than {@link #minLength} and longer than
+	 * {@link #maxLength}.
+	 * </p>
+	 */
+	private void pruneSequences() {
+		int i = 0;
+		while (i < sequences.size()) {
+			if ((sequences.get(i).size() < minLength)
+					|| sequences.get(i).size() > maxLength) {
+				sequences.remove(i);
+				users.remove(i);
+			} else {
+				i++;
+			}
+		}
+
+	}
+
+	/**
+	 * <p>
+	 * Reads {@link #ROBOTFILTERFILE} and creates a regular expression that
+	 * matches all the robots defined in the file. The regular expression is
+	 * stored in the field {@link #robotRegex}.
+	 * </p>
+	 * 
+	 * @throws IOException
+	 *             thrown if there is a problem reading the robot filter
+	 * @throws FileNotFoundException
+	 *             thrown if the robot filter is not found
+	 */
+	private void loadRobotRegex() throws IOException, FileNotFoundException {
+		String[] lines = FileTools.getLinesFromFile(ROBOTFILTERFILE);
+		StringBuilder regex = new StringBuilder();
+		for (int i = 0; i < lines.length; i++) {
+			regex.append("(.*" + lines[i] + ".*)");
+			if (i != lines.length - 1) {
+				regex.append('|');
+			}
+		}
+		robotRegex = regex.toString();
+	}
+
+	/**
+	 * <p>
+	 * Checks whether an agent is a robot.
+	 * </p>
+	 * 
+	 * @param agent
+	 *            agent that is checked
+	 * @return true, if the agent is a robot; false otherwise
+	 */
+	private boolean isRobot(String agent) {
+		return agent.matches(robotRegex);
+	}
+
+	/**
+	 * <p>
+	 * Parses the URI and extracts the GET variables that have been passed.
+	 * </p>
+	 * 
+	 * @param uri
+	 *            URI that is parsed
+	 * @return a list with all GET variables
+	 * @throws URISyntaxException
+	 *             thrown if one of the variables seems to indicate that the
+	 *             request is a malicious attack on the web application
+	 */
+	private List<String> extractGetVarsFromUri(URI uri)
+			throws URISyntaxException {
+		List<String> getVars = new ArrayList<String>();
+		String query = uri.getQuery();
+		if (query != null) {
+			String[] paramPairs = query.split("&");
+			for (String paramPair : paramPairs) {
+				String[] paramSplit = paramPair.split("=");
+				if (!isBrokenVariable(paramSplit[0])) {
+					for (int i = 1; i < paramSplit.length; i++) {
+						checkForAttack(paramSplit[i]);
+					}
+					getVars.add(paramSplit[0]);
+				}
+			}
+		}
+		return getVars;
+	}
+
+	/**
+	 * <p>
+	 * Checks if a variable is broken.Currently, the check rather imprecise and
+	 * checks only if the term &quot;and&quot; is part of the variable name.
+	 * </p>
+	 * 
+	 * @param var
+	 *            variable that is checked
+	 * @return true if the variable is broken, false otherwise
+	 */
+	private boolean isBrokenVariable(String var) {
+		return var.contains("and");
+	}
+
+	/**
+	 * <p>
+	 * Checks if the variable name send with a request seems like an attack on the server.
+	 * </p>
+	 * @param value
+	 * @throws URISyntaxException
+	 */
+	private void checkForAttack(String value) throws URISyntaxException {
+		if (value.contains("UNION+") || value.contains("SELECT+")) {
+			throw new URISyntaxException(value, "possible injection attack");
+		}
+	}
+}
Index: /trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/quest/plugin/php/commands/CMDloadWebSequences.java
===================================================================
--- /trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/quest/plugin/php/commands/CMDloadWebSequences.java	(revision 910)
+++ /trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/quest/plugin/php/commands/CMDloadWebSequences.java	(revision 910)
@@ -0,0 +1,116 @@
+package de.ugoe.cs.quest.plugin.php.commands;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.Collection;
+import java.util.List;
+
+import de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.eventcore.Event;
+import de.ugoe.cs.quest.plugin.php.WeblogParser;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command to load sessions from a web log.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDloadWebSequences implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		if (parameters.size() < 1) {
+			throw new IllegalArgumentException();
+		}
+		String source;
+		String sequencesName;
+		String serverUrl = null;
+		int timeout = -1;
+		int minLength = -1;
+		int maxLength = -1;
+		boolean generateFrequentUsers = false;
+		int frequentUserThreshold = 20;
+		try {
+			source = (String) parameters.get(0);
+			sequencesName = (String) parameters.get(1);
+			if (parameters.size() >= 3) {
+				serverUrl = (String) parameters.get(2);
+			}
+			if (parameters.size() >= 6) {
+				timeout = Integer.parseInt((String) parameters.get(3));
+				minLength = Integer.parseInt((String) parameters.get(4));
+				maxLength = Integer.parseInt((String) parameters.get(5));
+			}
+			if (parameters.size() >= 8) {
+				generateFrequentUsers = Boolean
+						.parseBoolean((String) parameters.get(6));
+				frequentUserThreshold = Integer.parseInt((String) parameters
+						.get(7));
+			}
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		WeblogParser parser = new WeblogParser();
+		if (serverUrl != null) {
+			parser.setUrl(serverUrl);
+		}
+		if (timeout != -1) {
+			parser.setTimeout(timeout);
+			parser.setMinLength(minLength);
+			parser.setMaxLength(maxLength);
+		}
+		if (generateFrequentUsers) {
+			parser.setFrequentUserThreshold(frequentUserThreshold);
+		}
+		try {
+			parser.parseFile(source);
+		} catch (FileNotFoundException e) {
+			Console.printerrln(e.getMessage());
+		} catch (IOException e) {
+			Console.printerrln(e.getMessage());
+		} catch (ParseException e) {
+			Console.printerrln("Invalid format of date stamps.");
+			Console.printerrln(e.getMessage());
+		}
+
+		if (GlobalDataContainer.getInstance().addData(sequencesName,
+				parser.getSequences())) {
+			CommandHelpers.dataOverwritten(sequencesName);
+		}
+		if (generateFrequentUsers) {
+			List<String> frequentUserIDs = parser.getFrequentUsers();
+			List<Collection<List<Event>>> frequentUserSessions = parser
+					.getFrequentUserSequences();
+			for (int i = 0; i < frequentUserIDs.size(); i++) {
+				String seqName = sequencesName + "_" + frequentUserIDs.get(i);
+				if (GlobalDataContainer.getInstance().addData(seqName,
+						frequentUserSessions.get(i))) {
+					CommandHelpers.dataOverwritten(seqName);
+				}
+			}
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "loadWebSequences <filename> <sequencesName> {<serverUrl>} {<timeout> <minSessionLength> <maxSessionLength>} {<generateFrequentUsers> <frequentUserThreshold>}";
+	}
+
+}
Index: /trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/quest/plugin/php/eventcore/PHPEventTarget.java
===================================================================
--- /trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/quest/plugin/php/eventcore/PHPEventTarget.java	(revision 910)
+++ /trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/quest/plugin/php/eventcore/PHPEventTarget.java	(revision 910)
@@ -0,0 +1,99 @@
+
+package de.ugoe.cs.quest.plugin.php.eventcore;
+
+import de.ugoe.cs.quest.eventcore.IEventTarget;
+
+/**
+ * <p>
+ * Event target for PHP web requests.
+ * </p>
+ * 
+ * @version $Revision: $ $Date: Aug 16, 2012$
+ * @author 2012, last modified by $Author: sherbold$
+ */
+public class PHPEventTarget implements IEventTarget {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Path of the PHP request.
+     * </p>
+     */
+    private String path;
+
+    /**
+     * <p>
+     * Constructor. Creates a new PHP event target as the path of the request.
+     * </p>
+     * 
+     * @param path
+     *            path of the URI of the event
+     */
+    public PHPEventTarget(String path) {
+        this.path = path;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.quest.eventcore.IEventTarget#getPlatform()
+     */
+    @Override
+    public String getPlatform() {
+        return "PHP";
+    }
+
+    @Override
+    public String getStringIdentifier() {
+        return this.toString();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return path;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#equals()
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof PHPEventTarget) {
+            if (path != null) {
+                return path.equals(((PHPEventTarget) obj).path);
+            }
+            else {
+                return ((PHPEventTarget) obj).path == null;
+            }
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        if (path != null) {
+            hash = path.hashCode();
+        }
+        return hash;
+    }
+
+}
Index: /trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/quest/plugin/php/eventcore/PHPEventType.java
===================================================================
--- /trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/quest/plugin/php/eventcore/PHPEventType.java	(revision 910)
+++ /trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/quest/plugin/php/eventcore/PHPEventType.java	(revision 910)
@@ -0,0 +1,129 @@
+
+package de.ugoe.cs.quest.plugin.php.eventcore;
+
+import java.util.List;
+
+import de.ugoe.cs.quest.eventcore.IEventType;
+
+/**
+ * <p>
+ * Event type for PHP web requests.
+ * </p>
+ * 
+ * @version $Revision: $ $Date: Aug 16, 2012$
+ * @author 2012, last modified by $Author: sherbold$
+ */
+public class PHPEventType implements IEventType {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Path of the web request.
+     * </p>
+     */
+    private final String path;
+
+    /**
+     * <p>
+     * List of post variable names posted with the request.
+     * </p>
+     */
+    private final List<String> postVars;
+
+    /**
+     * <p>
+     * List of get variable names posted with the request.
+     * </p>
+     */
+    private final List<String> getVars;
+
+    /**
+     * <p>
+     * Constructor. Creates a new PHPEventType from a given path, a list of post variables, and a
+     * list of get variables.
+     * </p>
+     * 
+     * @param path
+     *            path of the URI of the event
+     * @param postVars
+     *            POST variables send with the event
+     * @param getVars
+     *            GET variables send with the event
+     */
+    public PHPEventType(String path, List<String> postVars, List<String> getVars) {
+        this.path = path;
+        this.postVars = postVars;
+        this.getVars = getVars;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.quest.eventcore.IEventType#getName()
+     */
+    @Override
+    public String getName() {
+        return "PHPEventType";
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        String str = path;
+        if (getVars != null && !getVars.isEmpty()) {
+            str += "+GET" + getVars.toString().replace(" ", "");
+        }
+        if (postVars != null && !postVars.isEmpty()) {
+            str += "+POST" + postVars.toString().replace(" ", "");
+        }
+        return str;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof PHPEventType) {
+            PHPEventType other = (PHPEventType) obj;
+            return ((path == other.path) || (path != null && path.equals(other.path))) &&
+                ((postVars == other.postVars) || (postVars!=null && postVars.equals(other.postVars))) &&
+                ((getVars == other.getVars) || (getVars!=null && getVars.equals(other.getVars)));
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        int hash = 17;
+        int multiplier = 7;
+        if (path != null) {
+            hash = hash * multiplier + path.hashCode();
+        }
+        if (postVars != null) {
+            hash = hash * multiplier + postVars.hashCode();
+        }
+        if (getVars != null) {
+            hash = hash * multiplier + getVars.hashCode();
+        }
+        return hash;
+    }
+
+}
Index: /trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/quest/plugin/php/eventcore/WebRequest.java
===================================================================
--- /trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/quest/plugin/php/eventcore/WebRequest.java	(revision 910)
+++ /trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/quest/plugin/php/eventcore/WebRequest.java	(revision 910)
@@ -0,0 +1,170 @@
+package de.ugoe.cs.quest.plugin.php.eventcore;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.ugoe.cs.quest.IReplayDecorator;
+import de.ugoe.cs.quest.eventcore.IReplayable;
+
+/**
+ * <p>
+ * Contains all information related to a web request, i.e., the path, the POST variables and the GET
+ * variables. The generated replay are for the command line tool {@code curl}. The requests do not
+ * contain correct values for the POST and GET request. Instead, only the parameters that are part
+ * of the requests are added and the values of the parameters are DATA_$PARAMNAME$_DATA, where
+ * $PARAMNAME$ is the upper case string of the parameter name. This allows test data generators to
+ * insert concrete values, as EventBench does not include a test data generator for web software.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class WebRequest implements IReplayable {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * POST variables of the web request.
+     * </p>
+     */
+    List<String> postVars;
+
+    /**
+     * <p>
+     * GET variables of the web request.
+     * </p>
+     */
+    List<String> getVars;
+
+    /**
+     * <p>
+     * URI of the web request.
+     * </p>
+     */
+    String targetUri;
+
+    /**
+     * <p>
+     * URL of the server.
+     * </p>
+     */
+    String serverUrl;
+
+    /**
+     * <p>
+     * Constructor. Creates a new WebRequest.
+     * </p>
+     * 
+     * @param uri
+     *            URI of the request
+     * @param postVars
+     *            POST variables of the request
+     * @param getVars
+     *            GET variables of the request
+     */
+    public WebRequest(String url, String uri, List<String> postVars, List<String> getVars) {
+        serverUrl = url;
+        targetUri = uri;
+        this.postVars = new ArrayList<String>(postVars); // defensive copy
+        this.getVars = new ArrayList<String>(getVars);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.quest.eventcore.IReplayable#getReplay()
+     */
+    @Override
+    public String getReplay() {
+        StringBuilder builder = new StringBuilder();
+        builder.append("curl");
+        if (!postVars.isEmpty()) {
+            boolean isFirstPost = true;
+            for (String postVar : postVars) {
+                if (isFirstPost) {
+                    builder.append(" --data \"");
+                    isFirstPost = false;
+                }
+                else {
+                    builder.append('&');
+                }
+                builder.append(postVar + "=DATA_" + postVar.toUpperCase() + "_DATA");
+            }
+            builder.append('\"');
+        }
+        builder.append(' ');
+        if (serverUrl != null) {
+            builder.append(serverUrl);
+        }
+        builder.append(targetUri);
+        if (!getVars.isEmpty()) {
+            boolean isFirstGet = true;
+            for (String getVar : getVars) {
+                if (isFirstGet) {
+                    builder.append('?');
+                    isFirstGet = false;
+                }
+                else {
+                    builder.append('&');
+                }
+                builder.append(getVar + "=DATA_" + getVar.toUpperCase() + "_DATA");
+            }
+        }
+        return builder.toString();
+    }
+
+    /**
+     * <p>
+     * Two {@link WebRequest}s are equal, if their {@link #targetUri}, {@link #postVars}, and
+     * {@link #getVars} are equal.
+     * </p>
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (other instanceof WebRequest) {
+            return targetUri.equals(((WebRequest) other).targetUri) &&
+                postVars.equals(((WebRequest) other).postVars) &&
+                getVars.equals(((WebRequest) other).getVars);
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        int multiplier = 17;
+        int hash = 42;
+
+        hash = multiplier * hash + targetUri.hashCode();
+        hash = multiplier * hash + postVars.hashCode();
+        hash = multiplier * hash + getVars.hashCode();
+
+        return hash;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.quest.eventcore.IReplayable#getDecorator()
+     */
+    @Override
+    public IReplayDecorator getDecorator() {
+        return null;
+    }
+
+}
Index: /trunk/autoquest-plugin-php/src/main/resources/manuals/loadWebSequences
===================================================================
--- /trunk/autoquest-plugin-php/src/main/resources/manuals/loadWebSequences	(revision 910)
+++ /trunk/autoquest-plugin-php/src/main/resources/manuals/loadWebSequences	(revision 910)
@@ -0,0 +1,15 @@
+Parses a log created by the PHPMonitor and creates a collection of user sequences. 
+
+The optional parameters <timeout>, <minSessionLength>, and <maxSessionLength> can only be used together.
+
+The optional parameters <generateFrequentUsers> and <frequentUserThreshold> can only be used together. 
+
+$USAGE$
+<filename> name of the log file
+<sequencesName> name of the sequences
+<serverUrl> URL of the server that has been monitored
+<timeout> optional; timeout of a user session in seconds; default: 360000 (1 hour) 
+<minSessionLength> optional; minimal length of a user session; default: 2
+<maxSessionLength> optional; maximal length of a user session; default: 100
+<generateFrequentUsers> optional; if true, seperate sequence collection for frequent users are generated; default: false
+<frequentUserThreshold> optional; defines how many sessions of the same user are required, to be counted as a frequent user
