Pārlūkot izejas kodu

Initial commit: webservice

Mathias Landhäußer 11 gadi atpakaļ
vecāks
revīzija
8ac7e3b30d
26 mainītis faili ar 6499 papildinājumiem un 0 dzēšanām
  1. 165 0
      jplag.webService/INSTALL_NOTES
  2. 67 0
      jplag.webService/README
  3. 14 0
      jplag.webService/etc/config-server.xml
  4. 24 0
      jplag.webService/etc/index.html
  5. 14 0
      jplag.webService/generatecerts.sh
  6. BIN
      jplag.webService/jplag.key
  7. 6 0
      jplag.webService/jplag_users.xml
  8. 218 0
      jplag.webService/pom.xml
  9. 703 0
      jplag.webService/src/main/java/jplagWebService/serverAccess/AccessStructure.java
  10. 199 0
      jplag.webService/src/main/java/jplagWebService/serverAccess/JPlagServerAccessHandler.java
  11. 387 0
      jplag.webService/src/main/java/jplagWebService/serverAccess/MailTemplateAdmin.java
  12. 48 0
      jplag.webService/src/main/java/jplagWebService/serverAccess/MailTemplateData.java
  13. 166 0
      jplag.webService/src/main/java/jplagWebService/serverAccess/MemoryManager.java
  14. 350 0
      jplag.webService/src/main/java/jplagWebService/serverAccess/RequestAdmin.java
  15. 476 0
      jplag.webService/src/main/java/jplagWebService/serverAccess/ResultAdmin.java
  16. 91 0
      jplag.webService/src/main/java/jplagWebService/serverAccess/StatusDecorator.java
  17. 387 0
      jplag.webService/src/main/java/jplagWebService/serverAccess/TransferManager.java
  18. 75 0
      jplag.webService/src/main/java/jplagWebService/serverAccess/TrustAllSSLSocketFactory.java
  19. 956 0
      jplag.webService/src/main/java/jplagWebService/serverAccess/UserAdmin.java
  20. 362 0
      jplag.webService/src/main/java/jplagWebService/serverImpl/JPlagCentral.java
  21. 383 0
      jplag.webService/src/main/java/jplagWebService/serverImpl/JPlagTypImpl.java
  22. 22 0
      jplag.webService/src/main/resources/WS-ISwA.xsd
  23. 7 0
      jplag.webService/src/main/resources/jplagWebService/serverAccess/mailconfig.properties
  24. 1318 0
      jplag.webService/src/main/webapp/WEB-INF/JPlagService.wsdl
  25. 27 0
      jplag.webService/src/main/webapp/WEB-INF/jaxrpc-ri.xml
  26. 34 0
      jplag.webService/src/main/webapp/WEB-INF/web.xml

+ 165 - 0
jplag.webService/INSTALL_NOTES

@@ -0,0 +1,165 @@
+Installation notes:
+
+- Create a home directory for the JPlag web service.
+- Create a file named "jplag_users.xml" in it with content of the following form
+  (this case creates two admins having quite some not so valid (say missing)
+   entries, but they can be just added with the AdminTool):
+
+<?xml version="1.0" encoding="UTF-8"?>
+<jplag-users>
+	<user username="mo" password="om" state="192" />
+	<user username="emeric" password="emeric" state="192" />
+</jplag-users>
+
+
+
+VERY IMPORTANT: If you're using Java 1.4.2_06, you'll probably need to use
+				the -server option,otherwise the complete server will crash
+				during comparison because of a bug in the VM.
+
+================================================================================
+				
+"SUN One Application Server 8" install notes:
+
+- Home directory setup:
+  In the JVM Options page on the admin console add
+     "-Djava_home=<path to jplag home directory>"
+  Also replace the "-client" option by "-server" if you're using Java 1.4.2_06
+  Additionally make sure that -Xmx1024M is set to avoid "Out of memory" errors.
+
+- Permission setup:
+  Add the following lines to the end of the domain's server.policy file to
+  allow the JPlag web service to create, use and delete files in its home dir:
+
+--------------------------------------------------------------------------------
+
+// These permissions apply to the JPlag web service
+grant codeBase "file:${com.sun.aas.instanceRoot}/applications/j2ee-modules/JplagService/WEB-INF/-" {
+    permission java.io.FilePermission "${jplag_home}${/}-", "read,write,delete";
+};
+
+--------------------------------------------------------------------------------
+
+- Copy "jplag.jar" and "antlr.jar" to the "lib/ext" directory inside the used
+  domain directory
+- Copy the "JplagService.war" file to the domains "autodeploy" directory
+- (Re-)Start the server (you may try without restarting, though you probably get
+  problems, if the server doesn't update it's libraries because of antlr.jar,
+  which is packed inside a server library)
+  
+- SSL setup
+
+  Go into the domains's "config" directory and execute the following command:
+  
+    keytool -genkey -alias jplag-server -keystore keystore.jks
+
+  Use "changeit" (really "changeit") as keystore and key password.    
+
+  In the "domain.xml" in the domain's config directory you should also change
+  the http-listener ports to 2222 for the unsecured one (port 8080) and to
+  2223 for the secured one (port 8181) to have the same settings as the
+  JPlag server.
+  Additionally you NEED to add the following line inside the 2223-http-listener:
+  
+     <ssl cert-nickname="jplag-server" client-auth-enabled="false"
+     	  ssl2-enabled="false" ssl3-enabled="true" tls-enabled="true"
+     	  tls-rollback-enabled="true"/>
+  
+  There might be more to do because of the JplagBean stuff, but this application
+  server is currently not supported anymore, although it shouldn't be a problem
+  to get it to work ;)
+
+
+================================================================================
+
+"Tomcat" install notes:
+
+You'll probably need Sun's Tomcat version "Tomcat 5.0 for WSDP 1.5" available at
+"http://java.sun.com/webservices/containers/", but other versions may work, too.
+
+Install Java Web Services Developer Pack 2.0 (JWSDP) onto Tomcat (will install
+both into Tomcat and into a separate folder) (This needs Java 1.5 and above!)
+(available at "http://java.sun.com/webservices/downloads/webservicespack.html"):
+  
+- Set the following environment variable before starting the server
+  (if you know a better way to define a home directory from outside the service
+   please let us know, as I think, this is a quite ugly solution...)
+
+CATALINA_OPTS="-Djplag_home=<path to jplag home directory> -server -Xmx1024M"
+
+  (-Xmx1024M is set to avoid "Out of memory" errors for larger submissions)
+  (The "-server" option is only needed for Java 1.4.2_06)
+
+- Due to class loader issues,
+  "jplagWebService/serverAccess/TrustSSLSocketFactor.class" and
+  "jplagWebService/serverAccess/TrustSSLSocketFactor$1.class" need to be copied
+  (including their directory structure) into the Tomcat's "common/classes"
+  folder.
+
+- Permission setup (if required!)
+
+  WARNING: This has NOT been tested yet, as my server doesn't start when I
+           use the "-security" option! You may need to change the codeBase!
+           
+  Add the following lines to the end of the catalina.policy file to
+  allow the JPlag web service to create, use and delete files in its home dir:
+
+--------------------------------------------------------------------------------
+
+// These permissions apply to the JPlag web service
+grant codeBase "file:${catalina.home}/webapps/JplagService/WEB-INF/-" {
+    permission java.io.FilePermission "${jplag_home}${/}-", "read,write,delete";
+};
+
+--------------------------------------------------------------------------------
+
+- SSL setup
+
+  If you don't have a keystore yet, you'll have to execute the following lines,
+  which can also be found in the bash script "generatecerts.sh":
+  
+    keytool -genkey -dname "CN=swt.ira.uka.de, OU=[IPD] JPlag Team, O=Uni Karlsruhe, L=Karlsruhe, S=Baden-Wuerttemberg, C=DE" -alias tomcat -keyalg RSA -keystore server.keystore -storepass gulpie! -keypass gulpie!
+    keytool -export -alias tomcat -keystore server.keystore -file tomcat.cer -storepass gulpie!
+    keytool -import -alias tomcat-server -keystore server.trust -trustcacerts -file tomcat.cer -storepass gulpie! -noprompt
+    
+  The "fore- and surname" must be the servers domain name (i.e. swt.ira.uka.de)!
+  You MUST use the same key password and keystore password!
+  
+  Add the following lines to the "server.xml" file inside Tomcat's "conf" folder
+
+    <Connector port="2223" disableUploadTimeout="true" acceptCount="100"
+    		   scheme="https" secure="true" clientAuth="false" sslProtocol="TLS"
+    		   keystoreFile="<full path to your server.keystore>"
+    		   keystorePass="<your keystore password>" />
+  
+  Of course you've got to replace the <...> stuff with the correct values.
+  
+  You'll also need the above mentioned script (which also deletes the old files)
+  to >>update the certificates<<, as those self-signed certificates only last 3
+  months. After executing that script you'll need to restart the server.
+  
+  Well, with the new configuration you additionally need to import apache's
+  certificate found here (change year as needed):
+    "/etc/apache2/ssl.crt/www-07.crt"
+  So the following additional line is required:
+
+    keytool -import -alias apache-server -keystore server.trust -trustcacerts -file /etc/apache2/ssl.crt/www-07.crt -storepass gulpie! -noprompt
+    
+  May be the "tomcat-server" import is not needed anymore...
+
+--------------------------------------------------------------------------------
+
+- Updating the www certificate
+  (when the register page throws RemoteExceptions with SSLHandshakeException)
+
+  This happens, when the www server gets a new SSL certificate.
+  In this case execute the following commands to introduce the new certificate
+  to our Tomcat server (replacing www-07.crt with the new filename):
+  
+    In ~jplag/jplag_webservice/tomcat50-jwsdp:
+      keytool -delete -alias apache-server -keystore server.trust -storepass gulpie! -noprompt
+      keytool -import -alias apache-server -keystore server.trust -trustcacerts -file /etc/apache2/ssl.crt/www-07.crt -storepass gulpie! -noprompt
+
+    In ~jplag/jplag_webservice:
+      ./stopserver.sh
+      ./startserver.sh

+ 67 - 0
jplag.webService/README

@@ -0,0 +1,67 @@
+<html>
+  <head>
+    <title>JPLAG INFO</title>
+    <meta content="">
+    <style></style>
+  </head>
+  <body>
+  <H>
+  This File Contains Information about Jplag  WEb Service : Please when you
+   adds some interressant or important code  in the repository ,just notify
+    how to build it at the top of this file ,please do not forget to add  the DATE!!! 
+    
+      before doing anything  , please let  export the JAVA_HOME location on
+       your environement    variables and add the location of  ant/bin  to
+        your environement Path.
+  </H>
+  <pre>
+  
+ 
+  
+   <div style="color:blue;">
+   
+ Date:25-02-05:........</div>
+ build.xml is now able to deploy JplagService on a web container
+  (Sun java System Application server oder tomcat)
+  
+  ..before you deploy JplagService ,please start you server .
+  and set build properties in build.propreties
+  
+  then run ant deploy-war
+   
+   
+   
+   
+  
+<div style="color:blue;"> 
+Date:24-02-05:</div>
+  
+  Web service Description and configuration files  are now available. please
+  use the build.xml Ant file to build the WSDL file.
+  
+  before doing it , please let set up   the properties in the file
+   build.properties band export the JAVA_HOME location on your environement 
+  <h3> variables and add the location of  ant/bin  to your environement Path.</h3>
+  
+   
+  Important: Only the Targets :"prepare" and "generate-server" are up to run .
+  to use another Targets , you must edit it . 
+  
+  config-server.xml is used by the wscompile tool to generate the server
+  If you want to know wich options are used, please look in  build.xml the
+   target "generate-server"  and the task "wscompile".
+  
+  in the directory util,you can write all java codes that will be usefull the 
+  run the service .
+  The wscompile will also generate the directory server/jplag in  util.
+ <H3> Just the service implementation have to be edited in the directory server/jplag </H3> 
+  
+  Thank You Emeric .
+  
+  
+  
+    
+   
+  </pre>
+  </body>
+</html>

+ 14 - 0
jplag.webService/etc/config-server.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
+    <wsdl xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config" 
+    	  packageName="jplagWebService.server" 
+    	  location="./src/main/webapp/WEB-INF/JPlagService.wsdl">
+    	<handlerChains xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
+            <chain xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config"
+            	   runAt="server">
+                <handler xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config"
+                		 className="jplagWebService.serverAccess.JPlagServerAccessHandler" />
+            </chain>
+        </handlerChains>
+    </wsdl>
+</configuration>

+ 24 - 0
jplag.webService/etc/index.html

@@ -0,0 +1,24 @@
+<html>
+<head>
+<title>Simple JPlag web service page</title>
+</head>
+<body>
+<center>Welcome to our JPlag web service!</center><br><br>
+This is just a totally basic web page to make it easier to access the Web Start client:<br><br>
+<center><a href="jplagClient.jnlp">Sample Web Start client for JPlag web service</a></center>
+<br><br>
+There's a big load of libraries, which will be downloaded together with the client
+(7.3 MB). I only uploaded a minimum set of libraries, but perhaps there are
+smaller ones available for this perpose.<br>
+The client will ask for full access to your computer, as the original version did,
+because it zips up a directory (I didn't check whether there are better security
+settings for that than there were when the original version was developed...).<br>
+If you submit files, the client seems to hang, because the zipping, sending and
+waiting for a response are stupidly executed in Swing's interaction thread. This
+will be subject of change ;)<br>
+Also there may be problems when sending bigger submissions, when the SOAP messages
+become too big. This hasn't been correctly tested yet!<br><br>
+But we still hope, that you can enjoy playing around with this application ;D<br><br>
+PS: The "Advanced options" button is a fake currently...
+</body>
+</html>

+ 14 - 0
jplag.webService/generatecerts.sh

@@ -0,0 +1,14 @@
+#!/bin/bash
+#
+# Creates a new self-signed certificate and imports it into a new truststore
+# It also imports the www servers certificate to allow communication with it
+#
+
+cd ~/jplag_webservice/tomcat50-jwsdp
+rm server.keystore
+rm tomcat.cer
+rm server.trust
+keytool -genkey -dname "CN=www.ipd.uni-karlsruhe.de, OU=[IPD] JPlag Team, O=Uni Karlsruhe, L=Karlsruhe, S=Baden-Wuerttemberg, C=DE" -alias tomcat -keyalg RSA -keystore server.keystore -storepass gulpie! -keypass gulpie!
+keytool -export -alias tomcat -keystore server.keystore -file tomcat.cer -storepass gulpie!
+keytool -import -alias tomcat-server -keystore server.trust -trustcacerts -file tomcat.cer -storepass gulpie! -noprompt
+keytool -import -alias apache-server -keystore server.trust -trustcacerts -file /etc/apache2/ssl.crt/www-06.crt -storepass gulpie! -noprompt

BIN
jplag.webService/jplag.key


+ 6 - 0
jplag.webService/jplag_users.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<jplag-users>
+	<user username="mo" password="om" />
+	<user username="blubgulp" password="gulpie!" />
+	<user username="emeric" password="emeric" />
+</jplag-users>

+ 218 - 0
jplag.webService/pom.xml

@@ -0,0 +1,218 @@
+<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">
+	<modelVersion>4.0.0</modelVersion>
+	<artifactId>webService</artifactId>
+	<version>0.3-SNAPSHOT</version>
+
+	<parent>
+		<groupId>edu.kit.ipd.jplag</groupId>
+		<artifactId>parent</artifactId>
+		<version>0.3-SNAPSHOT</version>
+	</parent>
+
+	<repositories>
+		<repository>
+			<id>ipdNexus</id>
+			<name>Recaa Repository</name>
+			<url>http://bob.ipd.kit.edu/nexus/content/repositories/public/</url>
+		</repository>
+	</repositories>
+
+	<build>
+		<plugins>
+		
+		<plugin>
+		<groupId>org.codehaus.mojo</groupId>
+		<artifactId>maven-war-plugin</artifactId>
+		<version>2.3</version>
+		<configuration>
+		
+					<encoding>${project.build.sourceEncoding}</encoding>
+					<source>${project.build.source}</source>
+					<target>${project.build.target}</target>
+					<optimize>${project.build.optimize}</optimize>
+		</configuration>
+		</plugin>
+		
+			<plugin>
+				<groupId>org.codehaus.mojo</groupId>
+				<artifactId>build-helper-maven-plugin</artifactId>
+				<version>1.7</version>
+				<executions>
+					<execution>
+						<id>add-wscompile-source</id>
+						<phase>generate-sources</phase>
+						<goals>
+							<goal>add-source</goal>
+						</goals>
+						<configuration>
+							<sources>
+								<source>target/generated-sources/wscompile</source>
+							</sources>
+						</configuration>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<artifactId>maven-antrun-plugin</artifactId>
+				<version>1.7</version>
+				<executions>
+					<execution>
+						<id>wsCompile</id>
+						<phase>generate-sources</phase>
+						<configuration>
+							<target>
+								<mkdir dir="${basedir}/target" />
+								<!-- <javac destdir="${basedir}/target" srcdir="${basedir}/src/main/java/" 
+									includes="jplagWsClient/util/JPlagClientAccessHandler.java" debug="true" 
+									fork="true"> <classpath> <path refid="maven.compile.classpath" /> </classpath> 
+									</javac> Deactivated by lama: I don't understand what this is used for - 
+									I also removed the class and the ant-dependency <taskdef name="wscompile" 
+									classname="jplagWsClient.util.wscompileSSLLauncher"> <wscompile base="${basedir}/target" 
+									features="wsi" config="${basedir}/config-client.xml" xPrintStackTrace="true" 
+									keep="true" sourcebase="${basedir}/target/generated-sources/wscompile" client="true" 
+									verbose="true" fork="true"> <classpath> <path refid="maven.compile.classpath" 
+									/> <pathelement path="${basedir}/target"/> <pathelement location="${java.home}/../lib/tools.jar" 
+									/> </classpath> </wscompile> -->
+
+								<taskdef name="wscompile" classname="com.sun.xml.rpc.tools.ant.Wscompile">
+									<classpath>
+										<path refid="maven.compile.classpath" />
+									</classpath>
+								</taskdef>
+
+								<mkdir dir="${basedir}/target/generated-sources/wscompile" />
+								<wscompile base="${basedir}/target" features="wsi"
+									config="${basedir}/etc/config-server.xml" xPrintStackTrace="true"
+									model="${basedir}/src/main/webapp/WEB-INF/model-wsdl-doclit.xml.gz"
+									keep="true" sourcebase="${basedir}/target/generated-sources/wscompile"
+									client="true" verbose="true" fork="true">
+									<classpath>
+										<path refid="maven.compile.classpath" />
+										<pathelement path="${basedir}/target" />
+										<pathelement location="${java.home}/../lib/tools.jar" />
+									</classpath>
+								</wscompile>
+
+								<!-- <wscompile base="${basedir}/target" features="wsi" config="${basedir}/client/etc/config-client.xml" 
+									xPrintStackTrace="true" keep="true" sourcebase="${basedir}/target/generated-sources/wscompile" 
+									client="true" verbose="true" fork="true"> <classpath> <path refid="maven.compile.classpath" 
+									/> <pathelement path="${basedir}/target" /> <pathelement location="${java.home}/../lib/tools.jar" 
+									/> </classpath> </wscompile> -->
+
+							</target>
+						</configuration>
+						<goals>
+							<goal>run</goal>
+						</goals>
+					</execution>
+
+					<execution>
+						<id>wsDeploy</id>
+						<phase>package</phase>
+						<configuration>
+							<target>
+								<taskdef name="wsdeploy" classname="com.sun.xml.rpc.tools.ant.Wsdeploy">
+									<classpath>
+										<path refid="maven.compile.classpath" />
+										<pathelement path="${basedir}/target" />
+										<pathelement location="${java.home}/../lib/tools.jar" />
+									</classpath>
+								</taskdef>
+
+								<wsdeploy
+									inWarFile="${basedir}/target/${project.artifactId}-${project.version}.war"
+									keep="false" outWarFile="${basedir}/target/JPlagService.war"
+									verbose="true" fork="true">
+									<classpath>
+										<path refid="maven.compile.classpath" />
+										<pathelement path="${basedir}/target" />
+										<pathelement location="${java.home}/../lib/tools.jar" />
+									</classpath>
+								</wsdeploy>
+
+							</target>
+						</configuration>
+						<goals>
+							<goal>run</goal>
+						</goals>
+					</execution>
+
+				</executions>
+			</plugin>
+
+<!-- Das funktioniert bei mir leider nicht - man muss manuell deployen :( -->
+<!-- 
+			<plugin>
+				<groupId>org.apache.tomcat.maven</groupId>
+				<artifactId>tomcat7-maven-plugin</artifactId>
+				<version>2.1</version>
+				<configuration>
+					<url>http://localhost:2606/manager</url>
+					<server>mytomcat</server>
+					<path>/JplagService</path>
+				</configuration>
+			</plugin>
+ -->
+
+		</plugins>
+		<pluginManagement>
+			<plugins>
+				<!--This plugin's configuration is used to store Eclipse m2e settings 
+					only. It has no influence on the Maven build itself. -->
+				<plugin>
+					<groupId>org.eclipse.m2e</groupId>
+					<artifactId>lifecycle-mapping</artifactId>
+					<version>1.0.0</version>
+					<configuration>
+						<lifecycleMappingMetadata>
+							<pluginExecutions>
+								<pluginExecution>
+									<pluginExecutionFilter>
+										<groupId>
+											org.apache.maven.plugins
+										</groupId>
+										<artifactId>
+											maven-antrun-plugin
+										</artifactId>
+										<versionRange>
+											[1.7,)
+										</versionRange>
+										<goals>
+											<goal>run</goal>
+										</goals>
+									</pluginExecutionFilter>
+									<action>
+										<ignore></ignore>
+									</action>
+								</pluginExecution>
+							</pluginExecutions>
+						</lifecycleMappingMetadata>
+					</configuration>
+				</plugin>
+			</plugins>
+		</pluginManagement>
+	</build>
+
+	<dependencies>
+
+		<dependency>
+			<groupId>javax.mail</groupId>
+			<artifactId>mail</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>edu.kit.ipd.jplag</groupId>
+			<artifactId>jplag</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.sun.xml.rpc</groupId>
+			<artifactId>jaxrpc-impl</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>edu.kit.ipd.jplag</groupId>
+			<artifactId>utils</artifactId>
+		</dependency>
+	</dependencies>
+	<packaging>war</packaging>
+</project>

+ 703 - 0
jplag.webService/src/main/java/jplagWebService/serverAccess/AccessStructure.java

@@ -0,0 +1,703 @@
+package jplagWebService.serverAccess;
+
+/**
+ * Author Emeric Kwemou, Moritz Kroll
+ */
+
+//import java.util.Date;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.Vector;
+
+import javax.activation.DataHandler;
+import javax.activation.FileDataSource;
+import javax.activation.MimetypesFileTypeMap;
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeMultipart;
+
+import jplag.options.CommandLineOptions;
+import jplag.options.util.ZipUtil;
+import jplagWebService.server.JPlagException;
+import jplagWebService.server.Status;
+import jplagWebService.serverImpl.JPlagCentral;
+import jplagWebService.serverImpl.JPlagTypImpl;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class AccessStructure {
+	public static final int MAX_UNZIPPED_SIZE = 60 * 1024 * 1024;
+    
+    /*
+     * Directory settings
+     */
+
+    private static String JPLAG_DIRECTORY = null;
+    private static String JPLAG_ENTRIES_DIRECTORY = null;
+    private static String JPLAG_RESULTS_DIRECTORY = null;
+
+    /*
+     * Some defaults
+     */
+
+    private static final int DEFAULT_STORABLE = 20;
+    private static final int MAX_STORABLE = 50;
+    private static final int MIN_STORABLE = 10;
+
+    private static final String PARSERLOG = "parser-log.txt";
+
+    /*
+     * Class attributes
+     */
+
+    private String title = null;
+    private String username = null;
+
+    private String submissionID = "Not initialised!";
+
+    private long date = 0;
+
+    private String commandLineInString = "";
+
+    private long lastStatusRequest = 0; // System.currentTimeMillis();
+
+    private jplag.options.CommandLineOptions options = null;
+
+    private StatusDecorator dec;
+
+    /**
+     * Constructs a new AccessStructure object for an Option
+     */
+    public AccessStructure(String user,
+            jplagWebService.server.Option usr_option) throws JPlagException {
+        dec = new StatusDecorator(new Status());
+        submissionID = JPlagCentral.getNextSubmissionID();
+        commandLineInString = generateCMD(usr_option);
+        username = user;
+        title = usr_option.getTitle();
+        date = System.currentTimeMillis();
+
+        generateStructure(usr_option, commandLineInString);
+    }
+
+    /**
+     * Constructs a new AccessStructure object for an Option and a MimeMultipart
+     */
+    public AccessStructure(String user, jplagWebService.server.Option usr_option,
+            MimeMultipart inputZipFile) throws JPlagException {
+        this(user, usr_option);
+        saveZipFile(inputZipFile);
+    }
+
+    /**
+     * Protected constructor to be used to recreate an AccessStructure out of an
+     * XML tag
+     */
+    protected AccessStructure(String submissionid, CommandLineOptions copts,
+            String username, String title, long date, int laststate,
+            String report) {
+        dec = new StatusDecorator(new Status(laststate, 0, report));
+        this.username = username;
+        this.title = title;
+        this.submissionID = submissionid;
+        this.date = date;
+        options = copts;
+    }
+
+    /**
+     * Ensure that temporary directories for entries and results exist
+     */
+    public static void ensureExistence() {
+        JPLAG_DIRECTORY = System.getProperty("jplag_home");
+        if (JPLAG_DIRECTORY == null) {
+            System.out.println("jplag_home property not set!!");
+            return;
+        }
+        System.out.println("jplag_home = " + JPLAG_DIRECTORY);
+        JPLAG_ENTRIES_DIRECTORY = JPLAG_DIRECTORY + File.separator + "entries";
+        JPLAG_RESULTS_DIRECTORY = JPLAG_DIRECTORY + File.separator + "results";
+        File f = new File(JPLAG_ENTRIES_DIRECTORY);
+        f.mkdir();
+        f = new File(JPLAG_RESULTS_DIRECTORY);
+        f.mkdir();
+    }
+
+    /**
+     * @return Command line options to be displayed in the result file
+     */
+    private static String generateCMD(jplagWebService.server.Option opt) {
+        String result = "path_to_files=" + opt.getPathToFiles()
+                + " min_token_length=" + opt.getMinimumMatchLength()
+                + " basecode_dir=" + opt.getBasecodeDir()
+                + " read_subdirs=" + opt.isReadSubdirs()
+                + " clustertype=" + opt.getClustertype()
+                + " store_matches=" + opt.getStoreMatches()
+                + " suffixes=";
+        for(int i=0; i<opt.getSuffixes().length; i++)
+        {
+            result += opt.getSuffixes()[i]
+                + ((i==opt.getSuffixes().length-1) ? "" : ",");
+        }
+        result += " language=" + opt.getLanguage() + " title=" + opt.getTitle();
+
+        return result;
+    }
+	
+	/**
+	 * @return True, if the given string is not a null or empty string.
+	 * 		   False, otherwise
+	 */
+	private static boolean isSet(String str) {
+		return str!=null && str.length()!=0;
+	}
+
+    /**
+     * This method is used to translate a jplag.server.Option object to a
+     * jplag.options.CommandLineOptions object
+     * 
+     * @param usr_opt
+     * @param argsInString
+     * @throws JPlagException, if the options are not valid
+     */
+    private void generateStructure(jplagWebService.server.Option usr_opt,
+            String argsInString) throws JPlagException {
+
+        Vector<String> vec = new Vector<String>();
+        vec.add("JPlag");
+
+        // Verbose parser and verbose quiet (don't fill server logs)
+		
+		vec.add("-vp");
+        vec.add("-vq");
+		
+        if (usr_opt.isReadSubdirs())
+            vec.add("-s");
+
+        if (isSet(usr_opt.getPathToFiles())) {
+            // Search with pattern <rootdir>/*/<pathToFiles>
+            vec.add("-S");
+            vec.add(usr_opt.getPathToFiles());
+        }
+
+        vec.add("-m");
+        String tmpmatch = usr_opt.getStoreMatches();
+        if (!isSet(tmpmatch)) {
+            vec.add(DEFAULT_STORABLE + "");
+        } else {
+            int index = tmpmatch.indexOf("%");
+            try {
+                if (index != -1) {
+                    tmpmatch = tmpmatch.substring(0, index);
+                    int percent = Integer.parseInt(tmpmatch);
+                    if (percent > 100) {
+                        throw new JPlagException("optionsException",
+                                "Illegal store matches option specified!",
+                                "There can't be a similarity bigger than 100%!");
+                    }
+                    tmpmatch += "%";
+                } else {
+                    int nummatches = Integer.parseInt(tmpmatch);
+                    if (nummatches > MAX_STORABLE)
+                        nummatches = MAX_STORABLE;
+                    if (nummatches < MIN_STORABLE)
+                        nummatches = MIN_STORABLE;
+                    tmpmatch = nummatches + "";
+                }
+            }
+			catch (NumberFormatException ex) {
+                throw new JPlagException("optionsException",
+                        "Illegal store matches option specified!",
+                        "Please check the usage function for correct "
+                                + "store_matches format");
+            }
+            vec.add(tmpmatch);
+        }
+        
+        // Comparison mode
+        vec.add("-compmode");
+        if(usr_opt.getComparisonMode() == null)
+            vec.add(jplag.options.Options.COMPMODE_NORMAL + "");
+        else
+            vec.add(usr_opt.getComparisonMode().toString());
+
+		// Language the output should "speak"
+
+        vec.add("-clang");
+        vec.add(usr_opt.getCountryLang());
+
+        // Suffixes
+
+        if (usr_opt.getSuffixes().length != 0) {
+            vec.add("-p");
+            
+            String str = "";
+            String[] suffixes = usr_opt.getSuffixes();
+            for(int i=0; i<suffixes.length; i++) {
+                str += suffixes[i];
+                if(i != suffixes.length-1) str += ",";
+            }
+
+            vec.add(str);
+        }
+
+        // Use base code directory?
+
+        if (isSet(usr_opt.getBasecodeDir())) {
+            vec.add("-bc");
+            vec.add(usr_opt.getBasecodeDir());
+        }
+
+        // Clustering
+		
+        if (isSet(usr_opt.getClustertype())) {
+            vec.add("-clustertype");
+            vec.add(usr_opt.getClustertype());
+        }
+
+        // Language
+
+        String language = usr_opt.getLanguage();
+        int languagenum;
+
+		// Check whether this language name exists
+        for (languagenum = 0; languagenum < JPlagTypImpl.languageInfos.length; languagenum++) {
+			if (JPlagTypImpl.languageInfos[languagenum].getName().equals(language)) {
+				vec.add("-l");
+                vec.add(language);
+                break;
+            }
+        }
+        if (languagenum == JPlagTypImpl.languageInfos.length) {
+            System.out.println("Wrong language specified: " + language);
+            throw new JPlagException(
+					"optionsException",
+                    "Illegal language specified!",
+                    "Use getServerInfo to see which languages are supported " +
+                    "and how they are spelled!");
+        }
+
+        // Detecting sensitivity
+        // TODO: Should there be a minimal minimum match length being accepted??
+		
+		int mintoklen = usr_opt.getMinimumMatchLength();
+        if (mintoklen <= 0)
+            mintoklen = JPlagTypImpl.languageInfos[languagenum].getDefMinMatchLen();
+		
+        vec.add("-t");
+		vec.add(mintoklen + "");
+
+        // Result dir
+        String submissiondir = submissionID + username;
+
+        String result_dir = JPLAG_RESULTS_DIRECTORY + File.separator
+        					+ submissiondir;
+        vec.add("-r");
+        vec.add(result_dir);
+
+        // Output dir
+
+        vec.add("-o");
+        File file12s = new File(result_dir);
+        if (!(file12s.exists()))
+            file12s.mkdir();
+        String tmp11st = result_dir + File.separator + PARSERLOG;
+        vec.add(tmp11st);
+
+		// Original dir
+		
+		vec.add("-d");
+		vec.add(usr_opt.getOriginalDir());
+		
+		// Title
+		
+		vec.add("-title");
+		vec.add(usr_opt.getTitle());
+		
+        // Root_dir
+        
+        vec.add(JPLAG_ENTRIES_DIRECTORY + File.separator + submissiondir);
+        
+        /**
+         * All command line options have now been set and a first option
+         * validation has been done
+         */
+        try {
+            String[] args = new String[vec.size()];
+            for (int i = 0; i < args.length; i++)
+                args[i] = vec.elementAt(i);
+
+            options = new jplag.options.CommandLineOptions(args, argsInString);
+            getDecorator().add(options.getState(), options.getProgress(), "");
+        }
+		catch (jplag.ExitException e) {
+            throw new JPlagException("invalidOptionsException", e.getReport(),
+                "Check your options");
+        }
+    }
+
+    /**
+     * @return The path (including the filename) to the result zip file
+     */
+    public String getEntryPath() {
+        return JPLAG_ENTRIES_DIRECTORY + File.separator + getSubmissionID()
+                + getUsername() + ".zip";
+    }
+    
+    /**
+     * Saves the zip file inside the entries directory
+     */
+    public void saveZipFile(MimeMultipart inputZipFile) {
+        try {
+            MimeBodyPart bdp = (MimeBodyPart) inputZipFile.getBodyPart(0);
+            DataHandler dh = bdp.getDataHandler();
+            File part = new File(getEntryPath());
+            FileOutputStream fos = new FileOutputStream(part);
+            dh.writeTo(fos);
+            fos.close();
+            System.gc();
+        }
+        catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+    
+    /**
+     * Deletes a whole directory including subdirectories
+     * 
+     * XXX: Recursive routine! Beware stack overflows!?
+     */
+    public static void deleteDir(File dir) {
+        if (!dir.isDirectory()) {
+            System.out.println("Util::deleteDir(): " + dir
+                    + " is not a directory!");
+            return;
+        }
+
+        File[] files = dir.listFiles();
+        for (int i = 0; i < files.length; i++) {
+            if (files[i].isDirectory())
+                deleteDir(files[i]);
+            else
+                files[i].delete();
+        }
+        dir.delete();
+    }
+
+    /**
+     * Deletes the submission input zip file and the unpacked contents
+     * 
+     * @return True, if server allowed to delete the files
+     */
+    public boolean deleteEntryFiles() {
+        String tmp2 = getSubmissionID() + getUsername();
+        File zipfile = new File(JPLAG_ENTRIES_DIRECTORY + File.separator + tmp2
+                + ".zip");
+        File entrydir = new File(JPLAG_ENTRIES_DIRECTORY + File.separator
+                + tmp2);
+
+        try {
+            zipfile.delete();
+            if(entrydir.exists()) deleteDir(entrydir);
+        } catch (SecurityException ex) {
+            System.out.println("Not allowed to delete entry files!");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Deletes the submission result zip file
+     * 
+     * @return True, if server allowed to delete the files
+     */
+    public boolean deleteResultFiles() {
+        /*
+         * if (!resultAdmin.deleteResult(struct.getUsername(), struct
+         * .getSubmissionID())) return false;
+         */
+
+        String tmp2 = getSubmissionID() + getUsername();
+        File zipfile = new File(JPLAG_RESULTS_DIRECTORY + File.separator + tmp2
+                + ".zip");
+        File resultdir = new File(JPLAG_RESULTS_DIRECTORY + File.separator
+                + tmp2);
+
+        try {
+            zipfile.delete();
+            if (resultdir.exists())
+                deleteDir(resultdir);
+        } catch (SecurityException ex) {
+            System.out.println("Not allowed to delete zipped result file!");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Deletes all files from this submission
+     * 
+     * @return True, if server allowed to delete the files
+     */
+    public boolean delete() {
+        if (getState() < 300)
+            return deleteEntryFiles();
+        else
+            return deleteResultFiles();
+    }
+
+    /**
+     * @return A MimeMultipart object containing the zipped result files
+     */
+    public MimeMultipart getResult() {
+
+        File file = new File(JPLAG_RESULTS_DIRECTORY + File.separator
+                + submissionID + getUsername() + ".zip");
+
+        MimeMultipart mmp = new MimeMultipart();
+
+        FileDataSource fds1 = new FileDataSource(file);
+        MimetypesFileTypeMap mftp = new MimetypesFileTypeMap();
+        mftp.addMimeTypes("multipart/zip zip ZIP");
+        fds1.setFileTypeMap(mftp);
+
+        MimeBodyPart mbp = new MimeBodyPart();
+
+        try {
+            mbp.setDataHandler(new DataHandler(fds1));
+            mbp.setFileName(file.getName());
+
+            mmp.addBodyPart(mbp);
+        } catch (MessagingException me) {
+            me.printStackTrace();
+        }
+        return mmp;
+    }
+
+    /**
+     * @return The path (including the filename) to the result zip file
+     */
+    public String getResultPath() {
+		return JPLAG_RESULTS_DIRECTORY + File.separator + getSubmissionID() + getUsername() + ".zip";
+    }
+
+    /**
+     * Unzips the submission input file into a <submissionID><username>folder
+     * inside the entries directory
+     */
+    public void unzipEntry() throws jplag.ExitException {
+        String tmp2 = getSubmissionID() + getUsername();
+		File part = new File(JPLAG_ENTRIES_DIRECTORY + File.separator + tmp2 + ".zip");
+
+        int totalsize = ZipUtil.unzip(part, JPLAG_ENTRIES_DIRECTORY, tmp2);
+        if(totalsize > MAX_UNZIPPED_SIZE)
+            throw new jplag.ExitException("Submission too big! It may be "
+                + (MAX_UNZIPPED_SIZE / 1024) + " kB at maximum, but it is "
+                + (totalsize / 1024) + " kB!");
+        
+        File unzipped = new File(JPLAG_ENTRIES_DIRECTORY + File.separator + tmp2);
+
+        // Searching root dir
+        File[] files = unzipped.listFiles();
+
+		String root_dir = (files.length == 1) ? files[0].getPath() : unzipped.getPath();
+        files = null;
+        getOption().root_dir = root_dir;
+        System.gc();
+    }
+
+    public static String getJPLAG_DIRECTORY() {
+        return JPLAG_DIRECTORY;
+    }
+
+    public StatusDecorator getDecorator() {
+        return this.dec;
+    }
+
+    /**
+     * Simpler version of setLastStatusRequest(long lastStatusRequest) The
+     * parameter is always System.currentTimeMillis();
+     */
+    public void setLastStatusRequest() {
+        setLastStatusRequest(System.currentTimeMillis());
+    }
+
+    /**
+     * @param lastStatusRequest:
+     *            The last time user has performed a getStatus request. This
+     *            method is used to prohibit getStatus flooding
+     */
+    public synchronized void setLastStatusRequest(long lastStatusRequest) {
+        this.lastStatusRequest = lastStatusRequest;
+    }
+
+    /**
+     * @return Last time of status request
+     */
+    public long getLastStatusRequest() {
+        return lastStatusRequest;
+    }
+
+    public int getState() {
+        return dec.getState();
+    }
+
+    public jplag.options.CommandLineOptions getOption() {
+        return this.options;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public String getSubmissionID() {
+        return submissionID;
+    }
+
+    public void setSubmissionID(String subid) {
+        submissionID = subid;
+    }
+
+    public long getDate() {
+        return date;
+    }
+
+    public void setDate(long date) {
+        this.date = date;
+    }
+
+    public String getCMDInString() {
+        return this.commandLineInString;
+    }
+
+    /**
+     * @param doc
+     *            The document for which the new element is to be created
+     * @return An "entry" element to be stored in the user submission database
+     */
+    public Element toXMLEntryElement(Document doc) {
+        Element entry = doc.createElement("entry");
+
+        entry.setAttribute("id", getSubmissionID());
+        entry.setAttribute("username", getUsername());
+        entry.setAttribute("title", getTitle());
+        entry.setAttribute("date", Long.toString(getDate()));
+        entry.setAttribute("commandLine", getCMDInString());
+
+        jplag.options.CommandLineOptions copt = getOption();
+        String[] args = copt.getArgs();
+
+        entry.setAttribute("numargs", args.length + "");
+        for (int i = 0; i < args.length; i++) {
+            entry.setAttribute("arg_" + i, args[i]);
+        }
+
+        return entry;
+    }
+
+    /**
+     * @param doc
+     *            The document for which the new element is to be created
+     * @return A "result" element to be stored in the user submission database
+     */
+    public Element toXMLResultElement(Document doc) {
+        Element submission = doc.createElement("result");
+
+        submission.setAttribute("id", getSubmissionID());
+        submission.setAttribute("title", getTitle());
+        submission.setAttribute("date", Long.toString(date));
+        submission.setAttribute("laststate", getState() + "");
+        if(dec.getReport().length()>0)
+        	submission.setAttribute("report", dec.getReport());
+
+        return submission;
+    }
+
+    /**
+     * @param entry
+     *            Entry element to be parsed
+     * @return AccessStructure object being usable in JPlagCentral.run()
+     */
+    public static AccessStructure fromXMLEntryElement(Element entry) {
+        String subID = entry.getAttribute("id");
+        String username = entry.getAttribute("username");
+        String title = entry.getAttribute("title");
+        long date;
+        try {
+            date = Long.parseLong(entry.getAttribute("date"));
+        } catch (NumberFormatException ex) {
+            System.out.println("jplag.server.serverAccess.AccessStructure:"
+                    + " NumberFormatException during date=parseInt("
+                    + entry.getAttribute("date") + ")");
+            date = 0;
+        }
+        int numargs;
+        try {
+            numargs = Integer.parseInt(entry.getAttribute("numargs"));
+        } catch (NumberFormatException ex) {
+            System.out.println("jplag.server.serverAccess.AccessStructure:"
+                    + " NumberFormatException during numargs=parseInt("
+                    + entry.getAttribute("numargs") + ")");
+            numargs = 0;
+        }
+        String[] args = new String[numargs];
+        String commandLine = entry.getAttribute("commandLine");
+        for (int i = 0; i < args.length; i++) {
+            args[i] = entry.getAttribute("arg_" + i);
+        }
+        jplag.options.CommandLineOptions copt;
+        try {
+            copt = new jplag.options.CommandLineOptions(args, commandLine);
+            //Added Emeric Kwemou 22-03-05. I just conserve the commandline in
+            // a string
+        } catch (jplag.ExitException ex) {
+            System.out.println("ExitException caught in getNextEntry()");
+            ex.printStackTrace();
+            return null;
+        }
+
+        return new AccessStructure(subID, copt, username, title, date,
+            StatusDecorator.WAITING_IN_QUEUE, "");
+    }
+
+    /**
+     * @param submission
+     *            Result element to be parsed
+     * @return AccessStructure object for informational use
+     */
+    public static AccessStructure fromXMLResultElement(Element submission) {
+        Element user = (Element) submission.getParentNode();
+
+        String subID = submission.getAttribute("id");
+        String username = user.getAttribute("username");
+        String title = submission.getAttribute("title");
+        long date;
+        try {
+            date = Long.parseLong(submission.getAttribute("date"));
+        } catch (NumberFormatException ex) {
+            System.out.println("jplag.server.serverAccess.AccessStructure:"
+                    + " NumberFormatException during date=parseInt("
+                    + submission.getAttribute("date") + ")");
+            date = 0;
+        }
+
+        int laststate;
+        try {
+            laststate = Integer.parseInt(submission.getAttribute("laststate"));
+        } catch (NumberFormatException ex) {
+            System.out.println("jplag.server.serverAccess.AccessStructure:"
+                    + " NumberFormatException during laststate=parseInt("
+                    + submission.getAttribute("laststate") + ")");
+            laststate = 499;
+        }
+        
+        String report = submission.getAttribute("report");
+
+        return new AccessStructure(subID, null, username, title, date,
+                laststate, report);
+    }
+}

+ 199 - 0
jplag.webService/src/main/java/jplagWebService/serverAccess/JPlagServerAccessHandler.java

@@ -0,0 +1,199 @@
+/*
+ * Created on 15.03.2005
+ */
+package jplagWebService.serverAccess;
+
+import java.util.Date;
+import java.util.Iterator;
+
+import javax.xml.namespace.QName;
+import javax.xml.rpc.handler.GenericHandler;
+import javax.xml.rpc.handler.HandlerInfo;
+import javax.xml.rpc.handler.MessageContext;
+import javax.xml.rpc.handler.soap.SOAPMessageContext;
+import javax.xml.soap.Detail;
+import javax.xml.soap.DetailEntry;
+import javax.xml.soap.SOAPBody;
+import javax.xml.soap.SOAPConstants;
+import javax.xml.soap.SOAPElement;
+import javax.xml.soap.SOAPEnvelope;
+import javax.xml.soap.SOAPException;
+import javax.xml.soap.SOAPFault;
+import javax.xml.soap.SOAPHeader;
+import javax.xml.soap.SOAPHeaderElement;
+import javax.xml.soap.SOAPMessage;
+
+import jplagWebService.serverImpl.JPlagCentral;
+
+/**
+ * @author Moritz Kroll
+ */
+public class JPlagServerAccessHandler extends GenericHandler {
+	private static final String JPLAG_WEBSERVICE_BASE_URL = "http://jplag.ipd.kit.edu/";
+	public static final int compatibilityLevel = 4;
+	protected HandlerInfo info = null;
+
+	public QName[] getHeaders() {
+		return info.getHeaders();
+	}
+
+	public void init(HandlerInfo arg) {
+		info = arg;
+	}
+
+	/**
+	 * Extracts the username out of the header of a SOAP message
+	 */
+	public static String extractUsername(SOAPMessageContext smsg) {
+		try {
+			SOAPHeader header = smsg.getMessage().getSOAPHeader();
+			if (header != null) {
+				@SuppressWarnings("unchecked")
+				Iterator<SOAPHeaderElement> headers = header.examineAllHeaderElements();
+				while (headers.hasNext()) {
+					SOAPHeaderElement he = headers.next();
+
+					if (he.getElementName().getLocalName().equals("Access")) {
+						@SuppressWarnings("unchecked")
+						Iterator<SOAPElement> elements = he.getChildElements();
+						while (elements.hasNext()) {
+							SOAPElement e = elements.next();
+							String name = e.getElementName().getLocalName();
+							if (name.equals("username"))
+								return e.getValue();
+						}
+					}
+				}
+			}
+		} catch (SOAPException x) {
+			x.printStackTrace();
+		}
+		return null;
+	}
+
+	/**
+	 * Manually builds up a JPlagException SOAP message and replaces the
+	 * original one with it
+	 */
+	public void replaceByJPlagException(SOAPMessageContext smsg, String desc, String rep) {
+		try {
+			SOAPMessage msg = smsg.getMessage();
+			SOAPEnvelope envelope = msg.getSOAPPart().getEnvelope();
+
+			/*
+			 * Remove old header andy body
+			 */
+
+			SOAPHeader oldheader = envelope.getHeader();
+			if (oldheader != null)
+				oldheader.detachNode();
+			SOAPBody oldbody = envelope.getBody();
+			if (oldbody != null)
+				oldbody.detachNode();
+
+			SOAPBody sb = envelope.addBody();
+			SOAPFault sf = sb.addFault(envelope.createName("Server", "env", SOAPConstants.URI_NS_SOAP_ENVELOPE),
+					"jplagWebService.server.JPlagException");
+			Detail detail = sf.addDetail();
+			DetailEntry de = detail.addDetailEntry(envelope.createName("JPlagException", "ns0", JPLAG_WEBSERVICE_BASE_URL + "types"));
+
+			SOAPElement e = de.addChildElement("exceptionType");
+			e.addTextNode("accessException");
+
+			e = de.addChildElement("description");
+			e.addTextNode(desc);
+
+			e = de.addChildElement("repair");
+			e.addTextNode(rep);
+		} catch (SOAPException x) {
+			x.printStackTrace();
+		}
+	}
+
+	/**
+	 * Checks whether the SOAP message contains a valid Access element (correct
+	 * username + password) and whether the account may still be used
+	 */
+	public boolean handleRequest(MessageContext context) {
+		String username = null;
+		String password = null;
+		int compatLevel = 0;
+		if (context instanceof SOAPMessageContext) {
+			SOAPMessageContext smsg = (SOAPMessageContext) context;
+
+			/*
+			 * Iterator iter = context.getPropertyNames();
+			 * System.out.println("Context properties:"); while(iter.hasNext())
+			 * { String propname = (String) iter.next();
+			 * System.out.println(propname + " = " +
+			 * context.getProperty(propname).toString()); }
+			 * 
+			 * HttpServletRequest request = (HttpServletRequest)
+			 * context.getProperty(
+			 * "com.sun.xml.rpc.server.http.HttpServletRequest");
+			 * System.out.println("Client IP: " + request.getRemoteAddr());
+			 */
+
+			try {
+				SOAPHeader header = smsg.getMessage().getSOAPHeader();
+				if (header != null) {
+					@SuppressWarnings("unchecked")
+					Iterator<SOAPHeaderElement> headers = header.examineAllHeaderElements();
+					while (headers.hasNext()) {
+						SOAPHeaderElement he = headers.next();
+
+						if (he.getElementName().getLocalName().equals("Access")) {
+							@SuppressWarnings("unchecked")
+							Iterator<SOAPElement> elements = he.getChildElements();
+							while (elements.hasNext()) {
+								SOAPElement e = elements.next();
+								String name = e.getElementName().getLocalName();
+								if (name.equals("username"))
+									username = e.getValue();
+								else if (name.equals("password"))
+									password = e.getValue();
+								else if (name.equals("compatLevel")) {
+									try {
+										compatLevel = Integer.parseInt(e.getValue());
+									} catch (NumberFormatException ex) {
+										compatLevel = -1;
+									}
+								}
+							}
+						}
+					}
+					if (compatLevel < compatibilityLevel) {
+						replaceByJPlagException(smsg, "Client outdated!", "Please update your client " + "to compatibility level "
+								+ compatibilityLevel + ".");
+						return false;
+					}
+					if (username != null && password != null) {
+						int state = JPlagCentral.getInstance().getUserAdmin().getLoginState(username, password);
+						if ((state & UserAdmin.MASK_EXPIRED) != 0) {
+							replaceByJPlagException(smsg, "Access denied!", "Your account has " + "expired! Please contact the JPlag "
+									+ "administrator to reactivate it!");
+							return false;
+						} else if ((state & UserAdmin.MASK_DEACTIVATED) != 0) {
+							replaceByJPlagException(smsg, "Access denied!", "Your account has " + "been deactivated! Please contact the "
+									+ "JPlag administrator to reactivate it!");
+							return false;
+						} else if (state != UserAdmin.USER_INVALID)
+							return true;
+					}
+				} else {
+					System.out.println("No header available!");
+					replaceByJPlagException(smsg, "Access denied!", "The SOAP message doesn't contain an access header!");
+					return false;
+				}
+			} catch (SOAPException x) {
+				x.printStackTrace();
+			}
+			System.out.println("[" + new Date() + "] Access denied for user \"" + username + "\"!");
+
+			replaceByJPlagException(smsg, "Access denied!", "Check your username and password!");
+			return false;
+		}
+		System.out.println("Not a SOAP message context!!!");
+		return false;
+	}
+}

+ 387 - 0
jplag.webService/src/main/java/jplagWebService/serverAccess/MailTemplateAdmin.java

@@ -0,0 +1,387 @@
+/*
+ * Created on 12.06.2005
+ * Author: Moritz Kroll
+ */
+
+package jplagWebService.serverAccess;
+
+import java.io.File;
+import java.io.FileInputStream;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import jplagWebService.server.JPlagException;
+import jplagWebService.server.MailTemplate;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+public class MailTemplateAdmin {
+	public static final int MAIL_ACCEPTED = 0;
+	public static final int MAIL_DECLINED = 1;
+	public static final int MAIL_SERVER = 2;
+    public static final int MAIL_OTHERS = 3;
+	
+	private static final String[] mailTypes={ "accepted", "declined",
+		"serverMail", "others" };
+	
+	public static final String SERVER_VERIFICATION = "verification";
+	public static final String SERVER_WARNEXPIRE = "warnExpire";
+    public static final String SERVER_ASKEXTEND = "askExtend";
+	public static final String SERVER_EXPIRED = "expired";
+	public static final String SERVER_REQUESTNOTIFY = "requestNotify";
+	
+    private Document doc = null;
+    private Element rootElement = null;
+	
+	private File mailTemplateFile;
+
+	public MailTemplateAdmin(String jplagHome) {
+		mailTemplateFile=new File(jplagHome + File.separator +
+				"mailTemplates.xml");
+        if(!mailTemplateFile.exists())
+            create();
+        else
+            parse();
+	}
+
+    /**
+     * Writes the mail templates data to an XML file
+     */
+    private synchronized void writeXMLFile() {
+        try {
+            // Prepare the DOM document for writing
+            Source source = new DOMSource(doc);
+
+            // Prepare the output file
+            Result result = new StreamResult(mailTemplateFile);
+
+            // Write the DOM document to the file
+            Transformer xformer = TransformerFactory.newInstance()
+                    .newTransformer();
+            xformer.transform(source, result);
+        } catch (TransformerConfigurationException e) {
+            e.printStackTrace();
+        } catch (TransformerException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Creates a new "jplag-user database" XML file
+     */
+    private void create() {
+        DocumentBuilderFactory docBFac;
+        DocumentBuilder docBuild;
+        try {
+            docBFac = DocumentBuilderFactory.newInstance();
+            docBuild = docBFac.newDocumentBuilder();
+            doc = docBuild.newDocument();
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            return;
+        }
+        rootElement = doc.createElement("mailTemplates");
+        doc.appendChild(rootElement);
+		
+		// Add Standard messages
+		
+		Element newelem = doc.createElement("accepted");
+		newelem.setAttribute("name","Standard");
+		newelem.setAttribute("subject","[JPlag] Account request accepted");
+		newelem.setAttribute("data","Dear {realname},\n\nYour JPlag account " +
+				"request has been accepted.\nPlease visit the following " +
+				"URL to get access to the client application:\n\n" +
+                "Website: {server}\nUsername: {username}\n\n" +
+				"Best regards\nThe JPlag administration");
+		rootElement.appendChild(newelem);
+		
+		newelem = doc.createElement("accepted");
+		newelem.setAttribute("name","No email");
+		rootElement.appendChild(newelem);
+		
+		newelem = doc.createElement("declined");
+		newelem.setAttribute("name","Standard");
+		newelem.setAttribute("subject","[JPlag] Account request rejected");
+		newelem.setAttribute("data","Dear {realname},\n\nWe are sorry to tell" +
+				" you, that we decided not to give you a JPlag account" +
+				" because\n\n<ENTER REASON HERE>\n\nBest regards\nThe JPlag" +
+				" administration");
+		rootElement.appendChild(newelem);
+        
+        newelem = doc.createElement("declined");
+        newelem.setAttribute("name","No comparison to the Internet");
+        newelem.setAttribute("subject","[JPlag] Account request denied");
+        newelem.setAttribute("data","Dear {realname},\n\nIt is our impression" +
+                " that you want to compare your students work to the" +
+                " internet, but JPlag does not do that. For more information" +
+                " about what JPlag is able to do, please refer to our" +
+                " website.\nIf this assumption is wrong, please apply again" +
+                " so that we can give you an account.\n\n" +
+                "Best regards\nThe JPlag administration");
+        rootElement.appendChild(newelem);
+        
+        newelem = doc.createElement("declined");
+        newelem.setAttribute("name","No teacher");
+        newelem.setAttribute("subject","[JPlag] Account request denied");
+        newelem.setAttribute("data","Dear {realname},\n\n"
+            + "You need to be a teacher or lecturer to qualify for an account. "
+            + "If you are neither, you may still get an account for research "
+            + "reasons, but you have to give more details about the kind of "
+            + "research you are doing.\n\n"
+            + "Best regards\nThe JPlag administration");
+
+        newelem = doc.createElement("declined");
+        newelem.setAttribute("name","Illegal Email address");
+        newelem.setAttribute("subject","[JPlag] Account request denied");
+        newelem.setAttribute("data","Dear {realname},\n\n" +
+            "We are sorry to tell you that we can not give you a JPlag " +
+            "account, since you registered with an anonymous email address. " +
+            "You may re-apply with your official school/university email " +
+            "address.\n\n" +
+            "Best regards\nThe JPlag administration");
+
+        newelem = doc.createElement("declined");
+        newelem.setAttribute("name","No individual account");
+        newelem.setAttribute("subject","[JPlag] Account request denied");
+        newelem.setAttribute("data","Dear {realname},\n\n" +
+            "We are sorry to tell you that we can not give you a JPlag " +
+            "account, since you did not apply as an individual. (We do not " +
+            "give accounts to groups.)\nPlease re-apply with your personal " +
+            "data.\n\n" +
+            "Best regards\nThe JPlag administration");
+
+        newelem = doc.createElement("declined");
+        newelem.setAttribute("name","Duplication");
+        newelem.setAttribute("subject","[JPlag] Account request denied");
+        newelem.setAttribute("data","Dear {realname},\n\n" +
+            "We denied your account, since you plan to use JPlag to find " +
+            "code duplication; something which JPlag can not do.\n" +
+            "It is made to compare different programs to each other. While " +
+            "this seems to be similar to find duplicates, it is something " +
+            "different from the technical point of view.\n\n" +
+            "Best regards\nThe JPlag administration");
+
+        newelem = doc.createElement("declined");
+        newelem.setAttribute("name","English");
+        newelem.setAttribute("subject","[JPlag] Account request denied");
+        newelem.setAttribute("data","Dear {realname},\n\n" +
+            "Although we have localized versions of the JPlag program itself," +
+            " you still have to apply in English or German, since these are" +
+            " the only languages we understand.\n\n" +
+            "Best regards\nThe JPlag administration");
+
+        newelem = doc.createElement("declined");
+        newelem.setAttribute("name","Full name");
+        newelem.setAttribute("subject","[JPlag] Account request denied");
+        newelem.setAttribute("data","Dear {realname},\n\n" +
+            "Your account was denied, since you did not fill in the form on " +
+            "the Website properly.\nPlease apply with your full name and " +
+            "correct email address of your school/university.\n\n" +
+            "Best regards\nThe JPlag administration");
+        
+		newelem = doc.createElement("declined");
+		newelem.setAttribute("name","No email");
+		rootElement.appendChild(newelem);
+		
+		newelem = doc.createElement("serverMail");
+		newelem.setAttribute("name","verification");
+		newelem.setAttribute("subject","[JPlag] Email address verification");
+		newelem.setAttribute("data","Dear {realname},\n\nThis mail is sent " +
+				"to you in order to verificate your email address. Please " +
+                "visit the following URL to activate your request:\n\n" +
+				"{server}/indexActivate.jsp?code={code}" +
+				"\n\nAfter the activation the request will be sent to the " +
+				"administrator who will then decide on your application.\n\n" +
+				"Best regards\nThe JPlag administration");
+		rootElement.appendChild(newelem);
+		
+		newelem = doc.createElement("serverMail");
+		newelem.setAttribute("name","warnExpire");
+		newelem.setAttribute("subject","[JPlag] Your account expires within two weeks!");
+		newelem.setAttribute("data","Dear {realname},\n\n" +
+				"Your JPlag account will expire on {expires} (DD.MM.YYYY).\n" +
+				"Please contact us, if you want to extend your " +
+				"account time.\n\nBest regards\nThe JPlag administration");
+		rootElement.appendChild(newelem);
+
+        newelem = doc.createElement("serverMail");
+        newelem.setAttribute("name","askExtend");
+        newelem.setAttribute("subject","[JPlag] Your account expires within two weeks!");
+        newelem.setAttribute("data","Dear {realname},\n\n"
+                + "Your JPlag account will expire on {expires} (DD.MM.YYYY).\n"
+                + "If you want to extend your account time for a year, just "
+                + "visit the following URL:\n\n"
+                + "{server}/indexExtend.jsp?code={code}"
+                + "\n\nIf you do not need this account anymore, you can just "
+                + "ignore this mail."
+                + "\n\nBest regards\nThe JPlag administration");
+        rootElement.appendChild(newelem);
+
+		newelem = doc.createElement("serverMail");
+		newelem.setAttribute("name","expired");
+		newelem.setAttribute("subject","[JPlag] Your account has expired!");
+		newelem.setAttribute("data","Dear {realname},\n\n" +
+				"We are sorry to tell you that your JPlag account expired " +
+				"today.\nPlease contact us, if you want to reactivate your " +
+				"account.\n\nBest regards\nThe JPlag administration");
+		rootElement.appendChild(newelem);
+		
+		newelem = doc.createElement("serverMail");
+		newelem.setAttribute("name","requestNotify");
+		newelem.setAttribute("subject","[JPlag] New request notification");
+		newelem.setAttribute("data","Please decide on the following request " +
+				"in the AdminTool:\n\nUsername: {username}" +
+				"\nReal name: {realname}" +
+				"\nEmail: {email}\nAlternative email: {emailSecond}" +
+				"\nHomepage: {homepage}\nReason: {reason}" +
+				"\nAdditional notes: {notes}");
+		rootElement.appendChild(newelem);
+		
+        writeXMLFile();
+    }
+
+    /**
+     * Loads an existing "jplag-user database" into memory
+     */
+    private void parse() {
+        try {
+            FileInputStream xmlStream = new FileInputStream(mailTemplateFile);
+            DocumentBuilderFactory factory =
+                    DocumentBuilderFactory.newInstance();
+            factory.setIgnoringComments(true);
+            DocumentBuilder builder = factory.newDocumentBuilder();
+            doc = builder.parse(xmlStream);
+            xmlStream.close();
+            rootElement = doc.getDocumentElement();
+        }
+        catch (javax.xml.parsers.ParserConfigurationException e) {
+            // throw new RuntimeException("Failed to create DocumentBuilder");
+            e.printStackTrace();
+        } catch (org.xml.sax.SAXException e) {
+            // throw new RuntimeException("Error parsing users.xml");
+            e.printStackTrace();
+        } catch (Exception e) {
+            System.out.println("File error!");
+            e.printStackTrace();
+        }
+    }
+
+	/**
+	 * @return The mail template with the given name of the given type
+	 * 		   or null if it doesn't exist
+	 */
+	public synchronized MailTemplate getMailTemplate(int type, String name)
+	{
+		if(type<0 || type>=mailTypes.length) return null;
+		
+        NodeList list = rootElement.getElementsByTagName(mailTypes[type]);
+		for(int i=0;i<list.getLength();i++)
+		{
+			Element elem = (Element) list.item(i);
+			if(elem.getAttribute("name").equals(name))
+			{
+				return new MailTemplate(elem.getAttribute("name"),
+						elem.getAttribute("subject"),elem.getAttribute("data"));
+			}
+		}
+		return null;
+	}
+	
+	/**
+	 * @return An array of all mail templates of the given type
+	 */
+	public synchronized MailTemplate[] getMailTemplates(int type)
+			throws JPlagException
+	{
+		if(type<0 || type>=mailTypes.length)
+			throw new JPlagException("getMailTemplates", "Type index out of" +
+					" bounds", "Please check the type parameter!");
+		
+        NodeList list = rootElement.getElementsByTagName(mailTypes[type]);
+		MailTemplate[] templates = new MailTemplate[list.getLength()];
+		for(int i=0;i<list.getLength();i++)
+		{
+			Element elem = (Element) list.item(i);
+			templates[i] = new MailTemplate(elem.getAttribute("name"),
+					elem.getAttribute("subject"),elem.getAttribute("data"));
+		}
+		return templates;
+	}
+	
+	/**
+	 * Updates or adds the given mail template for the given type 
+	 */
+	public synchronized void setMailTemplate(int type, MailTemplate template)
+			throws JPlagException
+	{
+		if(type<0 || type>=mailTypes.length)
+			throw new JPlagException("setMailTemplate", "Type index out of" +
+					" bounds", "Please check the type parameter!");
+        NodeList list = rootElement.getElementsByTagName(mailTypes[type]);
+		for(int i=0;i<list.getLength();i++)
+		{
+			Element elem = (Element) list.item(i);
+			if(template.getName().equals(elem.getAttribute("name")))
+			{
+                if(template.getSubject().length()==0) {     // remove ?
+                    if(elem.getAttribute("subject").length()==0)
+                        throw new JPlagException("setMailTemplate",
+                            "You may not remove any \"No email\" entries!",
+                            "Tried to remove template with empty subject!");
+                    rootElement.removeChild(elem);
+                }
+                else if(template.getData().length()==0) {   // rename ?
+                    elem.setAttribute("name",template.getSubject());
+                }
+                else {                                      // change !
+                    elem.setAttribute("subject",template.getSubject());
+                    elem.setAttribute("data",template.getData());
+                }
+                writeXMLFile();
+				return;
+			}
+		}
+
+        if(template.getName().length()==0 || template.getSubject().length()==0
+                || template.getData().length()==0)
+            throw new JPlagException("setMailTemplate", "Name, subject or data"
+                + " is empty!", "Please check the parameters!");
+        
+		Element newelem = doc.createElement(mailTypes[type]);
+		newelem.setAttribute("name",template.getName());
+		newelem.setAttribute("subject",template.getSubject());
+		newelem.setAttribute("data",template.getData());
+		rootElement.appendChild(newelem);
+		writeXMLFile();
+	}
+	
+	/**
+	 * Replaces all keys of the form "{<keyname>}" in the template string
+	 * by the appropriate string given by the MailTemplateData object
+	 */
+	public String evalTemplateString(String template, MailTemplateData td) {
+		String result = "";
+		String[] tokens = template.split("[{}]");
+		// TODO: Check whether every '{' has a following '}'
+		result = tokens[0];
+		for(int i=1;i<tokens.length;i+=2)
+		{
+			String value = (String) td.get(tokens[i]);
+			if(value==null || value.length()==0) result += "[N/A]";
+			else result += td.get(tokens[i]);
+			if(i+1<tokens.length) result += tokens[i+1];
+		}
+		return result;
+	}
+}

+ 48 - 0
jplag.webService/src/main/java/jplagWebService/serverAccess/MailTemplateData.java

@@ -0,0 +1,48 @@
+package jplagWebService.serverAccess;
+
+import java.util.HashMap;
+
+import jplagWebService.server.RequestData;
+
+import org.w3c.dom.Element;
+
+public class MailTemplateData extends HashMap<String,String> {
+	private static final long serialVersionUID = 3908164446807386168L;
+
+	public MailTemplateData() {
+		super();
+		put("server",UserAdmin.JPLAG_SERVER);
+	}
+	
+	public MailTemplateData(RequestData rd) {
+		super();
+		put("username",rd.getUsername());
+		put("password",rd.getPassword());
+		put("realname",rd.getRealName());
+		put("email",rd.getEmail());
+		put("emailSecond",rd.getEmailSecond());
+		put("homepage",rd.getHomepage());
+		put("reason",rd.getReason());
+		put("notes",rd.getNotes());
+		put("validateTime",rd.getValidateTime());
+		
+		put("server",UserAdmin.JPLAG_SERVER);
+	}
+	
+	public MailTemplateData(Element elem) {
+		super();
+		put("username",elem.getAttribute("username"));
+		put("password",elem.getAttribute("password"));
+/*		data[j].setCreated(parseCalendar(elem,"created"));
+		data[j].setCreatedBy(elem.getAttribute("createdBy"));*/
+		put("expires",elem.getAttribute("expires"));
+		put("lastUsage",elem.getAttribute("lastUsage"));
+		put("numOfSubs",elem.getAttribute("numOfSubs"));
+		put("realname",elem.getAttribute("realname"));
+		put("email",elem.getAttribute("email"));
+		put("emailSecond",elem.getAttribute("emailSecond"));
+		put("homepage",elem.getAttribute("homepage"));
+		
+		put("server",UserAdmin.JPLAG_SERVER);
+	}
+}

+ 166 - 0
jplag.webService/src/main/java/jplagWebService/serverAccess/MemoryManager.java

@@ -0,0 +1,166 @@
+package jplagWebService.serverAccess;
+
+import java.util.Date;
+import java.util.Vector;
+
+import java.io.*;
+
+import jplagWebService.serverImpl.JPlagCentral;
+
+public class MemoryManager extends Thread {
+    private static final long ONEDAY = 86400000;
+    private static final long MAX_SUB_SIZE = 104857600; // 100MB
+    private static final long LOW_SUB_SIZE = 102400;    // 100kB
+    private static final long MIN_TIME_ON_SERVER = 1;   // in days
+    private static final long MAX_TIME_ON_SERVER = 7;   // in days
+    
+    /* 
+     * constants for linear interpolation between MINIMAL_TIME_ON_SERVER
+     * at MAX_SUB_SIZE and MAXIMAL_TIME_ON_SERVER at LOW_SUB_SIZE
+     */
+    
+    private static final float a = 
+        -(MAX_TIME_ON_SERVER-MIN_TIME_ON_SERVER) / (MAX_SUB_SIZE-LOW_SUB_SIZE);
+    private static final float b = MIN_TIME_ON_SERVER - a * MAX_SUB_SIZE;
+
+    private static MemoryManager instance = null;
+
+    private static long LAST_LOADING_TIME = 0;
+
+    /**
+     * used to stop memory manager.
+     */
+    private boolean manager_enabled = false;
+
+    /**
+     * All submissions at this time.
+     */
+    private Vector<AccessStructure> submissions = new Vector<AccessStructure>();
+    
+    /**
+     * Memory manager will react every SLEEP_TIME millisecond
+     */
+    private static final long SLEEP_TIME = 600000;// 10 min
+ 
+
+    private MemoryManager() {
+    }// cannot be instantiated out of this.
+
+    /**
+     * 
+     * @return unique instance of the memory manager.
+     */
+    public static MemoryManager giveInstance() {
+        if (instance == null)
+            instance = new MemoryManager();
+        return instance;
+
+    }
+
+    /**
+     * @return Whether the submission is to be erased or not
+     */
+    private static boolean isErasable(AccessStructure struct) {
+        // get size of struct
+        String result_loc = struct.getResultPath();
+        File res_file = new File(result_loc);
+        long size = Math.max(res_file.length(), 1);
+        
+        /**
+         * Check whether the file is bigger than the maximal one authorized
+         */
+        if(size>MAX_SUB_SIZE) return true;
+        
+        float time_diff = (System.currentTimeMillis()-struct.getDate())/ONEDAY;
+        
+        if (time_diff < MIN_TIME_ON_SERVER) return false;
+        else if (time_diff >= MAX_TIME_ON_SERVER) return true;
+        else if (time_diff >= a * size + b) return true;
+        else return false;
+    }
+
+    /**
+     * Mechanism to delete a submission from the Terminated Queue.
+     * 
+     * @param struct
+     *            submission to be deleted
+     */
+    private static void manageDeletion(AccessStructure struct) {
+        JPlagCentral.cancelSubmission(struct);
+
+        System.out.println("[" + new Date() + "] MemoryManager: Submission \""
+                + struct.getTitle() + "\" from user \""
+                + struct.getUsername() + "\" with submissionID "
+                + struct.getSubmissionID() + " saved on "
+                + new Date(struct.getDate()) + " has been deleted");
+    }
+ 
+    /**
+     * Used to actualize the vector containing all submissions
+     */
+    private void loadSubmissions() {
+        submissions.clear();
+        
+        // Collect all usernames on the server
+        String[] users = JPlagCentral.usersList();
+        
+        // Collect all submissions for each user
+        for (int i = 0; i < users.length; i++) {
+        	AccessStructure[] subm = JPlagCentral.listAccessStructures(users[i]);
+            if (subm != null && subm.length != 0) {
+                for (int j = 0; j < subm.length; j++) {
+                    submissions.add(subm[j]);
+                }
+            }
+        }
+    }
+
+    /**
+     * MemoryManager main loop
+     */
+    public void run() {
+    	System.out.println("MemoryManager running...");
+    	try
+    	{
+    		while (manager_enabled) {
+    			if ((System.currentTimeMillis() - LAST_LOADING_TIME) / ONEDAY
+    					>= MIN_TIME_ON_SERVER)
+    			{
+    				loadSubmissions();
+    				LAST_LOADING_TIME = System.currentTimeMillis();
+    			}
+   				for (int i = submissions.size()-1; i>=0 && manager_enabled; i--) {
+   					AccessStructure struct = (AccessStructure) submissions.get(i);
+
+   					if (isErasable(struct))
+    				{	
+    					manageDeletion(struct);
+    					submissions.remove(i);
+    				}
+    			}
+    			try {
+    				Thread.sleep(SLEEP_TIME);
+    			} catch (InterruptedException e) {
+    				if(manager_enabled) e.printStackTrace();
+    			}
+    		}
+    	}
+    	catch(Exception e)
+    	{
+    		e.printStackTrace();
+    	}
+    	System.out.println("MemoryManager stopped!");
+    }
+
+    public boolean isManager_enabled() {
+        return manager_enabled;
+    }
+    
+    /**
+     * @param manager_enabled New MemoryManager state. Must be set to true
+     *            before starting the memory manager.
+     */
+    public synchronized void setManager_enabled(boolean manager_enabled) {
+        this.manager_enabled = manager_enabled;
+    }
+}

+ 350 - 0
jplag.webService/src/main/java/jplagWebService/serverAccess/RequestAdmin.java

@@ -0,0 +1,350 @@
+/*
+ * Created on 28.05.2005
+ * Author: Moritz Kroll
+ */
+package jplagWebService.serverAccess;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.text.DateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import jplagWebService.server.JPlagException;
+import jplagWebService.server.RequestData;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+public class RequestAdmin {
+
+    private Document doc = null;
+    private Element rootElement = null;
+	private Element nonValidatedRequestsElement = null;
+	private Element validatedRequestsElement = null;
+
+    private File requestFile = null;
+	
+    public RequestAdmin(String jplagHome) {
+        requestFile = new File(jplagHome + File.separator +
+				"account_requests.xml");
+        if(!requestFile.exists())
+            create();
+        else
+            parse();
+    }
+	
+    /**
+     * Writes the "account-requests" data to an XML file
+     */
+    private synchronized void writeXMLFile() {
+        try {
+            // Prepare the DOM document for writing
+            Source source = new DOMSource(doc);
+
+            // Prepare the output file
+            Result result = new StreamResult(requestFile);
+
+            // Write the DOM document to the file
+            Transformer xformer = TransformerFactory.newInstance()
+                    .newTransformer();
+            xformer.transform(source, result);
+        } catch (TransformerConfigurationException e) {
+            e.printStackTrace();
+        } catch (TransformerException e) {
+            e.printStackTrace();
+        }
+    }
+    
+    /**
+     * Creates a new "account-request database" XML file
+     */
+    private void create() {
+        DocumentBuilderFactory docBFac;
+        DocumentBuilder docBuild;
+        try {
+            docBFac = DocumentBuilderFactory.newInstance();
+            docBuild = docBFac.newDocumentBuilder();
+            doc = docBuild.newDocument();
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            return;
+        }
+        rootElement = doc.createElement("account-requests");
+        doc.appendChild(rootElement);
+		nonValidatedRequestsElement = doc.createElement("nonvalidated-requests");
+		validatedRequestsElement = doc.createElement("validated-requests");
+		rootElement.appendChild(nonValidatedRequestsElement);
+		rootElement.appendChild(validatedRequestsElement);
+        writeXMLFile();
+    }
+
+    /**
+     * Loads an existing "account-request database" into memory
+     */
+    private void parse() {
+        try {
+            FileInputStream xmlStream = new FileInputStream(requestFile);
+            DocumentBuilderFactory factory =
+                    DocumentBuilderFactory.newInstance();
+            factory.setIgnoringComments(true);
+            DocumentBuilder builder = factory.newDocumentBuilder();
+            doc = builder.parse(xmlStream);
+            xmlStream.close();
+            rootElement = doc.getDocumentElement();
+			NodeList nodes=rootElement.getElementsByTagName(
+					"nonvalidated-requests");
+			nonValidatedRequestsElement=(Element) nodes.item(0);
+			nodes=rootElement.getElementsByTagName("validated-requests");
+			validatedRequestsElement=(Element) nodes.item(0);
+        }
+        catch (javax.xml.parsers.ParserConfigurationException e) {
+            // throw new RuntimeException("Failed to create DocumentBuilder");
+            e.printStackTrace();
+        } catch (org.xml.sax.SAXException e) {
+            // throw new RuntimeException("Error parsing users.xml");
+            e.printStackTrace();
+        } catch (Exception e) {
+            System.out.println("File error!");
+            e.printStackTrace();
+        }
+    }
+	
+	private void setString(Element elem, String attr, String str) {
+		if(str!=null && str.length()!=0)
+			elem.setAttribute(attr,str);
+	}
+	
+	public static String getRandomCode() {
+		char[] code=new char[11];
+		for(int i=0;i<11;i++) {
+			if((i&3)==3) code[i]=(char)((Math.random()*10)+'0');
+			else code[i]=(char)((Math.random()*26)+'A');
+		}
+		return new String(code);
+	}
+
+	private String formatCalendar(Calendar cal)
+	{
+		DateFormat df=DateFormat.getDateInstance(DateFormat.MEDIUM,Locale.GERMAN);
+		return df.format(cal.getTime());
+	}
+	
+/*	private void setCalendar(Element user, Calendar cal, String attr) {
+		user.setAttribute(attr,formatCalendar(cal));
+	}*/
+	
+    private Calendar parseCalendar(String str)
+    {
+		Calendar cal=new GregorianCalendar(TimeZone.getTimeZone("GMT"));
+		try
+		{
+			cal.setTime(DateFormat.getDateInstance(DateFormat.MEDIUM,
+					Locale.GERMAN).parse(str));
+		}
+		catch(java.text.ParseException ex)
+		{
+			ex.printStackTrace();
+			System.out.println("Illegal date: " + str);
+			cal.set(1970,0,1);	// set an error date (1.1.1970)
+			System.out.println("Set to " + formatCalendar(cal));
+		}
+		return cal;
+    }
+
+    private Calendar parseCalendar(Element elem, String attrname) {
+		return parseCalendar(elem.getAttribute(attrname));
+	}
+	
+	public synchronized String addRequest(RequestData rd) {
+		Element elem=doc.createElement("request");
+		setString(elem,"username",rd.getUsername());
+		setString(elem,"password",rd.getPassword());
+		setString(elem,"realname",rd.getRealName());
+		setString(elem,"email",rd.getEmail());
+		setString(elem,"emailSecond",rd.getEmailSecond());
+		setString(elem,"homepage",rd.getHomepage());
+		setString(elem,"reason",rd.getReason());
+		setString(elem,"notes",rd.getNotes());
+		String code=getRandomCode() + rd.getUsername();
+		setString(elem,"code",code);
+		Calendar cal=Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+		elem.setAttribute("requested",formatCalendar(cal));
+		nonValidatedRequestsElement.appendChild(elem);
+		writeXMLFile();
+		return code;
+	}
+	
+	/**
+	 * Checks the email verification and moves the request from "non-validated"
+	 * to "validated" status, if the verification is valid. Otherwise it throws
+	 * an exception.
+	 * 
+	 * @return The username corresponding to the verification code
+	 * @throws JPlagException when the username wasn't found, the code is not
+	 * 		  correct or the request has already been verified
+	 */
+	public synchronized String validateRequest(String code)
+			throws JPlagException
+	{
+		if(code.length()<12) // username must be at least one character
+		{
+			throw new JPlagException("validateRequest",
+					"Wrong verification code given: " + code,
+					"Please check the correct spelling of the code!");
+		}
+		String username=code.substring(11);
+		System.out.println("[" + new Date() + "] validateRequest: username="
+            + username + " code=" + code);
+		NodeList reqs=nonValidatedRequestsElement.getElementsByTagName("request");
+		for(int i=0;i<reqs.getLength();i++) {
+			Element elem=(Element) reqs.item(i);
+			if(elem.getAttribute("username").equals(username))
+			{
+				if(elem.getAttribute("code").equals(code))
+				{
+					nonValidatedRequestsElement.removeChild(elem);
+					elem.removeAttribute("code");
+					elem.setAttribute("validated",new Date()+"");
+					validatedRequestsElement.appendChild(elem);
+					writeXMLFile();
+					return username;
+				}
+				else throw new JPlagException("validateRequest",
+						"Wrong verification code given: " + code,
+						"Please check the correct spelling of the code!");
+			}
+		}
+		reqs=validatedRequestsElement.getElementsByTagName("request");
+		for(int i=0;i<reqs.getLength();i++) {
+			Element elem=(Element) reqs.item(i);
+			if(elem.getAttribute("username").equals(username))
+			{
+				throw new JPlagException("validateRequest",
+						"The request's email address has already been " +
+						"verified!", "Please wait until the administrator has" +
+						" decided on your request!");
+			}
+		}
+		throw new JPlagException("validateRequest",
+				"No request for user \"" + username + "\" found!",
+				"Please check the correct spelling of the username! "
+                + "The request may also have expired.");
+	}
+	
+	public synchronized void removeRequest(String username) {
+		NodeList reqs=validatedRequestsElement.getElementsByTagName("request");
+		for(int i=0;i<reqs.getLength();i++) {
+			Element elem=(Element) reqs.item(i);
+			if(elem.getAttribute("username").equals(username))
+			{
+				validatedRequestsElement.removeChild(elem);
+				writeXMLFile();
+				return;
+			}
+		}
+	}
+	
+	public synchronized void removeExpiredUnvalidatedRequests() {
+		boolean changed=false;
+		NodeList reqs=nonValidatedRequestsElement.getElementsByTagName("request");
+		Calendar cal=Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+		cal.add(Calendar.DATE,-3);
+		for(int i=reqs.getLength()-1;i>=0;i--) {
+			Element elem=(Element) reqs.item(i);
+			Calendar curcal=parseCalendar(elem,"requested");
+			if(cal.after(curcal))
+			{
+				System.out.println("[" + new Date() + "] Non-validated request"
+						+ " for username " + elem.getAttribute("username")
+						+ " for " + elem.getAttribute("realname")
+						+ " with email " + elem.getAttribute("email")
+						+ " expired!");
+				nonValidatedRequestsElement.removeChild(elem);
+				changed=true;
+			}
+		}
+		if(changed) writeXMLFile();
+	}
+	
+	private String parseNillableString(Element elem, String attrname) {
+		String str=elem.getAttribute(attrname);
+		if(str.length()==0) return null;
+		else return str;
+	}
+	
+	private RequestData getRequestDataFromElement(Element elem) {
+		RequestData rd=new RequestData();
+		rd.setValidateTime(elem.getAttribute("validated"));
+		rd.setUsername(elem.getAttribute("username"));
+		rd.setPassword(elem.getAttribute("password"));
+		rd.setRealName(elem.getAttribute("realname"));
+		rd.setEmail(elem.getAttribute("email"));
+		rd.setEmailSecond(parseNillableString(elem,"emailSecond"));
+		rd.setHomepage(parseNillableString(elem,"homepage"));
+		rd.setReason(elem.getAttribute("reason"));
+		rd.setNotes(parseNillableString(elem,"notes"));
+		return rd;
+	}
+	
+	public synchronized RequestData getRequestData(String username) {
+		NodeList reqs=validatedRequestsElement.getElementsByTagName("request");
+		for(int i=0;i<reqs.getLength();i++) {
+			Element elem=(Element) reqs.item(i);
+			if(elem.getAttribute("username").equals(username))
+				return getRequestDataFromElement(elem);
+		}
+		return null;
+	}
+	
+	public synchronized RequestData[] getRequests(boolean lengthOnly) {
+		NodeList reqs=validatedRequestsElement.getElementsByTagName("request");
+		RequestData[] rds=new RequestData[reqs.getLength()];
+		if(!lengthOnly)	{
+			for(int i=0;i<reqs.getLength();i++) {
+				Element elem=(Element) reqs.item(i);
+				rds[i]=getRequestDataFromElement(elem);
+			}
+		}
+		else {
+			for(int i=0;i<reqs.getLength();i++) {
+				rds[i]=new RequestData();
+			}
+		}
+		return rds;
+	}
+	
+	/**
+	 * @return whether there is a request for the given username
+	 */
+	public synchronized boolean exists(String username) {
+		NodeList reqs=nonValidatedRequestsElement.getElementsByTagName("request");
+		for(int i=0;i<reqs.getLength();i++) {
+			Element elem=(Element) reqs.item(i);
+			if(elem.getAttribute("username").equals(username))
+				return true;
+		}
+		reqs=validatedRequestsElement.getElementsByTagName("request");
+		for(int i=0;i<reqs.getLength();i++) {
+			Element elem=(Element) reqs.item(i);
+			if(elem.getAttribute("username").equals(username))
+				return true;
+		}
+		return false;
+	}
+}

+ 476 - 0
jplag.webService/src/main/java/jplagWebService/serverAccess/ResultAdmin.java

@@ -0,0 +1,476 @@
+/*
+ * Created on 16.03.2005
+ */
+package jplagWebService.serverAccess;
+
+import java.io.File;
+import java.io.FileInputStream;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.*;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import jplagWebService.server.Submission;
+import jplagWebService.serverImpl.JPlagCentral;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+public class ResultAdmin {
+
+	public static final int SEARCH_ENTRIES=1;
+	public static final int SEARCH_RESULTS=2;
+	public static final int SEARCH_ALL=SEARCH_ENTRIES | SEARCH_RESULTS;
+	
+	private File usersDataFile=null;
+	private Document doc=null;
+	private Element entryRoot=null;
+	private Element resultRoot=null;
+	private Element rootElement=null;
+	
+	private int lastSubmissionID=1000000000;
+	
+	public ResultAdmin(String jplagHome)
+	{
+		usersDataFile=new File(jplagHome + File.separator + "usersdata.xml");
+		if(!usersDataFile.exists()) create();
+		else parse();
+	}
+	
+    /**
+     * Writes the "user submission database" to an XML file
+     */ 
+    private void writeXMLFile()
+    {
+        try {
+            // Prepare the DOM document for writing
+            Source source = new DOMSource(doc);
+    
+            // Prepare the output file
+            Result result = new StreamResult(usersDataFile);
+    
+            // Write the DOM document to the file
+            Transformer xformer =
+                TransformerFactory.newInstance().newTransformer();
+            xformer.transform(source, result);
+        } catch (TransformerConfigurationException e) {
+        	e.printStackTrace();
+        } catch (TransformerException e) {
+        	e.printStackTrace();
+        }
+    }
+	
+    /**
+     * Creates a new "user submission database" XML file
+     */
+	private void create()
+	{
+		DocumentBuilderFactory docBFac;
+		DocumentBuilder docBuild;
+		try
+		{
+			docBFac=DocumentBuilderFactory.newInstance();
+			docBuild = docBFac.newDocumentBuilder();
+			doc = docBuild.newDocument();
+		}
+		catch(Exception ex)
+		{
+			ex.printStackTrace();
+			return;
+		}
+		rootElement=doc.createElement("jplagData");
+		lastSubmissionID=1000000000;
+		rootElement.setAttribute("lastSubID",lastSubmissionID+"");
+		entryRoot=doc.createElement("EntriesQueue");
+		resultRoot=doc.createElement("ResultData");
+		rootElement.appendChild(entryRoot);
+		rootElement.appendChild(resultRoot);
+		doc.appendChild(rootElement);
+
+		writeXMLFile();
+	}
+	
+	/**
+	 * Loads an existing "user submission database" into memory
+	 */
+	private void parse()
+	{
+		try
+		{
+			FileInputStream xmlStream=new FileInputStream(usersDataFile);
+			DocumentBuilderFactory factory =
+                    DocumentBuilderFactory.newInstance();
+			factory.setIgnoringComments(true);
+			DocumentBuilder builder=factory.newDocumentBuilder();
+			doc=builder.parse(xmlStream);
+            xmlStream.close();
+			rootElement=doc.getDocumentElement();
+			lastSubmissionID=Integer.parseInt(
+                rootElement.getAttribute("lastSubID"));
+			System.out.println("lastSubmissionID="+lastSubmissionID);
+			
+			NodeList entriesList=doc.getElementsByTagName("EntriesQueue");
+			entryRoot=(Element) entriesList.item(0);
+			entriesList=doc.getElementsByTagName("ResultData");
+			resultRoot=(Element) entriesList.item(0);
+		}
+		catch(javax.xml.parsers.ParserConfigurationException e)
+		{
+			System.out.println("Failed to create DocumentBuilder!");
+			e.printStackTrace();
+		}
+		catch(org.xml.sax.SAXException e)
+		{
+			System.out.println("Error parsing users.xml!");
+			e.printStackTrace();
+		}
+		catch(Exception e)
+		{
+			System.out.println("File error!");
+			e.printStackTrace();
+		}
+	}
+	
+	/**
+	 * @return Next available submission ID
+	 */
+	public synchronized String getNextSubmissionID()
+	{
+		lastSubmissionID++;
+		rootElement.setAttribute("lastSubID",lastSubmissionID+"");
+        writeXMLFile();
+		return lastSubmissionID+"";
+	}
+	
+	/**
+	 * Adds a new submission to the entry queue
+	 * @param struct Submission to be queued
+	 */
+	public synchronized void addEntry(AccessStructure struct)
+	{
+		entryRoot.appendChild(struct.toXMLEntryElement(doc));
+		
+		writeXMLFile();
+	}
+
+	public synchronized int getNumEntries()
+	{
+		NodeList list=entryRoot.getElementsByTagName("entry");
+		return list.getLength();
+	}
+
+	public synchronized AccessStructure getNextEntry()
+	{
+		if(!entryRoot.hasChildNodes()) return null;
+		return AccessStructure.fromXMLEntryElement(
+            (Element) entryRoot.getFirstChild());
+	}
+	
+	public synchronized AccessStructure getAccessStruct(String subID,
+            int searchFlags)
+	{
+		NodeList list;
+		if((searchFlags & SEARCH_ENTRIES)!=0)
+		{
+			list=entryRoot.getElementsByTagName("entry");
+			for(int i=0;i<list.getLength();i++)
+			{
+				Element entry=(Element) list.item(i);
+			
+				if(entry.getAttribute("id").equals(subID))
+				{
+					AccessStructure struct = 
+                        AccessStructure.fromXMLEntryElement(entry);
+                    struct.getDecorator().setProgress(i);   // set queue pos
+                    return struct;
+				}
+			}
+		}
+		if((searchFlags & SEARCH_RESULTS)!=0)
+		{
+			list=resultRoot.getElementsByTagName("result");
+			for(int i=0;i<list.getLength();i++)
+			{
+				Element submission=(Element) list.item(i);
+			
+				if(submission.getAttribute("id").equals(subID))
+				{
+					return AccessStructure.fromXMLResultElement(submission);
+				}
+			}
+		}
+		return null;
+	}
+	
+	/**
+	 * Adds a completed submission to the resultRoot and removes the first
+	 * submission from the elementRoot 
+	 */
+	public synchronized void addResult(AccessStructure struct)
+	{
+		NodeList usersList=resultRoot.getElementsByTagName("user");
+		Element founduser=null;
+		for(int i=0;i<usersList.getLength();i++)
+		{
+			Element user=(Element) usersList.item(i);
+			
+			if(user.getAttribute("username").equals(struct.getUsername()))
+			{
+				founduser=user;
+				break;
+			}
+		}
+		if(founduser==null)
+		{
+			founduser=doc.createElement("user");
+			founduser.setAttribute("username",struct.getUsername());
+			resultRoot.appendChild(founduser);
+		}
+		founduser.appendChild(struct.toXMLResultElement(doc));
+		
+		// XXX: Check whether the first is the right one		
+		Node firstEntry=entryRoot.getFirstChild();
+		entryRoot.removeChild(firstEntry);
+		writeXMLFile();
+	}
+	
+	/**
+	 * Removes a given ready to be processed submission from the database
+	 * @return True, when it was successfully deleted.
+	 * False, if the submission wasn't found.
+	 */
+	public synchronized boolean deleteEntry(String username,
+            String submissionID)
+	{
+		NodeList list=entryRoot.getElementsByTagName("entry");
+		for(int i=0;i<list.getLength();i++)
+		{
+			Element entry=(Element) list.item(i);
+		
+			if(entry.getAttribute("id").equals(submissionID))
+			{
+				if(!entry.getAttribute("username").equals(username))
+					return false;
+				entryRoot.removeChild(entry);
+				writeXMLFile();
+				return true;
+			}
+		}
+		return false;
+	}
+	
+	/**
+	 * Removes a given completed submission from the database
+	 * @return True, when it was successfully deleted.
+	 * False, if the submission wasn't found.
+	 */
+	public synchronized boolean deleteResult(String username,
+            String submissionID)
+	{
+		NodeList usersList = resultRoot.getElementsByTagName("user");
+        int numusers = usersList.getLength();
+		for(int i=0; i<numusers; i++)
+		{
+			Element user = (Element) usersList.item(i);
+			
+			if(user.getAttribute("username").equals(username))
+			{
+				NodeList subList = user.getElementsByTagName("result");
+                int numresults = subList.getLength();
+				for(int j=0; j<numresults; j++)
+				{
+					Element elem = (Element) subList.item(j);
+					if(elem.getAttribute("id").equals(submissionID))
+					{
+						user.removeChild(elem);
+						if(numresults==1)                // removed last result?
+							resultRoot.removeChild(user);	// remove user entry
+					
+						writeXMLFile();
+						return true;
+					}
+				}
+			}
+		}
+		return false;
+	}
+	
+	/**
+	 * Deletes an submission from the database
+	 * @param struct: Submission to be deleted
+	 * @return true, if everything's alright
+	 */	
+	public synchronized boolean deleteSubmission(AccessStructure struct)
+	{
+		if(struct.getState()<300)
+			return deleteEntry(struct.getUsername(),struct.getSubmissionID());
+		else
+			return deleteResult(struct.getUsername(),struct.getSubmissionID());
+	}
+
+	/**
+	 * @return String array containing all usernames
+	 */
+	public synchronized String[] usersList()
+	{	
+		NodeList usersList= doc.getElementsByTagName("user");
+		String [] result = new String [usersList.getLength()];
+		for(int i=0;i<usersList.getLength();i++)
+		{
+			Element user=(Element) usersList.item(i);
+			result[i]=user.getAttribute("username");
+		}
+		return result;
+	}
+	
+	/**
+	 * @return The number of waiting submissions for a given user
+	 */
+	public synchronized int getNumUserEntries(String username)
+	{
+		NodeList list=entryRoot.getElementsByTagName("entry");
+		int numentries=0;
+		for(int j=0;j<list.getLength();j++)
+		{
+			Element entry=(Element) list.item(j);
+		
+			if(entry.getAttribute("username").equals(username))
+				numentries++;
+		}
+		return numentries;
+	}
+	
+	/**
+	 * @return The number of finished submissions for a given user
+	 */
+	public synchronized int getNumUserResults(String username)
+	{
+		NodeList usersList=doc.getElementsByTagName("user");
+		
+		for(int i=0;i<usersList.getLength();i++)
+		{
+			Element user=(Element) usersList.item(i);
+				
+			if(user.getAttribute("username").equals(username))
+			{
+				NodeList subList=user.getElementsByTagName("result");
+				return subList.getLength();
+			}
+		}
+		return 0;
+	}
+	
+	/**
+	 * @return A list of Submission objects belonging to a given user
+	 */
+	public synchronized Submission[] listSubmissions(String username)
+	{
+		NodeList usersList=doc.getElementsByTagName("user");
+	
+		for(int i=0;i<usersList.getLength();i++)
+		{
+			Element user=(Element) usersList.item(i);
+			
+			if(user.getAttribute("username").equals(username))
+			{
+				NodeList list=entryRoot.getElementsByTagName("entry");
+				int numentries=0;
+				for(int j=0;j<list.getLength();j++)
+				{
+					Element entry=(Element) list.item(j);
+				
+					if(entry.getAttribute("username").equals(username))
+						numentries++;
+				}
+				NodeList subList = user.getElementsByTagName("result");
+				Submission[] subArray =
+                    new Submission[numentries+subList.getLength()];
+				int subind=0;
+				for(int j=0;j<list.getLength();j++)
+				{
+					Element entry=(Element) list.item(j);
+				
+					if(entry.getAttribute("username").equals(username))
+					{
+						String subID=entry.getAttribute("id");
+						int state=JPlagCentral.getStateIfActive(username,subID);
+						subArray[subind++]=new Submission(subID,
+							entry.getAttribute("title"),new java.util.Date(
+								Long.parseLong(entry.getAttribute("date")))
+								.toString(),state);
+					}
+				}
+				for(int j=0;j<subList.getLength();j++)
+				{
+					Element elem=(Element) subList.item(j);
+					int laststate;
+					try
+					{
+						laststate=Integer.parseInt(
+                            elem.getAttribute("laststate"));
+					}
+					catch(NumberFormatException e)
+					{
+						laststate=499;
+					}
+					subArray[subind++]=new Submission(elem.getAttribute("id"),
+						elem.getAttribute("title"),
+						new java.util.Date(
+								Long.parseLong(elem.getAttribute("date")))
+								.toString(),
+						laststate);
+				}
+				return subArray;
+			}
+		}
+		return new Submission[0];
+	}
+
+	/**
+	 * @return A list of AccessStructure objects belonging to a given user
+	 */
+	public synchronized AccessStructure[] listAccessStructures(String username)
+	{
+		NodeList usersList=doc.getElementsByTagName("user");
+	
+		for(int i=0;i<usersList.getLength();i++)
+		{
+			Element user=(Element) usersList.item(i);
+			
+			if(user.getAttribute("username").equals(username))
+			{
+				NodeList list=entryRoot.getElementsByTagName("entry");
+				int numentries=0;
+				for(int j=0;j<list.getLength();j++)
+				{
+					Element entry=(Element) list.item(j);
+					if(entry.getAttribute("username").equals(username))
+						numentries++;
+				}
+				NodeList subList=user.getElementsByTagName("result");
+				AccessStructure[] structArray=new AccessStructure[
+                        numentries+subList.getLength()];
+				int subind=0;
+				for(int j=0;j<list.getLength();j++)
+				{
+					Element entry=(Element) list.item(j);
+					if(entry.getAttribute("username").equals(username))
+						structArray[subind++] =
+                            AccessStructure.fromXMLEntryElement(entry);
+				}
+				for(int j=0;j<subList.getLength();j++)
+				{
+					Element elem=(Element) subList.item(j);
+					structArray[subind++] =
+                        AccessStructure.fromXMLResultElement(elem);
+				}
+				return structArray;
+			}
+		}
+		return new AccessStructure[0];
+	}
+}

+ 91 - 0
jplag.webService/src/main/java/jplagWebService/serverAccess/StatusDecorator.java

@@ -0,0 +1,91 @@
+/*
+ * Created on 06.03.2005
+ */
+package jplagWebService.serverAccess;
+
+import jplagWebService.server.Status;
+
+/**
+ * Redirects status changes to AccessStructure.status object
+ * 
+ * @author Emeric Kwemou
+ */
+
+public class StatusDecorator {
+    public static final int UPLOADING = 0;
+	public static final int WAITING_IN_QUEUE = 50;
+	public static final int COMPARE_SOURCE_DONE = 300;
+
+	private Status status;
+	
+	public StatusDecorator(Status status) {
+		this.status = status;
+		ensureSerializable();
+	}
+
+	public Status getStatus() {
+		return status;
+	}
+	
+	public int getState() {
+		return status.getState();
+	}
+
+	public void setState(int state) {
+		status.setState(state);
+	}
+
+	public int getProgress() {
+		return status.getProgress();
+	}
+
+	public void setProgress(int progress) {
+		status.setProgress(progress);
+	}
+
+	public java.lang.String getReport() {
+		return status.getReport();
+	}
+
+	public void setReport(java.lang.String report) {
+		this.status.setReport(report);
+	}
+	
+	public void addReport(String report) {
+		if(status.getReport().length()==0) status.setReport(report);
+		else status.setReport(status.getReport() + "\n" + report); 
+	}
+
+	/**
+	 * @param state
+	 * 		New state to be added, only added if there wasn't a previous error
+	 * @param report
+	 *      New message to be added to the report
+	 */
+	public void add(int state, String report) {
+		if (getState() < 400) setState(state);
+		addReport(report);
+	}
+
+	public void add(int state, int progress, String report) {
+		if(getState() < 400) setState(state);
+		setProgress(progress);
+		addReport(report);
+	}
+
+	public void add(jplag.ExitException ex) {
+		add(ex.getState(), ex.getReport());
+	}
+
+	/**
+	 * Ensure that nothing is null
+	 */
+	private void ensureSerializable() {
+		if (status == null) {
+			status = new Status(0, 0, "");
+			return;
+		}
+		if (status.getReport() == null)
+			status.setReport("");
+	}
+}

+ 387 - 0
jplag.webService/src/main/java/jplagWebService/serverAccess/TransferManager.java

@@ -0,0 +1,387 @@
+package jplagWebService.serverAccess;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.concurrent.DelayQueue;
+import java.util.concurrent.Delayed;
+import java.util.concurrent.TimeUnit;
+
+import jplagWebService.server.JPlagException;
+import jplagWebService.server.StartResultDownloadData;
+import jplagWebService.serverImpl.JPlagCentral;
+
+public class TransferManager {
+    private DelayQueue<TransferObject> uploadTimeoutQueue =
+        new DelayQueue<TransferObject>();
+    private DelayQueue<TransferObject> downloadTimeoutQueue =
+        new DelayQueue<TransferObject>();
+    
+    private HashMap<String,TransferObject> userToUploadMap
+        = new HashMap<String,TransferObject>();
+    private HashMap<String,TransferObject> userToDownloadMap
+    = new HashMap<String,TransferObject>();
+
+    private boolean doStopTransferManager = false;
+        
+    private class TransferObject implements Delayed {
+        private AccessStructure struct;
+        private long timeoutTime;
+        private File file;
+        private int filesize;
+        private int remainingBytes;
+        
+        /**
+         * timeout is given in seconds
+         */
+        TransferObject(AccessStructure struct, File file, int filesize,
+                long timeout) {
+            this.struct = struct;
+            this.file = file;
+            this.filesize = filesize;
+            remainingBytes = filesize;
+            setTimeout(timeout);
+        }
+
+        public long getDelay(TimeUnit unit) {
+            long n = timeoutTime - System.nanoTime();
+            return unit.convert(n, TimeUnit.NANOSECONDS);
+        }
+
+        public int compareTo(Delayed o) {
+            if(timeoutTime < ((TransferObject)o).timeoutTime)
+                return -1;
+            else if(timeoutTime > ((TransferObject)o).timeoutTime)
+                return 1;
+            else
+                return 0;
+        }
+        
+        public AccessStructure getStruct() {
+            return struct;
+        }
+        
+        public File getFile() {
+            return file;
+        }
+        
+        public int getRemainingBytes() {
+            return remainingBytes;
+        }
+        
+        /**
+         * Writes the next part of the related file
+         * @returns true, if this was the last part
+         * @throws JPlagException If there is more data, than expected or
+         *    writing to the file doesn't work
+         */
+        public boolean writeNextPart(byte[] data) throws JPlagException {
+            if(remainingBytes<data.length) {
+                throw new JPlagException("uploadException", "More data sent " +
+                        "than expected!","");
+            }
+            try {
+                FileOutputStream out = new FileOutputStream(file, true);
+                out.write(data);
+                out.close();
+                remainingBytes -= data.length;
+            }
+            catch(IOException e) {
+                e.printStackTrace();
+                throw new JPlagException("uploadException", "Unable to save " +
+                        "submission part on the server!",
+                        "Server out of disk space??");
+            }
+            return remainingBytes==0;
+        }
+        
+        /**
+         * Reads the next part of the related file
+         * @return an array containing the next part with the correct size
+         * @throws JPlagException If the download has already been completed
+         *    or there was an I/O error
+         */
+        public byte[] readNextPart() throws JPlagException {
+            if(remainingBytes==0) {
+                throw new JPlagException("downloadException",
+                    "There's nothing left to be downloaded!","");
+            }
+            try {
+                FileInputStream in = new FileInputStream(file);
+                in.skip(filesize-remainingBytes);
+                
+                int partsize = remainingBytes;
+                if(partsize>81920) partsize = 81920;
+                
+                byte[] data = new byte[partsize];
+                in.read(data);
+                in.close();
+                remainingBytes -= partsize;
+                return data;
+            }
+            catch(IOException e) {
+                e.printStackTrace();
+                throw new JPlagException("downloadException", "Unable to read" +
+                        " submission part from server!","");
+            }
+        }
+        
+        public void setTimeout(long timeout) {
+            timeoutTime = System.nanoTime() + timeout * 1000000000;
+        }
+    }
+    
+    /**
+     * Checks, whether the user is already uploading a submission, and if not,
+     * saves the first part of the entry file onto disk. If there are more
+     * parts, the user becomes marked as uploading and the upload is added to
+     * the upload timeout queue
+     * @param struct AccessStructure for the according submission
+     * @param filesize Total size of the zip file containing the submission
+     * @param data First 80 kB part (or less, if file is less than 80 kB)
+     * @throws JPlagException If user is already uploading, the file
+     *      already exists or saving didn't work
+     */
+    public synchronized void startUpload(AccessStructure struct,
+            int filesize, byte[] data) throws JPlagException
+    {
+        if(userToUploadMap.containsKey(struct.getUsername())) {
+            throw new JPlagException("uploadException", "You are already " +
+                    "uploading a submission!", "Only one submission may be " +
+                    "uploaded by one user at a time");
+        }
+        File file = new File(struct.getEntryPath());
+        
+        if(file.exists()) {
+            throw new JPlagException("uploadException", "File already exist!?",
+                "Unable to create new file. Please tell the admins " +
+                "about this!");
+        }
+        
+        TransferObject obj = new TransferObject(struct, file, filesize, 120);
+        if(!obj.writeNextPart(data)) {
+            userToUploadMap.put(struct.getUsername(), obj);
+            uploadTimeoutQueue.add(obj);
+        }
+        else JPlagCentral.addToReadyQueue(struct);
+    }
+    
+    /**
+     * Writes the next part of the upload identified by the username.
+     * If it was the last part it unmarks the user and adds the submission
+     * to the ready queue
+     * @param username Username of user uploading the submission
+     * @param data Next 80 kB part (or less if it is the last part)
+     * @throws JPlagException If saving didn't work or upload doesn't exist
+     */
+    public synchronized void writeNextPart(String username, byte[] data)
+            throws JPlagException
+    {
+        TransferObject obj = userToUploadMap.get(username);
+        if(obj == null) {
+            throw new JPlagException("uploadException", "No upload started " +
+                    "or upload timed out!", "Restart the upload");
+        }
+        uploadTimeoutQueue.remove(obj);
+        if(!obj.writeNextPart(data)) {
+            obj.setTimeout(120);
+            uploadTimeoutQueue.add(obj);
+        }
+        else {
+            userToUploadMap.remove(username);
+            JPlagCentral.addToReadyQueue(obj.getStruct());
+        }
+    }
+    
+    /**
+     * Cancels the upload with the given submissionID (if provided) for the
+     * given user.
+     * If the user doesn't have an upload or the submissionID doesn't match,
+     * false is returned.
+     */
+    public synchronized boolean cancelUpload(String username,
+            String submissionID)
+    {
+        TransferObject obj = userToUploadMap.get(username);
+        if(obj == null || submissionID!=null && 
+                !obj.getStruct().getSubmissionID().equals(submissionID)) {
+            return false;
+        }
+        uploadTimeoutQueue.remove(obj);
+        userToUploadMap.remove(obj.getStruct().getUsername());
+        obj.getFile().delete();
+        return true;
+    }
+
+    /**
+     * Checks, whether the user is already downloading a submission, and if not,
+     * loads the first part of the result file from disk. If there are more
+     * parts, the user becomes marked as downloading and the download is added
+     * to the download timeout queue
+     * @param struct AccessStructure for the according submission
+     * @return The filesize and the first part of the entry file
+     * @throws JPlagException If user is already downloading, the file
+     *      doesn't exists or loading didn't work
+     */
+    public synchronized StartResultDownloadData startDownload(
+            AccessStructure struct) throws JPlagException
+    {
+        if(userToDownloadMap.containsKey(struct.getUsername())) {
+            throw new JPlagException("downloadException", "You are already " +
+                    "downloading a submission!", "Only one submission may be " +
+                    "downloaded by one user at a time");
+        }
+        File file = new File(struct.getResultPath());
+        
+        if(!file.exists()) {
+            System.out.println("startDownload: \"" + struct.getResultPath()
+                + "\" doesn't exist!");
+            throw new JPlagException("downloadException",
+                "File doesn't exist!?", "Unable to find result file!");
+        }
+        
+        int filesize = (int) file.length();
+        
+        TransferObject obj = new TransferObject(struct, file, filesize, 120);
+        byte[] data = obj.readNextPart();
+        if(obj.getRemainingBytes()>0) {
+            userToDownloadMap.put(struct.getUsername(), obj);
+            downloadTimeoutQueue.add(obj);
+        }
+        else JPlagCentral.cancelSubmission(struct);
+        return new StartResultDownloadData(filesize, data);
+    }
+
+    /**
+     * Reads the next part of the download identified by the username.
+     * If it was the last part it unmarks the user and deletes the entry file
+     * @param username Username of user downloading the result
+     * @return data Next 80 kB part (or less if it is the last part)
+     * @throws JPlagException If loading didn't work or download doesn't exist
+     */
+    public synchronized byte[] readNextPart(String username)
+            throws JPlagException
+    {
+        TransferObject obj = userToDownloadMap.get(username);
+        if(obj == null) {
+            throw new JPlagException("downloadException",
+                "No download started or download timed out!",
+                "Restart the download");
+        }
+        downloadTimeoutQueue.remove(obj);
+        byte[] data = obj.readNextPart();
+        if(obj.getRemainingBytes()!=0) {
+            obj.setTimeout(120);
+            downloadTimeoutQueue.add(obj);
+        }
+        else {
+            userToDownloadMap.remove(username);
+            JPlagCentral.cancelSubmission(obj.getStruct());
+        }
+        return data;
+    }
+
+    /**
+     * Cancels the download with the given submissionID (if provided) for the
+     * given user and deletes the all files related to the submission.
+     * If the user doesn't have a download or the submissionID doesn't match,
+     * false is returned.
+     */
+    public synchronized boolean cancelDownload(String username,
+            String submissionID)
+    {
+        TransferObject obj = userToDownloadMap.get(username);
+        if(obj == null || submissionID!=null && 
+                !obj.getStruct().getSubmissionID().equals(submissionID)) {
+            return false;
+        }
+        downloadTimeoutQueue.remove(obj);
+        userToDownloadMap.remove(obj.getStruct().getUsername());
+        JPlagCentral.cancelSubmission(obj.getStruct());
+        return true;
+    }
+    
+    /**
+     * Cancels a transfer for the user with the given submissionID if existing.
+     * @return true, if the transfer got cancelled
+     */
+    public boolean cancelTransfer(String username, String submissionID) {
+        if(cancelUpload(username, submissionID)) return true;
+        return cancelDownload(username, submissionID);
+    }
+
+    public void stopTransferManager() {
+        doStopTransferManager = true;
+    }
+    
+    /**
+     * Waits for the next uploaded transfer object to timeout and removes it
+     */
+    private Thread uploadThread = new Thread() {
+        public void run() {
+            try {
+                while(!doStopTransferManager) {
+                    TransferObject obj = null;
+                    try {
+                        obj = uploadTimeoutQueue.take();
+                    }
+                    catch(InterruptedException ex) {
+                        continue;
+                    }
+                    // Upload timed out, so delete already uploaded part
+                    synchronized(this) {
+                        obj.getFile().delete();
+                        userToUploadMap.remove(obj.getStruct().getUsername());
+                    }
+                }
+            }
+            catch(Exception ex) {
+                ex.printStackTrace();
+            }
+            System.out.println("Upload TransferManager stopped!");
+        }
+    };
+
+    /**
+     * Waits for the next downloaded transfer object to timeout and removes it
+     */
+    private Thread downloadThread = new Thread() {
+        public void run() {
+            try {
+                while(!doStopTransferManager) {
+                    TransferObject obj = null;
+                    try {
+                        obj = downloadTimeoutQueue.take();
+                    }
+                    catch(InterruptedException ex) {
+                        continue;
+                    }
+                    // Download timed out, so cancel download
+                    synchronized(this) {
+                        userToDownloadMap.remove(obj.getStruct().getUsername());
+                    }
+                }
+            }
+            catch(Exception ex) {
+                ex.printStackTrace();
+            }
+            System.out.println("Download TransferManager stopped!");
+        }
+    };
+    
+    public void start() {
+        uploadThread.start();
+        downloadThread.start();
+    }
+    
+    public boolean isAlive() {
+        return uploadThread.isAlive() || downloadThread.isAlive();
+    }
+    
+    public void interrupt() {
+        uploadThread.interrupt();
+        downloadThread.interrupt();
+    }
+}

+ 75 - 0
jplag.webService/src/main/java/jplagWebService/serverAccess/TrustAllSSLSocketFactory.java

@@ -0,0 +1,75 @@
+package jplagWebService.serverAccess;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * IMPORTANT NOTE: Because of the class loader hierarchy this classes class
+ * 				   files (including the folder stucture) has to be copied into
+ * 				   the common/classes folder on a Tomcat server!
+ * 				   Otherwise the SSL functions will not find this class!
+ * 				   (You have to restart the server after adding these files!)
+ */
+public class TrustAllSSLSocketFactory extends SSLSocketFactory
+{
+	static SSLSocketFactory sockFac=null;
+	static TrustAllSSLSocketFactory tassf=null;
+	public static synchronized SocketFactory getDefault()
+	{
+		if(sockFac==null)
+		{
+			// Create a trust manager that does not validate certificate chains
+			TrustManager[] trustAllCerts = new TrustManager[]{
+				new X509TrustManager() {
+					public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+						return null;
+					}
+					public void checkClientTrusted(
+						java.security.cert.X509Certificate[] certs, String authType) {
+					}
+					public void checkServerTrusted(
+						java.security.cert.X509Certificate[] certs, String authType) {
+					}
+				}
+			};
+			
+			try {
+				SSLContext sc = SSLContext.getInstance("SSL");
+				sc.init(null, trustAllCerts, new java.security.SecureRandom());
+				sockFac=sc.getSocketFactory();
+			} catch (Exception e) {
+		    }
+			tassf=new TrustAllSSLSocketFactory();
+		}
+		return tassf;
+	}
+	public String[] getDefaultCipherSuites() {
+		return sockFac.getDefaultCipherSuites();
+	}
+	public String[] getSupportedCipherSuites() {
+		return sockFac.getSupportedCipherSuites();
+	}
+	public Socket createSocket(Socket arg0, String arg1, int arg2, boolean arg3) throws IOException {
+		return sockFac.createSocket(arg0,arg1,arg2,arg3);
+	}
+	public Socket createSocket(String arg0, int arg1) throws IOException, UnknownHostException {
+		return sockFac.createSocket(arg0,arg1);
+	}
+	public Socket createSocket(InetAddress arg0, int arg1) throws IOException {
+		return sockFac.createSocket(arg0,arg1);
+	}
+	public Socket createSocket(String arg0, int arg1, InetAddress arg2, int arg3) throws IOException, UnknownHostException {
+		return sockFac.createSocket(arg0,arg1,arg2,arg3);
+	}
+	public Socket createSocket(InetAddress arg0, int arg1, InetAddress arg2, int arg3) throws IOException {
+		return sockFac.createSocket(arg0,arg1,arg2,arg3);
+	}
+}

+ 956 - 0
jplag.webService/src/main/java/jplagWebService/serverAccess/UserAdmin.java

@@ -0,0 +1,956 @@
+package jplagWebService.serverAccess;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.text.DateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Properties;
+import java.util.TimeZone;
+import java.util.regex.Pattern;
+
+import javax.mail.Authenticator;
+import javax.mail.Message;
+import javax.mail.PasswordAuthentication;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import jplagUtils.PropertiesLoader;
+import jplagWebService.server.FinishRequestData;
+import jplagWebService.server.JPlagException;
+import jplagWebService.server.MailTemplate;
+import jplagWebService.server.RequestData;
+import jplagWebService.server.UserData;
+import jplagWebService.server.UserDataArray;
+import jplagWebService.server.UserInfo;
+import jplagWebService.serverImpl.JPlagCentral;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+public class UserAdmin extends Thread {
+
+	private static final long ONEDAY = 86400000;
+
+	private boolean doStopUserAdmin = false;
+
+	public static final int USER_INVALID = 0;
+	public static final int USER_NORMAL = 1;
+	//	public static final int USER_EXPIRED = 5;
+	//	public static final int USER_DEACTIVATED = 10;
+	public static final int USER_GROUPADMIN = 64;
+	public static final int USER_JPLAGADMIN = 192;
+	public static final int USER_JPLAGADMINNOTIFY = 224; // (email notify)
+	public static final int USER_SERVERPAGE = 256;
+
+	public static final int MASK_DEVELOPER = 2;
+	public static final int MASK_EXPIRED = 4;
+	public static final int MASK_DEACTIVATED = 8;
+	public static final int MASK_REQUESTNOTIFY = 32;
+	public static final int MASK_ANYADMIN = 64;
+	public static final int MASK_JPLAGADMIN = 128;
+	public static final int MASK_SERVERPAGE = 256;
+	public static final int MASK_NOAUTOASKEXTEND = 512;
+
+	public static final String JPLAG_SERVER = "https://jplag.ipd.kit.edu/";
+
+	private Document doc = null;
+	private Element rootElement = null;
+
+	private File userFile = null;
+
+	private PrintWriter logWriter = null;
+	private PrintWriter mailLogWriter = null;
+
+	private HashMap<String, PassAndState> userPassList = new HashMap<String, PassAndState>();
+	private Object userPassListMutex = new Object();
+
+	private class PassAndState {
+		public String password;
+		public int state;
+
+		public PassAndState(String pass, int stat) {
+			password = pass;
+			state = stat;
+		}
+	}
+
+	private RequestAdmin requestAdmin = null;
+
+	public UserAdmin(String jplagHome) {
+		requestAdmin = new RequestAdmin(jplagHome);
+		userFile = new File(jplagHome + File.separator + "jplag_users.xml");
+		if (!userFile.exists())
+			create();
+		else
+			parse();
+		try {
+			logWriter = new PrintWriter(new FileWriter(new File(jplagHome + File.separator + "userAdmin.log"), true), true);
+			mailLogWriter = new PrintWriter(new FileWriter(new File(jplagHome + File.separator + "userAdminMail.log"), true), true);
+		} catch (java.io.IOException ex) {
+			System.out.println("UserAdmin: Unable to open log files!");
+		}
+	}
+
+	public RequestAdmin getRequestAdmin() {
+		return requestAdmin;
+	}
+
+	private synchronized void log(String str) {
+		if (logWriter != null)
+			logWriter.println(str);
+	}
+
+	/**
+	 * Writes the "jplag-users" data to an XML file
+	 */
+	private synchronized void writeXMLFile() {
+		try {
+			// Prepare the DOM document for writing
+			Source source = new DOMSource(doc);
+
+			// Prepare the output file
+			Result result = new StreamResult(userFile);
+
+			// Write the DOM document to the file
+			Transformer xformer = TransformerFactory.newInstance().newTransformer();
+			xformer.transform(source, result);
+		} catch (TransformerConfigurationException e) {
+			e.printStackTrace();
+		} catch (TransformerException e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * Creates a new "jplag-user database" XML file
+	 */
+	private void create() {
+		DocumentBuilderFactory docBFac;
+		DocumentBuilder docBuild;
+		try {
+			docBFac = DocumentBuilderFactory.newInstance();
+			docBuild = docBFac.newDocumentBuilder();
+			doc = docBuild.newDocument();
+		} catch (Exception ex) {
+			ex.printStackTrace();
+			return;
+		}
+		rootElement = doc.createElement("jplag-users");
+		doc.appendChild(rootElement);
+		writeXMLFile();
+	}
+
+	/**
+	 * Loads an existing "jplag-user database" into memory
+	 */
+	private void parse() {
+		try {
+			FileInputStream xmlStream = new FileInputStream(userFile);
+			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+			factory.setIgnoringComments(true);
+			DocumentBuilder builder = factory.newDocumentBuilder();
+			doc = builder.parse(xmlStream);
+			xmlStream.close();
+			rootElement = doc.getDocumentElement();
+
+			NodeList userList = rootElement.getElementsByTagName("user");
+			System.out.println("Number of users: " + userList.getLength());
+
+			synchronized (userPassListMutex) {
+				// avoid rehash operations during initialisation
+				userPassList = new HashMap<String, PassAndState>((int) (userList.getLength() * 1.4));
+				for (int j = 0; j < userList.getLength(); j++) {
+					Element user = (Element) userList.item(j);
+					userPassList.put(user.getAttribute("username"),
+							new PassAndState(user.getAttribute("password"), parseInt(user, "state")));
+				}
+			}
+		} catch (javax.xml.parsers.ParserConfigurationException e) {
+			// throw new RuntimeException("Failed to create DocumentBuilder");
+			e.printStackTrace();
+		} catch (org.xml.sax.SAXException e) {
+			// throw new RuntimeException("Error parsing users.xml");
+			e.printStackTrace();
+		} catch (Exception e) {
+			System.out.println("File error!");
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * Checks whether the given user already exists
+	 */
+	public boolean exists(String username) {
+		boolean ret;
+		synchronized (userPassListMutex) {
+			ret = userPassList.containsKey(username);
+		}
+		return ret;
+	}
+
+	/**
+	 * Checks whether the given username is valid
+	 */
+	public boolean isValidUsername(String username) {
+		return Pattern.matches("^[\\w\\@\\.]+$", username);
+	}
+
+	public int getLoginState(String username, String password) {
+		synchronized (userPassListMutex) {
+			if (userPassList.containsKey(username)) {
+				PassAndState pas = (PassAndState) userPassList.get(username);
+				if (!pas.password.equals(password))
+					return USER_INVALID;
+				return pas.state;
+			}
+		}
+		return USER_INVALID;
+	}
+
+	/**
+	 * Returns the user XML element for the given username
+	 */
+	private Element getUserXMLElement(String username) {
+		NodeList userList = rootElement.getElementsByTagName("user");
+		for (int i = 0; i < userList.getLength(); i++) {
+			Element elem = (Element) userList.item(i);
+			if (elem.getAttribute("username").equals(username))
+				return elem;
+		}
+		return null;
+	}
+
+	private void setString(Element elem, String attrname, String str) {
+		String old = elem.getAttribute(attrname);
+		if (old.equals(str))
+			return;
+		log("   " + attrname + ": " + old + " -> " + str);
+		elem.setAttribute(attrname, str);
+	}
+
+	private void setNillableString(Element elem, String attrname, String str) {
+		if (str == null || str.length() == 0) {
+			if (elem.hasAttribute(attrname)) {
+				log("   " + attrname + ": " + elem.getAttribute(attrname) + " gets deleted");
+				elem.removeAttribute(attrname);
+			}
+		} else
+			setString(elem, attrname, str);
+	}
+
+	private String parseNillableString(Element elem, String attrname) {
+		String str = elem.getAttribute(attrname);
+		if (str.length() == 0)
+			return null;
+		else
+			return str;
+	}
+
+	private static String formatCalendar(Calendar cal) {
+		DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.GERMAN);
+		return df.format(cal.getTime());
+	}
+
+	private void setNillableCalendar(Element user, String attr, Calendar cal) {
+		setNillableString(user, attr, (cal == null) ? null : formatCalendar(cal));
+	}
+
+	private static Calendar parseCalendar(String str) {
+		Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
+		try {
+			cal.setTime(DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.GERMAN).parse(str));
+		} catch (java.text.ParseException ex) {
+			System.out.println("Illegal date: \"" + str + "\"");
+			cal.set(1970, 0, 1); // set an error date (1.1.1970)
+			System.out.println("Set to " + formatCalendar(cal));
+		}
+		return cal;
+	}
+
+	private static Calendar parseCalendar(Element elem, String attrname) {
+		return parseCalendar(elem.getAttribute(attrname));
+	}
+
+	private static Calendar parseNillableCalendar(String str) {
+		if (str.length() == 0)
+			return null;
+		else
+			return parseCalendar(str);
+	}
+
+	public static Calendar parseNillableCalendar(Element elem, String attrname) {
+		return parseNillableCalendar(elem.getAttribute(attrname));
+	}
+
+	public static int parseInt(Element elem, String attrname) {
+		int val;
+		try {
+			val = Integer.parseInt(elem.getAttribute(attrname));
+		} catch (NumberFormatException ex) {
+			System.out.println("Illegal " + attrname + " int: \"" + elem.getAttribute(attrname) + "\"");
+			val = -1;
+		}
+		return val;
+	}
+
+	public synchronized int getState(String name) {
+		Element user = getUserXMLElement(name);
+		int val = parseInt(user, "state");
+		if (val < 0)
+			return USER_INVALID;
+		else
+			return val;
+	}
+
+	public synchronized UserInfo getUserInfo(String username, int leftSlots) {
+		Element elem = getUserXMLElement(username);
+		Calendar expires = parseNillableCalendar(elem, "expires");
+		String email = elem.getAttribute("email");
+		String emailSecond = parseNillableString(elem, "emailSecond");
+		String homepage = parseNillableString(elem, "homepage");
+		return new UserInfo(leftSlots, expires, email, emailSecond, homepage);
+	}
+
+	public synchronized void updateUserInfo(String username, String newpassword, String newemailsecond, String newhomepage)
+			throws JPlagException {
+		Element elem = (Element) getUserXMLElement(username);
+
+		log("[" + new Date() + "] " + username + " called updateUserInfo:");
+
+		if (newpassword != null) {
+			if (newpassword.length() < 6)
+				throw new JPlagException("updateUserInfo", "The password is too short!", "The password must have at least 6 characters!");
+			setString(elem, "password", newpassword);
+			synchronized (userPassListMutex) {
+				userPassList.put(username, new PassAndState(newpassword, parseInt(elem, "state")));
+			}
+		}
+
+		if (newemailsecond != null)
+			setNillableString(elem, "emailSecond", newemailsecond);
+
+		if (newhomepage != null)
+			setNillableString(elem, "homepage", newhomepage);
+
+		writeXMLFile();
+	}
+
+	/**
+	 * If isGroupAdmin is false, it returns a list of all users.<br>
+	 * If isGroupAdmin is true, it returns a list of all users he created with
+	 * the password set to an empty string.<br>
+	 * 
+	 * @return A list of userdata elements in form of an ArrayOfUserData element
+	 */
+	public synchronized UserDataArray getUserDataArray(String calling_username, boolean isGroupAdmin) throws JPlagException {
+		NodeList userList = rootElement.getElementsByTagName("user");
+		int num;
+		if (isGroupAdmin) {
+			num = 0;
+			for (int i = 0; i < userList.getLength(); i++)
+				if (((Element) userList.item(i)).getAttribute("createdBy").equals(calling_username))
+					num++;
+		} else
+			num = userList.getLength();
+		UserData[] data = new UserData[num];
+		for (int i = 0, j = 0; i < userList.getLength(); i++) {
+			Element elem = (Element) userList.item(i);
+			if (!isGroupAdmin || elem.getAttribute("createdBy").equals(calling_username)) {
+				data[j] = new UserData();
+				data[j].setUsername(elem.getAttribute("username"));
+				data[j].setPassword(isGroupAdmin ? "" : elem.getAttribute("password"));
+
+				data[j].setCreated(parseCalendar(elem, "created"));
+				data[j].setCreatedBy(elem.getAttribute("createdBy"));
+				data[j].setExpires(parseNillableCalendar(elem, "expires"));
+				data[j].setLastUsage(parseNillableCalendar(elem, "lastUsage"));
+
+				data[j].setNumOfSubs(parseInt(elem, "numOfSubs"));
+				data[j].setRealName(elem.getAttribute("realname"));
+				data[j].setEmail(elem.getAttribute("email"));
+				data[j].setEmailSecond(parseNillableString(elem, "emailSecond"));
+				data[j].setHomepage(parseNillableString(elem, "homepage"));
+				data[j].setReason(elem.getAttribute("reason"));
+				data[j].setNotes(parseNillableString(elem, "notes"));
+				data[j].setState(parseInt(elem, "state"));
+				j++;
+			}
+		}
+		return new UserDataArray(data);
+	}
+
+	/**
+	 * This function only creates a new user element. It does only initialise
+	 * "createdBy", "numOfSubs" and "state"!
+	 */
+	private Element getNewUserElement(String creator) {
+		Element newuser = doc.createElement("user");
+		newuser.setAttribute("createdBy", creator);
+		newuser.setAttribute("numOfSubs", "0");
+		newuser.setAttribute("state", USER_NORMAL + "");
+		Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+		newuser.setAttribute("created", formatCalendar(cal));
+
+		return newuser;
+	}
+
+	/**
+	 * Sets userdata fields, adds a new user or deletes one. Group admins can
+	 * only change their "own" users and can not change the "createdBy" and
+	 * "state" fields. For JPlag admins the "createdBy" field has to point to a
+	 * valid user!
+	 */
+	public synchronized void setUserData(UserData data, String oldUsername, String calling_username, boolean isGroupAdmin)
+			throws JPlagException {
+		Element user;
+
+		log("[" + new Date() + "] " + calling_username + " called setUserData " + "for user " + oldUsername + " :");
+
+		if (oldUsername == null) // create a new user?
+		{
+			if (data.getUsername() == null)
+				throw new JPlagException("setUserData", "Illegal parameters! " + "oldUsername and username is null!", "Find out what "
+						+ "you want, create an user or delete one!");
+
+			if (!isValidUsername(data.getUsername()))
+				throw new JPlagException("setUserData", "Illegal new username!", "The username may only consist of the "
+						+ "following characters: 'A'-'Z', 'a'-'z', '0'-'9', " + "'@' and '.'");
+
+			if (exists(data.getUsername()))
+				throw new JPlagException("setUserData", "Adding a new user " + "with an already existing name!", "Try another name!");
+
+			log("  " + calling_username + " generates new user " + data.getUsername());
+			user = getNewUserElement(calling_username);
+		} else
+			user = getUserXMLElement(oldUsername);
+		if (user == null)
+			throw new JPlagException("setUserData", "User doesn't exist (anymore?)!", "Please update the user list!");
+
+		if (data.getUsername() == null) // delete user username?
+		{
+			if (isGroupAdmin && !calling_username.equals(user.getAttribute("createdBy"))) {
+				// log invalid access, wait a bit and throw an exception
+				System.out.println("setUserData: group admin " + calling_username + " tried to delete user " + oldUsername + " on "
+						+ new Date());
+				try {
+					Thread.sleep(10);
+				} catch (Exception ex) {
+				}
+				throw new JPlagException("setUserData", "You are not allowed" + "to delete this user!", "You can only delete users you"
+						+ " created! This has been logged.");
+			}
+			log("  deletes user " + oldUsername + " (real name: " + user.getAttribute("realname") + " email: " + user.getAttribute("email")
+					+ ")");
+			synchronized (userPassListMutex) {
+				userPassList.remove(oldUsername);
+			}
+			AccessStructure[] accStructs = JPlagCentral.listAccessStructures(oldUsername);
+			for (int i = 0; i < accStructs.length; i++)
+				JPlagCentral.cancelSubmission(accStructs[i]);
+			rootElement.removeChild(user);
+		} else {
+			if (isGroupAdmin && !calling_username.equals(user.getAttribute("createdBy"))) {
+				// log invalid access, wait a bit and throw an exception
+				System.out.println("setUserData: group admin " + calling_username + " tried to change fields for user "
+						+ data.getUsername() + " on " + new Date());
+				try {
+					Thread.sleep(10);
+				} catch (Exception ex) {
+				}
+				throw new JPlagException("setUserData", "You are not allowed" + "to change this user!", "You can only change users you"
+						+ " created! This has been logged.");
+			}
+			if (!user.getAttribute("username").equals(data.getUsername())) {
+				if (!isValidUsername(data.getUsername()))
+					throw new JPlagException("setUserData", "Illegal new username!", "The username may only consist of the "
+							+ "following characters: 'A'-'Z', 'a'-'z', " + "'0'-'9', '@' and '.'");
+
+				if (exists(data.getUsername()))
+					throw new JPlagException("setUserData", "Changing an username to an already existing name!", "Try another name!");
+
+				setString(user, "username", data.getUsername());
+				synchronized (userPassListMutex) {
+					userPassList.remove(oldUsername);
+					userPassList.put(data.getUsername(), new PassAndState(user.getAttribute("password"), parseInt(user, "state")));
+				}
+			}
+			if (data.getPassword().length() != 0 && !user.getAttribute("password").equals(data.getPassword())) {
+				setString(user, "password", data.getPassword());
+				synchronized (userPassListMutex) {
+					userPassList.put(user.getAttribute("username"), new PassAndState(data.getPassword(), parseInt(user, "state")));
+				}
+			}
+			setNillableCalendar(user, "expires", data.getExpires());
+			setString(user, "realname", data.getRealName());
+			setString(user, "email", data.getEmail());
+			setNillableString(user, "emailSecond", data.getEmailSecond());
+			setNillableString(user, "homepage", data.getHomepage());
+			setString(user, "reason", data.getReason());
+			setNillableString(user, "notes", data.getNotes());
+			if (!isGroupAdmin) {
+				setString(user, "created", formatCalendar(data.getCreated()));
+				setString(user, "createdBy", data.getCreatedBy());
+				setNillableCalendar(user, "lastUsage", data.getLastUsage());
+				setString(user, "numOfSubs", data.getNumOfSubs() + "");
+				if (parseInt(user, "state") != data.getState()) {
+					setString(user, "state", data.getState() + "");
+					synchronized (userPassListMutex) {
+						userPassList.put(user.getAttribute("username"), new PassAndState(user.getAttribute("password"), data.getState()));
+					}
+				}
+			} else {
+				if (parseInt(user, "state") != data.getState()) {
+					switch (data.getState() & ~MASK_DEVELOPER) {
+					case USER_NORMAL:
+					case MASK_EXPIRED:
+					case MASK_DEACTIVATED:
+						setString(user, "state", data.getState() + "");
+						synchronized (userPassListMutex) {
+							userPassList.put(user.getAttribute("username"),
+									new PassAndState(user.getAttribute("password"), data.getState()));
+						}
+						break;
+					default:
+						// log illegal state
+						System.out.println("setUserData: group admin " + calling_username + " tried to set " + data.getUsername()
+								+ "'s state to " + data.getState() + " on " + new Date());
+						break;
+					}
+				}
+			}
+			if (oldUsername == null) // create new user?
+				rootElement.appendChild(user);
+			checkExpired(user);
+		}
+		writeXMLFile();
+	}
+
+	private boolean updateCalendar(Element elem) {
+		Calendar curcal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
+		Calendar cal = parseCalendar(elem, "lastUsage");
+		if (curcal.get(Calendar.DAY_OF_MONTH) != cal.get(Calendar.DAY_OF_MONTH) || curcal.get(Calendar.MONTH) != cal.get(Calendar.MONTH)
+				|| curcal.get(Calendar.YEAR) != cal.get(Calendar.YEAR)) {
+			elem.setAttribute("lastUsage", formatCalendar(curcal));
+			return true;
+		}
+		return false;
+	}
+
+	public synchronized void updateLastUsage(String username) {
+		Element elem = getUserXMLElement(username);
+		if (updateCalendar(elem))
+			writeXMLFile();
+	}
+
+	public synchronized void incrementSubmissionCounter(String username) {
+		Element elem = getUserXMLElement(username);
+		int subs = parseInt(elem, "numOfSubs");
+		elem.setAttribute("numOfSubs", (subs + 1) + "");
+		updateCalendar(elem); // just in case a client runs a few months ;)
+		writeXMLFile();
+	}
+
+	/**
+	 * Called by an AdminTool client from a user with administrator rights.
+	 * Accepts or declines a specified request
+	 */
+	public synchronized boolean finishAccountRequest(FinishRequestData data, String adminname) throws JPlagException {
+		RequestData rd = requestAdmin.getRequestData(data.getOldUsername());
+		if (rd == null)
+			throw new JPlagException("requestAccount", "Request doesn't exist (anymore)!",
+					"Wrong username or this request has already been decided!");
+
+		if (data.getPassword() != null) // user not dismissed?
+		{
+			// User accepted, add to user database
+			UserData ud = new UserData(data.getUsername(), data.getPassword(), new GregorianCalendar(TimeZone.getTimeZone("GMT")),
+					adminname, data.getExpires(), null, 0, data.getRealName(), data.getEmail(), data.getEmailSecond(), data.getHomepage(),
+					data.getReason(), data.getNotes(), data.getState());
+			setUserData(ud, null, adminname, false);
+		}
+
+		if (data.getMailSubject() != null && data.getMailSubject().length() != 0) {
+			// inform user about acception or dismiss
+			sendMail(data.getEmail(), data.getMailSubject(), data.getMailMessage());
+		}
+
+		// Remove request from request list
+		requestAdmin.removeRequest(data.getOldUsername());
+		return true;
+	}
+
+	/**
+	 * Called by the server page. - If everything but username is null, it
+	 * returns, whether the username is OK (true) or it is already used (false).
+	 * - If everything but password is null and password is the email validation
+	 * code sent to the user, the request becomes available to the AdminTool and
+	 * an email is sent to the administrator, if the code is wrong, false is
+	 * returned. - If username, password, realname, email and reason is not
+	 * null, it saves the data and sends a validation email to the user. -
+	 * Otherwise an exception is thrown.
+	 */
+	public synchronized boolean requestAccount(RequestData data) throws JPlagException {
+		if (data.getRealName() == null && data.getEmail() == null && data.getEmailSecond() == null && data.getHomepage() == null
+				&& data.getReason() == null && data.getNotes() == null) {
+			if (data.getPassword() == null && data.getUsername() != null) {
+				if (exists(data.getUsername()))
+					return false;
+				else
+					return !requestAdmin.exists(data.getUsername());
+			} else if (data.getPassword() != null && data.getUsername() == null) {
+				String username = requestAdmin.validateRequest(data.getPassword());
+				RequestData rd = requestAdmin.getRequestData(username);
+
+				// Send notification messages to all administrators with
+				// notification to make them notice the new validated request
+
+				NodeList userList = rootElement.getElementsByTagName("user");
+				for (int i = 0; i < userList.getLength(); i++) {
+					Element elem = (Element) userList.item(i);
+					if ((parseInt(elem, "state") & MASK_REQUESTNOTIFY) != 0) {
+						sendMail(elem.getAttribute("email"), MailTemplateAdmin.SERVER_REQUESTNOTIFY, rd);
+					}
+				}
+				return true;
+			}
+		}
+
+		if (data.getUsername() == null || data.getPassword() == null || data.getRealName() == null || data.getEmail() == null
+				|| data.getReason() == null)
+			throw new JPlagException("requestAccount", "Username, password, " + "real name, primary email or reason is missing!",
+					"Check your input!");
+
+		if (!isValidUsername(data.getUsername()))
+			throw new JPlagException("requestAccount", "Illegal new username!", "The username may only consist of the "
+					+ "following characters: 'A'-'Z', 'a'-'z', " + "'0'-'9', '@' and '.'");
+
+		if (exists(data.getUsername()) || requestAdmin.exists(data.getUsername()))
+			throw new JPlagException("requestAccount", "Username already used!", "Seems like somebody else was faster ;)");
+
+		// Everything's fine, so add request to unverificated request list
+		// and send a verification mail
+
+		String vericode = requestAdmin.addRequest(data);
+		sendMail(data.getEmail(), MailTemplateAdmin.SERVER_VERIFICATION, data, "code", vericode);
+
+		return true;
+	}
+
+	public void extendAccount(String extendCode) throws JPlagException {
+		if (extendCode.length() >= 12) // username must be at least one character
+		{
+			String username = extendCode.substring(11);
+			System.out.println("[" + new Date() + "] extendAccount: username=" + username + " code=" + extendCode);
+
+			Element userElem = getUserXMLElement(username);
+			if (userElem != null) {
+				Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+				if (userElem.getAttribute("askExtendCode").equals(extendCode)) {
+					cal.add(Calendar.YEAR, 1);
+					setNillableCalendar(userElem, "expires", cal);
+					userElem.removeAttribute("expWarned");
+					userElem.removeAttribute("askExtendCode");
+					return;
+				} else {
+					Calendar expires = parseNillableCalendar(userElem, "expires");
+					if (expires != null) {
+						Calendar next2Week = (Calendar) cal.clone();
+						next2Week.add(Calendar.DATE, 14);
+						if (expires.after(next2Week)) {
+							throw new JPlagException("extendAccount", "Your account has already been extended!",
+									"You probably opened this page more than once and missed the result of the first time.");
+						}
+					}
+				}
+			}
+		}
+		throw new JPlagException("extendAccount", "Wrong extend code given: " + extendCode,
+				"Please check the correct spelling of the code!");
+	}
+
+	/**
+	 * Sends a specified email to all developers
+	 */
+	public void notifyDevelopers(String subject, String message) throws JPlagException {
+		NodeList userList = rootElement.getElementsByTagName("user");
+		for (int i = 0; i < userList.getLength(); i++) {
+			Element elem = (Element) userList.item(i);
+			if ((parseInt(elem, "state") & MASK_DEVELOPER) != 0)
+				sendMail(elem.getAttribute("email"), subject, message);
+		}
+	}
+
+	/**
+	 * Sets the developer flag for the specified user according to developer
+	 */
+	public synchronized void setDeveloperState(String username, boolean developer) throws JPlagException {
+		Element user = getUserXMLElement(username);
+		if (user == null)
+			throw new JPlagException("setDeveloperState", "User doesn't exist (anymore?)!", "Please update the user list!");
+
+		int state = parseInt(user, "state");
+
+		if (developer) {
+			if ((state & MASK_DEVELOPER) != 0)
+				throw new JPlagException("setDeveloperState", "You are already marked as a developer!", "Nothing changed.");
+			state |= MASK_DEVELOPER;
+		} else {
+			if ((state & MASK_DEVELOPER) == 0)
+				throw new JPlagException("setDeveloperState", "You are not marked as a developer!", "Nothing changed.");
+			state &= ~MASK_DEVELOPER;
+		}
+		user.setAttribute("state", state + "");
+		synchronized (userPassListMutex) {
+			PassAndState pas = (PassAndState) userPassList.get(username);
+			pas.state = state;
+		}
+		writeXMLFile();
+	}
+
+	public void stopUserAdmin() {
+		doStopUserAdmin = true;
+	}
+
+	/**
+	 * 
+	 * @param elem
+	 *            User element to be checked
+	 * @param cal
+	 *            Current date as calendar
+	 * @param next2Week
+	 *            Date after which a expiration notice should be sent
+	 */
+	private void checkExpired(Element elem, Calendar cal, Calendar next2Week) {
+		Calendar expires = parseNillableCalendar(elem, "expires");
+		if (expires != null) {
+			int state = parseInt(elem, "state");
+			if ((state & (MASK_EXPIRED | MASK_DEACTIVATED)) == 0) {
+				if (cal.after(expires)) {
+					System.out.println("[" + new Date() + "] User " + elem.getAttribute("username") + " expired (expires="
+							+ elem.getAttribute("expires") + ")");
+					elem.setAttribute("state", (state | MASK_EXPIRED) + "");
+					synchronized (userPassListMutex) {
+						PassAndState pas = (PassAndState) userPassList.get(elem.getAttribute("username"));
+						pas.state = state | MASK_EXPIRED;
+					}
+					try {
+						sendMail(elem.getAttribute("email"), MailTemplateAdmin.SERVER_EXPIRED, elem);
+					} catch (JPlagException ex) {
+						System.out.println("Unable to send \"expired\" mail (" + ex.getExceptionType() + ": " + ex.getDescription() + "\n"
+								+ ex.getRepair());
+						ex.printStackTrace();
+					}
+				} else if (next2Week.after(expires)) {
+					if (!elem.hasAttribute("expWarned")) {
+						if ((state & MASK_NOAUTOASKEXTEND) == 0 && parseInt(elem, "numOfSubs") != 0) {
+							System.out.println("[" + new Date() + "] User " + elem.getAttribute("username")
+									+ " expires within two weeks! Asking for extend.");
+							try {
+								String extendCode = RequestAdmin.getRandomCode() + elem.getAttribute("username");
+								sendMail(elem.getAttribute("email"), MailTemplateAdmin.SERVER_ASKEXTEND, elem, "code", extendCode);
+								elem.setAttribute("expWarned", "");
+								elem.setAttribute("askExtendCode", extendCode);
+							} catch (JPlagException ex) {
+								System.out.println("Unable to send \"askExtend" + "\" mail (" + ex.getExceptionType() + ": "
+										+ ex.getDescription() + "\n" + ex.getRepair());
+								ex.printStackTrace();
+							}
+						} else {
+							System.out.println("[" + new Date() + "] User " + elem.getAttribute("username") + " expires within two weeks!");
+							try {
+								sendMail(elem.getAttribute("email"), MailTemplateAdmin.SERVER_WARNEXPIRE, elem);
+								elem.setAttribute("expWarned", "");
+							} catch (JPlagException ex) {
+								System.out.println("Unable to send \"warnExpire" + "\" mail (" + ex.getExceptionType() + ": "
+										+ ex.getDescription() + "\n" + ex.getRepair());
+								ex.printStackTrace();
+							}
+						}
+					}
+					return;
+				}
+			}
+		}
+		elem.removeAttribute("expWarned");
+		elem.removeAttribute("askExtendCode");
+	}
+
+	private void checkExpired(Element elem) {
+		Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+		Calendar next2Week = (Calendar) cal.clone();
+		next2Week.add(Calendar.DATE, 14);
+		checkExpired(elem, cal, next2Week);
+	}
+
+	public void run() {
+		System.out.println("[" + new Date() + "] UserAdministration started");
+		try {
+			while (!doStopUserAdmin) {
+				// remove expired users				
+				Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+				Calendar next2Week = (Calendar) cal.clone();
+				next2Week.add(Calendar.DATE, 14);
+				synchronized (this) {
+					NodeList userList = rootElement.getElementsByTagName("user");
+					for (int i = userList.getLength() - 1; i >= 0; i--) {
+						Element elem = (Element) userList.item(i);
+						checkExpired(elem, cal, next2Week);
+					}
+					writeXMLFile();
+				}
+
+				// remove expired requests
+				requestAdmin.removeExpiredUnvalidatedRequests();
+
+				// get new current time
+				cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+				long nowMillis = cal.getTimeInMillis();
+				try {
+					// wait until next midnight
+					long sleepTime = ONEDAY - (nowMillis + ONEDAY - 1) % ONEDAY;
+
+					String sleepTimeStr = ((sleepTime / 3600000 > 0) ? (sleepTime / 3600000) + " h " : "")
+							+ ((sleepTime / 60000 > 0) ? ((sleepTime / 60000) % 60) + " min " : "") + (sleepTime / 1000 % 60) + " sec";
+
+					System.out.println("[" + new Date() + "] UserAdmin: I'll " + "sleep for " + sleepTimeStr);
+					Thread.sleep(sleepTime);
+				} catch (InterruptedException ex) {
+				}
+			}
+		} catch (Exception ex) {
+			ex.printStackTrace();
+		}
+		synchronized (this) {
+			logWriter.close();
+			mailLogWriter.close();
+		}
+		System.out.println("[" + new Date() + "] UserAdministration stopped!");
+	}
+
+	private class MailAuthenticator extends Authenticator {
+		String username, password;
+
+		public MailAuthenticator(String username, String password) {
+			super();
+			this.password = password;
+			this.username = username;
+		}
+
+		public PasswordAuthentication getPasswordAuthentication() {
+			return new PasswordAuthentication(this.username, password);
+		}
+	}
+
+	private MailTemplate evalMailTemplate(String name, MailTemplateData data) {
+		MailTemplateAdmin mailTemplateAdmin = JPlagCentral.getInstance().getMailTemplateAdmin();
+		MailTemplate template = mailTemplateAdmin.getMailTemplate(MailTemplateAdmin.MAIL_SERVER, name);
+		if (template == null)
+			return null;
+
+		// Evaluate subject
+		template.setSubject(mailTemplateAdmin.evalTemplateString(template.getSubject(), data));
+
+		// Evaluate message body
+		template.setData(mailTemplateAdmin.evalTemplateString(template.getData(), data));
+
+		return template;
+	}
+
+	private void sendMail(String destMail, String name, MailTemplateData data) throws JPlagException {
+		MailTemplate mail = evalMailTemplate(name, data);
+		if (mail == null) {
+			System.out.println("UserAdmin.sendMail(): Unable to get \"" + name + "\" message!!");
+			throw new JPlagException("UserAdminException", "Internal server error: Unable to find mail template!",
+					"Please contact the JPlag admin!");
+		} else
+			sendMail(destMail, mail.getSubject(), mail.getData());
+	}
+
+	private void sendMail(String destMail, String name, RequestData data, String paramname, String param) throws JPlagException {
+		MailTemplateData tempData = new MailTemplateData(data);
+		if (paramname != null)
+			tempData.put(paramname, param);
+		sendMail(destMail, name, tempData);
+	}
+
+	private void sendMail(String destMail, String name, RequestData data) throws JPlagException {
+		sendMail(destMail, name, new MailTemplateData(data));
+	}
+
+	private void sendMail(String destMail, String name, Element elem, String paramname, String param) throws JPlagException {
+		MailTemplateData tempData = new MailTemplateData(elem);
+		if (paramname != null)
+			tempData.put(paramname, param);
+		sendMail(destMail, name, tempData);
+	}
+
+	private void sendMail(String destMail, String name, Element elem) throws JPlagException {
+		sendMail(destMail, name, new MailTemplateData(elem));
+	}
+
+	public void sendMail(String destMail, String subject, String message) throws JPlagException {
+		Properties mailProps = PropertiesLoader.loadProps("jplagWebService/serverAccess/mailconfig.properties");
+		
+		// Gets the System properties
+		Properties l_props = System.getProperties();
+
+		// Puts the SMTP server name to properties object
+		l_props.put("mail.smtp.host", mailProps.getProperty("mail.smtp.host", "smtp.ira.uni-karlsruhe.de"));
+		l_props.put("mail.smtp.auth", mailProps.getProperty("mail.smtp.auth", "true"));
+		l_props.put("mail.smtp.starttls.enable", mailProps.getProperty("mail.smtp.starttls.enable", "true"));
+		l_props.put("mail.SSLSocketFactory.class",
+				mailProps.getProperty("mail.SSLSocketFactory.class", "jplagWebService.serverAccess.TrustAllSSLSocketFactory"));
+
+		// Get the default Session using Properties Object
+		Session session = Session.getInstance(l_props, new MailAuthenticator(
+				mailProps.getProperty("mail.smtp.auth.user", "jplag"),
+				mailProps.getProperty("mail.smtp.auth.pass", "zentis!?")));
+
+		//session.setDebug(true); // Enable the debug mode
+
+		try {
+			MimeMessage msg = new MimeMessage(session); // Create a New message
+
+			msg.setFrom(new InternetAddress(mailProps.getProperty("from_address", "smtp.ira.uni-karlsruhe.de")));
+			msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(destMail, false));
+			msg.setSubject(subject, "UTF-8");
+
+			msg.setContent(message, "text/plain; charset=UTF-8");
+
+			msg.setSentDate(new Date());
+
+			// Send the message
+			Transport.send(msg);
+
+			// If here, then message is successfully sent.
+		} catch (Exception e) {
+			System.out.println("Exception in sendMail: destMail=" + destMail);
+			e.printStackTrace();
+			throw new JPlagException("sendMail", "Exception caught: " + e.toString(), "Does this help?");
+		}
+		synchronized (this) {
+			if (mailLogWriter != null)
+				mailLogWriter.println("[" + new Date() + "] Sent the following " + "mail to " + destMail + ":\n\nSubject: " + subject
+						+ "\n\nMessage: " + message);
+		}
+	}
+}

+ 362 - 0
jplag.webService/src/main/java/jplagWebService/serverImpl/JPlagCentral.java

@@ -0,0 +1,362 @@
+/*
+ * Created on 01.03.2005
+ */
+
+package jplagWebService.serverImpl;
+
+import java.io.File;
+import java.util.Date;
+
+import jplag.Program;
+import jplag.options.CommandLineOptions;
+import jplag.options.util.ZipUtil;
+import jplagWebService.server.JPlagException;
+import jplagWebService.server.UserInfo;
+import jplagWebService.serverAccess.AccessStructure;
+import jplagWebService.serverAccess.MailTemplateAdmin;
+import jplagWebService.serverAccess.MemoryManager;
+import jplagWebService.serverAccess.ResultAdmin;
+import jplagWebService.serverAccess.StatusDecorator;
+import jplagWebService.serverAccess.TransferManager;
+import jplagWebService.serverAccess.UserAdmin;
+
+/**
+ * @author Moritz Kroll, Emeric Kwemou
+ */
+public class JPlagCentral extends Thread {
+	/**
+	 * The JPlagCentral singleton object
+	 */
+	private static JPlagCentral JPLAG_CENTRAL = null;
+
+	/*
+	 * Some constants
+	 */
+	public static final int READYQUEUE = ResultAdmin.SEARCH_ENTRIES;
+	public static final int TERMINATEDQUEUE = ResultAdmin.SEARCH_RESULTS;
+	public static final int ALLQUEUES = ResultAdmin.SEARCH_ALL;
+	
+	private static final int MAX_SUBMISSIONS_ON_SERVER=5;
+
+	/**
+	 * The currently processed submission structure
+	 */
+	protected static AccessStructure activeStruct = null;
+	
+	/**
+	 * If set to true forces the main thread to be ended
+	 */
+	private static boolean doStopCentral = false;
+	
+	/**
+	 * A singleton ResultAdministration object
+	 */
+    private static ResultAdmin resultAdmin = null;
+    
+    /**
+     * A singleton UserAdmin object
+     */
+    private static UserAdmin userAdmin = null;
+	
+	/**
+	 * A singleton MailTemplateAdmin object
+	 */
+	private static MailTemplateAdmin mailTemplateAdmin = null;
+    
+    /**
+     * A singleton TransferManager object
+     */
+    private static TransferManager transferManager = null;
+    
+    private MemoryManager memoryManager;
+
+    /**
+     * The private constructor to be used to construct the singleton object.
+     * Sets up the MemoryManager and ResultAdministration objects
+     */
+	private JPlagCentral() {
+		memoryManager = MemoryManager.giveInstance();
+        memoryManager.setManager_enabled(true);
+
+		resultAdmin = new ResultAdmin(AccessStructure.getJPLAG_DIRECTORY());
+		userAdmin = new UserAdmin(AccessStructure.getJPLAG_DIRECTORY());
+		mailTemplateAdmin = new MailTemplateAdmin(
+				AccessStructure.getJPLAG_DIRECTORY());
+        transferManager = new TransferManager();
+	}
+
+	/**
+ 	 * @return A singleton object of JPlagCentral
+	 */
+	public static JPlagCentral getInstance() {
+		if (JPLAG_CENTRAL == null) JPLAG_CENTRAL = new JPlagCentral();
+		return JPLAG_CENTRAL;
+	}
+	
+	public UserAdmin getUserAdmin() {
+		return userAdmin;
+	}
+	
+	public MailTemplateAdmin getMailTemplateAdmin() {
+		return mailTemplateAdmin;
+	}
+    
+    public TransferManager getTransferManager() {
+        return transferManager;
+    }
+
+	public static synchronized AccessStructure search(int select,
+			String submissionID)
+    {
+		if (activeStruct != null
+				&& activeStruct.getSubmissionID().compareTo(submissionID) == 0)
+		{
+			activeStruct.getDecorator().setState(
+					activeStruct.getOption().getState());
+			activeStruct.getDecorator().setProgress(
+					activeStruct.getOption().getProgress());
+			return activeStruct;
+		}
+		return resultAdmin.getAccessStruct(submissionID,
+				select);
+	}
+
+	public static synchronized void addToReadyQueue(AccessStructure struct) {
+        struct.getDecorator().setState(StatusDecorator.WAITING_IN_QUEUE);
+        struct.getDecorator().setProgress(resultAdmin.getNumEntries());
+		resultAdmin.addEntry(struct);
+	}
+
+    // TODO: rename this function!
+	public static synchronized void addToTerminatedQueue(AccessStructure struct)
+	{
+		if(struct==activeStruct) activeStruct=null;
+	}
+
+	public static synchronized void addToTerminatedQueue(AccessStructure struct,
+			int state,int progress,String message)
+    {
+		struct.getDecorator().add(state,progress,message);
+		if(struct==activeStruct) activeStruct=null;
+	}
+	
+	private static synchronized AccessStructure getNextReadyEntry() {
+		activeStruct = resultAdmin.getNextEntry();
+		return activeStruct;
+	}
+
+	/**
+	 * @return If the submissionID belongs to the activeStruct it returns
+	 * 		   its state, otherwise it returns 0
+	 */
+	public static synchronized int getStateIfActive(String user,String subID) {
+		if(activeStruct!=null && activeStruct.getSubmissionID().equals(subID)
+				&& activeStruct.getUsername().equals(user))
+			return activeStruct.getOption().getState();
+		return 0;
+	}
+	
+	/**
+	 * Stops the JPlagCentral by setting some stop flags
+	 */
+	public synchronized void stopCentral() {
+        doStopCentral = true;
+		if(activeStruct!=null) activeStruct.getOption().forceProgramToStop();
+		if(memoryManager==null)
+		{
+			// XXX: Sometimes stopCentral() is called several times??
+			System.out.println("Memory manager has already been deleted!?");
+			return;
+		}
+		memoryManager.setManager_enabled(false);
+		userAdmin.stopUserAdmin();
+        transferManager.stopTransferManager();
+		
+		// give threads time to complete their current work
+		try { Thread.sleep(1000); } catch(Exception e) {}
+		
+		// if still alive, interrupt any sleep loops
+		if(memoryManager.isAlive())	memoryManager.interrupt();
+		if(userAdmin.isAlive()) userAdmin.interrupt();
+        if(transferManager.isAlive()) transferManager.interrupt();
+		
+		memoryManager = null;
+		userAdmin = null;
+        transferManager = null;
+	}
+
+	/**
+	 * Main loop processing the submissions
+	 */
+	public void run() {
+		memoryManager.start();
+		userAdmin.start();
+		transferManager.start();
+		System.out.println("[" + new Date() + "] JPlagCentral started");
+
+		try {
+			Program program = null;
+			while (!doStopCentral) {
+				AccessStructure struct = getNextReadyEntry();
+				if (struct != null) {
+					CommandLineOptions opt = struct.getOption();
+
+					try {
+						System.out.println("[" + new Date() + "] New project " + "found: " + opt.root_dir);
+						struct.unzipEntry();
+
+						program = new Program(opt);
+						program.run();
+
+						File result = program.get_jplagResult();
+
+						ZipUtil.zip(result, result.getParentFile().getPath());
+						System.out.println("[" + new Date() + "] Project done, " + "saved to " + result.getParentFile().getPath());
+
+						addToTerminatedQueue(struct, StatusDecorator.COMPARE_SOURCE_DONE, 100, "");
+						struct.setDate(System.currentTimeMillis());
+						resultAdmin.addResult(struct);
+
+						program.closeWriter();
+						program = null;
+						System.gc();
+						try {
+							AccessStructure.deleteDir(result);
+						} catch (SecurityException ex) {
+							System.out.println("Not allowed to delete results!");
+						}
+						struct.deleteEntryFiles();
+					} catch (OutOfMemoryError e) {
+						// Do panic garbage collection (doing anything else
+						// would cause another OutOfMemoryError)
+						program = null;
+						System.gc();
+
+						System.out.println("OutOfMemoryError!!!");
+						e.printStackTrace(); // though there's nothing anyway
+
+						struct.getDecorator().add(jplag.ExitException.UNKNOWN_ERROR_OCCURRED, -1,
+								"Project terminated because of an OutOfMemoryError!");
+						addToTerminatedQueue(struct);
+						struct.setDate(System.currentTimeMillis());
+						resultAdmin.addResult(struct);
+					} catch (Throwable e) {
+						if (e instanceof jplag.ExitException) {
+							jplag.ExitException ex = (jplag.ExitException) e;
+							System.out.println("[" + new Date() + "] " + "ExitException occurred: State=" + ex.getState() + " Report="
+									+ ex.getReport());
+							struct.getDecorator().add(ex);
+						} else if (struct.getOption().isForceStop()) {
+							struct.getDecorator().add(jplag.options.Options.SUBMISSION_ABORTED, -1, "Submission aborted!");
+
+							System.out.println("Force stop exception occurred!");
+							e.printStackTrace();
+						} else {
+							struct.getDecorator().add(jplag.ExitException.UNKNOWN_ERROR_OCCURRED, -1,
+									"Project terminated with errors: " + e.getMessage());
+
+							System.out.println("Exception occurred!");
+							e.printStackTrace();
+						}
+						addToTerminatedQueue(struct);
+
+						/*
+						 * If exception was thrown because of an error, move
+						 * this submission into the result pool. Otherwise when
+						 * the server is going to be stopped the submission
+						 * should stay in the entry queue to be processed the
+						 * next time the server starts
+						 */
+						if (!struct.getOption().isForceStop()) {
+							struct.setDate(System.currentTimeMillis());
+							resultAdmin.addResult(struct);
+						}
+						if (program != null) {
+							program.closeWriter();
+							program = null;
+						}
+						System.gc();
+					}
+				} else {
+					try {
+						Thread.sleep(500);
+					} catch (Exception e) {
+						e.printStackTrace();
+					}
+				}
+			}
+		} catch (Throwable t) {
+			System.out.println("Unknown exception caught!!! " + t.getMessage());
+			t.printStackTrace();
+
+			if (activeStruct != null) {
+				try {
+					activeStruct.getDecorator().add(jplag.ExitException.UNKNOWN_ERROR_OCCURRED, -1,
+							"Unknown exception caught!!! " + t.getMessage());
+				} catch (Throwable t2) {
+					System.out.println("Setting activeStruct's error message " + "caused another exception! " + t2.getMessage());
+					t2.printStackTrace();
+				}
+			}
+		}
+		System.out.println("[" + new Date() + "] JPlagCentral stopped");
+	}
+
+	/**
+	 * @param struct Submission to be canceled (may be in entry queue, in the
+	 * 				 processing or an result)
+	 * @return True, if everything went fine
+	 */
+    public static boolean cancelSubmission(AccessStructure struct){
+        if(struct==activeStruct) activeStruct.getOption().forceProgramToStop();
+        resultAdmin.deleteSubmission(struct);
+        return struct.delete();        
+    }
+    
+    protected static jplagWebService.server.Submission[] listSubmissions(String username){
+        return resultAdmin.listSubmissions(username);
+    }
+    
+    public static AccessStructure[] listAccessStructures(String username){
+        return resultAdmin.listAccessStructures(username);
+    }
+    
+    public static String getNextSubmissionID(){
+        return resultAdmin.getNextSubmissionID();
+    }
+    public static String[] usersList(){
+        return resultAdmin.usersList();
+    }
+    
+    public static void checkQuota(String username) throws JPlagException {
+    	int numentries=resultAdmin.getNumUserEntries(username);
+    	int numresults=resultAdmin.getNumUserResults(username);
+		if(numentries>=MAX_SUBMISSIONS_ON_SERVER)
+		{
+			throw new JPlagException("quotaException",
+				"Too many waiting submissions ("+numentries+")!",
+				"Please wait for a submission to be finished or cancel one.");
+		}
+		if(numresults>=MAX_SUBMISSIONS_ON_SERVER)
+		{
+			throw new JPlagException("quotaException",
+				"Too many finished submissions ("+numresults+")!",
+				"Please download and delete a finished submission.");
+		}
+		if(numentries+numresults>=MAX_SUBMISSIONS_ON_SERVER)
+		{
+			throw new JPlagException("quotaException",
+				"Too many waiting and finished submissions ("+numentries+"+"
+				+ numresults+")!","Please download and delete a finished "
+				+ "submission, wait for a submission to be finished or cancel "
+				+ "a waiting submission.");
+		}
+    }
+    
+	public static UserInfo getUserInfo(String username) {
+    	int numentries=resultAdmin.getNumUserEntries(username);
+    	int numresults=resultAdmin.getNumUserResults(username);
+		int leftSlots=MAX_SUBMISSIONS_ON_SERVER-numentries-numresults;
+		return userAdmin.getUserInfo(username,leftSlots);
+	}
+}

+ 383 - 0
jplag.webService/src/main/java/jplagWebService/serverImpl/JPlagTypImpl.java

@@ -0,0 +1,383 @@
+package jplagWebService.serverImpl;
+
+/**
+ * @author Emeric Kwemou, Moritz Kroll
+ */
+
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+import java.util.Date;
+
+import javax.xml.rpc.ServiceException;
+import javax.xml.rpc.handler.soap.SOAPMessageContext;
+import javax.xml.rpc.server.ServiceLifecycle;
+import javax.xml.rpc.server.ServletEndpointContext;
+
+import jplagWebService.server.FinishRequestData;
+import jplagWebService.server.JPlagException;
+import jplagWebService.server.JPlagTyp;
+import jplagWebService.server.LanguageInfo;
+import jplagWebService.server.MailTemplateArray;
+import jplagWebService.server.NotifyDevelopersParams;
+import jplagWebService.server.RequestData;
+import jplagWebService.server.RequestDataArray;
+import jplagWebService.server.ServerInfo;
+import jplagWebService.server.SetDeveloperStateParams;
+import jplagWebService.server.SetMailTemplateParams;
+import jplagWebService.server.SetUserDataParams;
+import jplagWebService.server.StartResultDownloadData;
+import jplagWebService.server.StartSubmissionUploadParams;
+import jplagWebService.server.Status;
+import jplagWebService.server.UpdateUserInfoParams;
+import jplagWebService.server.UserDataArray;
+import jplagWebService.serverAccess.AccessStructure;
+import jplagWebService.serverAccess.JPlagServerAccessHandler;
+import jplagWebService.serverAccess.StatusDecorator;
+import jplagWebService.serverAccess.UserAdmin;
+
+public class JPlagTypImpl implements JPlagTyp, Remote, ServiceLifecycle {
+	private ServletEndpointContext servletEndpointContext = null;
+
+	/**
+	 * Singleton object. Will be initialized in the init() method
+	 */
+	private static JPlagCentral JPLAG_CENTRAL = null;
+
+	/**
+	 * Minimal time between two getStatus request of the same user.
+	 */
+	private static final long MINIMAL_DIFF_TIME = 10000;
+
+	private static final String[] javasuffixes = { ".java", ".jav", ".JAVA", ".JAV" };
+	private static final String[] schemesuffixes = { ".scm", ".SCM" };
+	private static final String[] csuffixes = { ".cpp", ".CPP", ".c++", ".C++", ".c", ".C", ".h", ".H", ".hpp", ".HPP" };
+	private static final String[] textsuffixes = { ".txt", ".TXT", ".asc", ".ASC", ".tex", ".TEX" };
+	private static final String[] cssuffixes = { ".cs", ".CS" };// @formatter:off
+	
+	public static final LanguageInfo[] languageInfos = {
+//		new LanguageInfo("java12", javasuffixes, 9),
+		new LanguageInfo("java15", javasuffixes, 8),
+		new LanguageInfo("java15dm", javasuffixes, 8),
+		new LanguageInfo("java17", javasuffixes, 8),
+		new LanguageInfo("scheme", schemesuffixes, 13),
+		new LanguageInfo("c/c++", csuffixes, 12),
+		new LanguageInfo("text", textsuffixes, 5),
+//		new LanguageInfo("char", textsuffixes, 10),
+		new LanguageInfo("c#-1.2", cssuffixes, 8) }; // @formatter:on
+
+	public static final String[] countryLanguages = { "en", "de", "fr", "es" };
+
+	/**
+	 * Initializes the web service and starts the main thread
+	 */
+	public void init(Object context) throws ServiceException {
+		servletEndpointContext = (ServletEndpointContext) context;
+
+		// ensure that entry and result directories exist
+		AccessStructure.ensureExistence();
+
+		// create and start jplagCentral thread
+		JPLAG_CENTRAL = JPlagCentral.getInstance();
+		JPLAG_CENTRAL.start();
+	}
+
+	/**
+	 * Sets a stop flag for the main thread
+	 */
+	public void destroy() {
+		servletEndpointContext = null;
+		JPLAG_CENTRAL.stopCentral();
+		JPLAG_CENTRAL = null;
+	}
+
+	private String getUsername() {
+		SOAPMessageContext smc = (SOAPMessageContext) servletEndpointContext.getMessageContext();
+		return JPlagServerAccessHandler.extractUsername(smc);
+	}
+
+	public ServerInfo getServerInfo() throws JPlagException, RemoteException {
+		String username = getUsername();
+		JPLAG_CENTRAL.getUserAdmin().updateLastUsage(username);
+		return new ServerInfo(JPlagCentral.getUserInfo(username), languageInfos, countryLanguages, JPlagCentral.listSubmissions(username));
+	}
+
+	// TODO: Perhaps get rid of the step over AccessStructure to make it clearer
+	public java.lang.String compareSource(jplagWebService.server.Option arguments, javax.mail.internet.MimeMultipart inputZipFile)
+			throws JPlagException, RemoteException {
+		String username = getUsername();
+		JPlagCentral.checkQuota(username);
+		JPLAG_CENTRAL.getUserAdmin().incrementSubmissionCounter(username);
+
+		AccessStructure struct = new AccessStructure(username, arguments, inputZipFile);
+
+		JPlagCentral.addToReadyQueue(struct);
+		return struct.getSubmissionID();
+	}
+
+	public String startSubmissionUpload(StartSubmissionUploadParams params) throws JPlagException, RemoteException {
+		if (params.getData().length < 1 || params.getData().length > 81920) {
+			System.out.println("startSubmissionUpload: data.length=" + params.getData().length);
+			throw new JPlagException("startSubmissionUploadException", "The size of the data array is invalid! Must be non empty and"
+					+ " 80 kB at maximum!", "Create correct data parts!");
+		}
+		String username = getUsername();
+		JPlagCentral.checkQuota(username);
+		JPLAG_CENTRAL.getUserAdmin().incrementSubmissionCounter(username);
+
+		AccessStructure struct = new AccessStructure(username, params.getSubmissionParams());
+
+		struct.getDecorator().setState(StatusDecorator.UPLOADING);
+
+		JPLAG_CENTRAL.getTransferManager().startUpload(struct, params.getFilesize(), params.getData());
+
+		return struct.getSubmissionID();
+	}
+
+	public int continueSubmissionUpload(byte[] data) throws JPlagException, RemoteException {
+		String username = getUsername();
+		if (data.length < 1 || data.length > 81920) {
+			System.out.println("startSubmissionUpload: data.length=" + data.length);
+			JPLAG_CENTRAL.getTransferManager().cancelUpload(username, null);
+			throw new JPlagException("continueSubmissionUploadException", "The size of the data array is invalid! Must be non empty and"
+					+ " 80 kB at maximum!", "Create correct data parts!");
+		}
+		JPLAG_CENTRAL.getTransferManager().writeNextPart(username, data);
+		return 0;
+	}
+
+	/**
+	 * @param submissionID
+	 *            : Used to retrieve the submission of this user.
+	 * @return Status object, containing all information about the state of
+	 *         operation on the submission with ID 'submissionID'
+	 * @throws jplag.server.JPlagException
+	 *             this exception occurs when a submissionID is not a valid one
+	 * @throws java.rmi.RemoteException
+	 */
+	public Status getStatus(java.lang.String submissionID) throws JPlagException, RemoteException {
+		String username = getUsername();
+
+		AccessStructure struct = JPlagCentral.search(JPlagCentral.ALLQUEUES, submissionID); // Search on all queues
+
+		/**
+		 * Here we want to solve any getStatus flooding problem. In fact, this
+		 * problem is very difficult to solve because an user could send
+		 * getStatus request through different Threads
+		 */
+
+		if (struct == null || !struct.getUsername().equals(username)) {
+			/**
+			 * In this situation, the access is denied or the submission does
+			 * not exist. JPlagTyp_Impl just waits 10 second and then sends the
+			 * exception or error message to the user.
+			 */
+
+			try {
+				Thread.sleep(MINIMAL_DIFF_TIME);
+			} catch (InterruptedException e) {
+				e.printStackTrace();
+			}
+
+			if (struct != null) {
+				System.out.println("Wrong username (" + username + ") for " + "submission ID " + submissionID);
+			}
+			throw new JPlagException("statusException", "Submission does not exist!", "Please check submission ID " + "and username!");
+		} else {
+			/**
+			 * The submission exists for this user, so check whether enough time
+			 * has elapsed since the last status request
+			 */
+			long difference_time = System.currentTimeMillis() - struct.getLastStatusRequest();
+			try {
+				if (difference_time < MINIMAL_DIFF_TIME)
+					Thread.sleep(MINIMAL_DIFF_TIME - difference_time);
+			} catch (InterruptedException e) {
+				e.printStackTrace();
+			}
+			struct.setLastStatusRequest();
+			return struct.getDecorator().getStatus();
+		}
+	}
+
+	public javax.mail.internet.MimeMultipart getResult(String submissionID) throws JPlagException, RemoteException {
+		String username = getUsername();
+
+		// Search on terminatedQueue
+		AccessStructure struct = JPlagCentral.search(JPlagCentral.TERMINATEDQUEUE, submissionID);
+		if (struct == null || !struct.getUsername().equals(username)) {
+			throw new JPlagException("resultException", "Submission does not exist!", "Please check submission ID " + "and username!");
+		}
+		Status status = struct.getDecorator().getStatus();
+
+		if (status == null) {
+			throw new JPlagException("resultException", "Unable to retrieve status for submission!",
+					"Please check submission ID and username!");
+		} else if (status.getState() >= 400) {
+			throw new JPlagException("resultException", "Submission has been completed with errors! " + "Results not available!",
+					"You need to remove the errors and submit again!");
+		} else if (status.getState() < 300) {
+			throw new JPlagException("resultException", "Submission has not been completed yet!",
+					"Please check submission ID or try again " + "later!");
+		} else {
+			// Compare source finished without an error
+			return struct.getResult();
+		}
+	}
+
+	public StartResultDownloadData startResultDownload(String submissionID) throws JPlagException, RemoteException {
+		String username = getUsername();
+
+		// Search on terminatedQueue
+		AccessStructure struct = JPlagCentral.search(JPlagCentral.TERMINATEDQUEUE, submissionID);
+		if (struct == null || !struct.getUsername().equals(username)) {
+			throw new JPlagException("resultException", "Submission does not exist!", "Please check submission ID " + "and username!");
+		}
+		Status status = struct.getDecorator().getStatus();
+
+		if (status == null) {
+			throw new JPlagException("resultException", "Unable to retrieve status for submission!",
+					"Please check submission ID and username!");
+		} else if (status.getState() >= 400) {
+			throw new JPlagException("resultException", "Submission has been completed with errors! " + "Results not available!",
+					"You need to remove the errors and submit again!");
+		} else if (status.getState() < 300) {
+			throw new JPlagException("resultException", "Submission has not been completed yet!",
+					"Please check submission ID or try again " + "later!");
+		} else {
+			// Compare source finished without an error
+			return JPLAG_CENTRAL.getTransferManager().startDownload(struct);
+		}
+	}
+
+	public byte[] continueResultDownload(int dummy) throws JPlagException, RemoteException {
+		String username = getUsername();
+		return JPLAG_CENTRAL.getTransferManager().readNextPart(username);
+	}
+
+	public int cancelSubmission(String submissionID) throws JPlagException, RemoteException {
+		String username = getUsername();
+
+		AccessStructure struct = JPlagCentral.search(JPlagCentral.ALLQUEUES, submissionID);
+		if (struct == null || !struct.getUsername().equals(username)) {
+			if (struct == null) {
+				// not in entry or result list? check up- and downloads
+				if (JPLAG_CENTRAL.getTransferManager().cancelTransfer(username, submissionID))
+					return 0;
+				return 0;
+			}
+			throw new JPlagException("cancelException", "Submission does not exist!", "Please check submission ID " + "and username!");
+		}
+		Status status = struct.getDecorator().getStatus();
+
+		if (status == null) {
+			throw new JPlagException("cancelException", "Unable to retrieve status for submission!",
+					"Please check submission ID and username!");
+		}
+		System.out.println("[" + new Date() + "] User " + username + " cancels submission " + struct.getSubmissionID());
+		JPlagCentral.cancelSubmission(struct);
+		return 0;
+	}
+
+	private void logInvalidAccessAndThrow(String methodName, String user) throws JPlagException {
+		// log invalid access, wait a bit and throw an exception
+		System.out.println(methodName + ": " + user + " tried to access this function on " + new Date());
+		try {
+			Thread.sleep(10);
+		} catch (Exception ex) {
+		}
+		throw new JPlagException(methodName, "You don't have the rights to access this function!",
+				"Your username has been logged. Go away!");
+	}
+
+	/**
+	 * Checks whether the current user has enough rights (mask parameter) to
+	 * access the function with the given name
+	 * 
+	 * @throws JPlagException
+	 *             when the user doesn't have those rights. The access will also
+	 *             be logged and the user has to wait 10 seconds.
+	 */
+	private int assertUser(String methodName, int mask) throws JPlagException {
+		String user = getUsername();
+		int userstate = JPLAG_CENTRAL.getUserAdmin().getState(user);
+
+		if ((userstate & mask) == 0)
+			logInvalidAccessAndThrow(methodName, user);
+
+		return userstate;
+	}
+
+	public UserDataArray getUserDataArray(int dummy) throws JPlagException, RemoteException {
+		int state = assertUser("getUserDataArray", UserAdmin.MASK_ANYADMIN);
+		return JPLAG_CENTRAL.getUserAdmin().getUserDataArray(getUsername(), (state & UserAdmin.MASK_JPLAGADMIN) == 0);
+	}
+
+	public int setUserData(SetUserDataParams params) throws JPlagException, RemoteException {
+		int state = assertUser("setUserData", UserAdmin.MASK_ANYADMIN);
+		JPLAG_CENTRAL.getUserAdmin().setUserData(params.getUserdata(), params.getOldUsername(), getUsername(),
+				(state & UserAdmin.MASK_JPLAGADMIN) == 0);
+		return 0;
+	}
+
+	public int updateUserInfo(UpdateUserInfoParams params) throws JPlagException, RemoteException {
+		JPLAG_CENTRAL.getUserAdmin().updateUserInfo(getUsername(), params.getNewPassword(), params.getNewEmailSecond(),
+				params.getNewHomepage());
+		return 0;
+	}
+
+	public boolean requestAccount(RequestData data) throws JPlagException, RemoteException {
+		assertUser("requestAccount", UserAdmin.USER_SERVERPAGE);
+		return JPLAG_CENTRAL.getUserAdmin().requestAccount(data);
+	}
+
+	public RequestDataArray getAccountRequests(boolean lengthOnly) throws JPlagException, RemoteException {
+		assertUser("getAccountRequests", UserAdmin.MASK_ANYADMIN);
+		return new RequestDataArray(JPLAG_CENTRAL.getUserAdmin().getRequestAdmin().getRequests(lengthOnly));
+	}
+
+	public int finishAccountRequest(FinishRequestData finishData) throws JPlagException, RemoteException {
+		assertUser("finishAccountRequest", UserAdmin.MASK_JPLAGADMIN);
+		JPLAG_CENTRAL.getUserAdmin().finishAccountRequest(finishData, getUsername());
+		return 0;
+	}
+
+	public int extendAccount(String extendCode) throws JPlagException, RemoteException {
+		assertUser("extendAccount", UserAdmin.USER_SERVERPAGE);
+		JPLAG_CENTRAL.getUserAdmin().extendAccount(extendCode);
+		return 0;
+	}
+
+	public MailTemplateArray getMailTemplates(int type) throws JPlagException, RemoteException {
+		assertUser("getMailTemplates", UserAdmin.MASK_JPLAGADMIN);
+		return new MailTemplateArray(JPLAG_CENTRAL.getMailTemplateAdmin().getMailTemplates(type));
+	}
+
+	public int setMailTemplate(SetMailTemplateParams params) throws JPlagException, RemoteException {
+		assertUser("setMailTemplate", UserAdmin.MASK_JPLAGADMIN);
+		JPLAG_CENTRAL.getMailTemplateAdmin().setMailTemplate(params.getType(), params.getTemplate());
+		return 0;
+	}
+
+	public int notifyDevelopers(NotifyDevelopersParams params) throws JPlagException, RemoteException {
+		assertUser("notifyDevelopers", UserAdmin.MASK_JPLAGADMIN);
+		JPLAG_CENTRAL.getUserAdmin().notifyDevelopers(params.getSubject(), params.getMessage());
+		return 0;
+	}
+
+	public int setDeveloperState(SetDeveloperStateParams params) throws JPlagException, RemoteException {
+		assertUser("setDeveloperState", UserAdmin.USER_SERVERPAGE);
+
+		int curstate = JPLAG_CENTRAL.getUserAdmin().getLoginState(params.getUsername(), params.getPassword());
+		if ((curstate & UserAdmin.MASK_EXPIRED) != 0) {
+			throw new JPlagException("setDeveloperState", "Access denied!",
+					"Your account has expired! Please contact the JPlag administrator to reactivate it!");
+		} else if ((curstate & UserAdmin.MASK_DEACTIVATED) != 0) {
+			throw new JPlagException("setDeveloperState", "Access denied!",
+					"Your account has been deactivated! Please contact the JPlag administrator to reactivate it!");
+		} else if (curstate == UserAdmin.USER_INVALID) {
+			throw new JPlagException("setDeveloperState", "Access denied!", "Wrong username or password!");
+		}
+		JPLAG_CENTRAL.getUserAdmin().setDeveloperState(params.getUsername(), params.isDeveloper());
+		return 0;
+	}
+}

+ 22 - 0
jplag.webService/src/main/resources/WS-ISwA.xsd

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8" ?> 
+
+<!--
+ Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+-->
+
+<!-- 
+	July, 2002
+
+	(c) Copyright 2003, The Web Services-Interoperability Organization (WS-I)
+	Download or use of this file is governed by the Policies and Bylaws of WS-I.
+
+	For more information, send email info@ws-i.org. 	
+--> 
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
+ targetNamespace="http://ws-i.org/profiles/basic/1.1/xsd"
+ xmlns:tns="http://ws-i.org/profiles/basic/1.1/xsd"> 
+	<xsd:simpleType name="swaRef"> 
+		<xsd:restriction base="xsd:anyURI" /> 
+	</xsd:simpleType> 
+</xsd:schema>

+ 7 - 0
jplag.webService/src/main/resources/jplagWebService/serverAccess/mailconfig.properties

@@ -0,0 +1,7 @@
+from_address=jplag@ipd.uni-karlsruhe.de
+mail.smtp.host=smtp.ira.uni-karlsruhe.de
+mail.smtp.auth=true
+mail.smtp.auth.user=jplag
+mail.smtp.auth.pass=zentis!?
+mail.smtp.starttls.enable=true
+mail.SSLSocketFactory.class=jplagWebService.serverAccess.TrustAllSSLSocketFactory

+ 1318 - 0
jplag.webService/src/main/webapp/WEB-INF/JPlagService.wsdl

@@ -0,0 +1,1318 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+				  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+				  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+				  xmlns:types="http://jplag.ipd.kit.edu/JPlagService/types"
+				  xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
+				  xmlns:tns="http://jplag.ipd.kit.edu/JPlagService"
+				  name="JPlagService"
+				  targetNamespace="http://jplag.ipd.kit.edu/JPlagService">
+				  
+	<wsdl:documentation>
+		Compatibility level: 4
+		
+		Simple lifecycle example (using 80 kB packets for file transfer.
+					This method allows to show the file transfer progress and
+					to abort the transfer and is .NET compatible):
+			Call "startSubmissionUpload" with a correct set of parameters
+				and the first 80 kB part of the zip file containing the files
+				and "continueSubmissionUpload" for all remaining 80 kB parts
+			Optionally call "getStatus" at a minimum intervall of 10 seconds
+				to get status information
+			Call "startResultDownload" to download the first 80 kB part of the
+				result zip file and "continueResultDownload" for all remaining
+				80 kB parts
+			 OR call "cancelSubmission" to cancel the submission if the
+				result is not needed anymore or the submission was aborted
+				because of an error
+	
+		Simple lifecycle example (using unabortable MIME file transfer):
+			Call "compareSource" with a correct set of parameters to let
+				the server work on a zipped submission
+			Optionally call "getStatus" at a minimum intervall of 10 seconds
+				to get status information
+			Call "getResult" to get the zipped HTML result files
+				and "cancelSubmission" to delete the submission from the server
+				afterwards
+			 OR call "cancelSubmission" to cancel the submission if the
+				result is not needed anymore or the submission was aborted
+				because of an error
+	</wsdl:documentation>
+				  
+	<wsdl:types>
+		<xsd:schema xmlns:wsi="http://ws-i.org/profiles/basic/1.1/xsd"
+				targetNamespace="http://jplag.ipd.kit.edu/JPlagService/types"
+				xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
+
+			<xsd:element name="JPlagException">
+				<xsd:complexType>
+					<xsd:sequence>
+						<xsd:element name="exceptionType" type="xsd:string" />
+						<xsd:element name="description"	type="xsd:string">
+							<xsd:annotation><xsd:documentation>
+								This describes what the user has to do when
+							 	an error or exception happens
+							</xsd:documentation></xsd:annotation>
+						</xsd:element>
+						<xsd:element name="repair" type="xsd:string" />
+					</xsd:sequence>
+				</xsd:complexType>
+			</xsd:element>
+			
+			<xsd:element name="Status">
+				<xsd:complexType>
+					<xsd:sequence>
+						<xsd:element name="state" type="xsd:int">
+							<xsd:annotation><xsd:documentation>
+								state values:
+									  0 = uploading
+									 50 = in queue -> progress=position in queue
+									      0 being next in queue
+									100 = parsing -> progress=percentage
+									200 = comparing -> progress=percentage
+									230 = generating result files
+									250 = packaging result
+									300 = done, result is ready for download
+									400 = general error
+									401 = error = Bad language
+									402 = error = Not enough submissions
+									403 = error = Submission aborted
+								A state greater or equal 400 indicates an error.
+								The report field will then provide more details.
+							</xsd:documentation></xsd:annotation>
+						</xsd:element>
+						<xsd:element name="progress" type="xsd:int" />
+						<xsd:element name="report" type="xsd:string" />
+					</xsd:sequence>
+				</xsd:complexType>
+			</xsd:element>
+
+			<xsd:complexType name="Option">
+				<xsd:sequence>
+					<xsd:element name="language" type="xsd:string">
+						<xsd:annotation><xsd:documentation>
+							Programming language.
+							See getServerInfo response for available languages.
+						</xsd:documentation></xsd:annotation>
+					</xsd:element>
+					
+					<xsd:element name="comparisonMode" type="xsd:int" minOccurs="0" maxOccurs="1" default="0">
+						<xsd:annotation><xsd:documentation>
+							Possible values are
+								- 0: Normal pairwise comparison between all programs,
+								     looks for maximum similarity (default).
+								- 1: Revision comparison: Only adjacent programs are compared,
+								     looks for minimum similarity
+								     ("adjacent" according to their lexicographically ordered names).
+						</xsd:documentation></xsd:annotation>
+					</xsd:element>
+					
+					<xsd:element name="minimumMatchLength"
+								 type="xsd:int">
+						<xsd:annotation><xsd:documentation>
+							Minimum match length in tokens.
+							A smaller value increases the sensitivity of
+							the comparison.
+							If set to 0 or below, a language specific
+							default value is used.
+						</xsd:documentation></xsd:annotation>
+					</xsd:element>
+					
+					<xsd:element name="suffixes" type="xsd:string"
+							minOccurs="0" maxOccurs="unbounded">
+						<xsd:annotation><xsd:documentation>
+							List of case sensitive file suffixes
+							(e.g. {".c", ".C"})
+							(default: language specific, if empty)
+						</xsd:documentation></xsd:annotation>
+					</xsd:element>
+					<xsd:element name="readSubdirs" type="xsd:boolean">
+						<xsd:annotation><xsd:documentation>
+							If true, also looks at files in subdirectories.
+						</xsd:documentation></xsd:annotation>
+					</xsd:element>
+					<xsd:element name="pathToFiles" type="xsd:string"
+								 nillable="true">
+						<xsd:annotation><xsd:documentation>
+        					Look in directories <root-dir/>/*/<pathToFiles/>
+        					for files (default: <root-dir/>/*, if null or "").
+						</xsd:documentation></xsd:annotation>
+					</xsd:element>
+					<xsd:element name="basecodeDir" type="xsd:string" 
+								 nillable="true">
+						<xsd:annotation><xsd:documentation>
+							Name of directory containing the basecode
+							(common framework) (null or "", if unused)
+						</xsd:documentation></xsd:annotation>
+					</xsd:element>
+					
+					<xsd:element name="storeMatches" type="xsd:string"
+								 nillable="true">
+						<xsd:annotation><xsd:documentation>
+							This can be either
+								- the number of matches being saved (default
+								  is 20, if null or "") or
+								- the minimum similarity percentage to be
+								  saved as a match (with a trailing "%")
+						</xsd:documentation></xsd:annotation>
+					</xsd:element>
+					<xsd:element name="clustertype" type="xsd:string"
+								 nillable="true">
+						<xsd:annotation><xsd:documentation>
+							Possible values are
+								- null or "" : no clustering (default)
+								- "min"
+								- "avr"
+								- "max"
+						</xsd:documentation></xsd:annotation>
+					</xsd:element>
+					
+					<xsd:element name="countryLang" type="xsd:string">
+						<xsd:annotation><xsd:documentation>
+							Country language the result files will be written
+							in.
+							See getServerInfo response for available country
+							languages.
+						</xsd:documentation></xsd:annotation>
+					</xsd:element>
+					
+					<xsd:element name="title" type="xsd:string">
+						<xsd:annotation><xsd:documentation>
+							Title describing the submission and being displayed
+							in the result files.
+						</xsd:documentation></xsd:annotation>
+					</xsd:element>
+					<xsd:element name="originalDir" type="xsd:string">
+						<xsd:annotation><xsd:documentation>
+							String to be displayed in the result files as the
+							directory where the source code comes from.
+						</xsd:documentation></xsd:annotation>
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:complexType>
+
+			<xsd:element name="eOption" type="types:Option" />
+			
+			<xsd:element name="StartSubmissionParams">
+				<xsd:complexType>
+					<xsd:sequence>
+						<xsd:element name="userInfo" type="types:UserInfo" />
+						<xsd:element name="languageInfos"
+							type="types:LanguageInfo" maxOccurs="unbounded" />
+						<xsd:element name="submissions" type="types:Submission"
+							minOccurs="0" maxOccurs="unbounded" />
+					</xsd:sequence>
+				</xsd:complexType>
+			</xsd:element>
+			
+			<xsd:element name="Access">
+				<xsd:complexType>
+					<xsd:sequence>
+						<xsd:element name="username" type="xsd:string" />
+						<xsd:element name="password" type="xsd:string" />
+						<xsd:element name="compatLevel" type="xsd:int" />
+					</xsd:sequence>
+				</xsd:complexType>
+			</xsd:element>
+
+			<xsd:complexType name="Submission">
+				<xsd:sequence>
+					<xsd:element name="submissionID" type="xsd:string" />
+					<xsd:element name="title" type="xsd:string" />
+					<xsd:element name="date" type="xsd:string" />
+					<xsd:element name="lastState" type="xsd:int" />
+				</xsd:sequence>
+			</xsd:complexType>
+
+			<xsd:complexType name="UserInfo">
+				<xsd:sequence>
+					<xsd:element name="leftSubmissionSlots" type="xsd:int" />
+					<xsd:element name="expires" type="xsd:dateTime"
+								 nillable="true" />
+					<xsd:element name="email" type="xsd:string" />
+					<xsd:element name="emailSecond" type="xsd:string"
+								 nillable="true" />
+					<xsd:element name="homepage" type="xsd:string"
+								 nillable="true" />
+				</xsd:sequence>
+			</xsd:complexType>
+			
+			<xsd:complexType name="LanguageInfo">
+				<xsd:sequence>
+					<xsd:element name="name" type="xsd:string" />
+					<xsd:element name="suffixes" type="xsd:string"
+						maxOccurs="unbounded" />
+					<xsd:element name="defMinMatchLen" type="xsd:int" />
+				</xsd:sequence>
+			</xsd:complexType>
+
+			<xsd:element name="ServerInfo">
+				<xsd:complexType>
+					<xsd:sequence>
+						<xsd:element name="userInfo" type="types:UserInfo" />
+						<xsd:element name="languageInfos"
+							type="types:LanguageInfo" maxOccurs="unbounded" />
+						<xsd:element name="countryLanguages"
+							type="xsd:string" maxOccurs="unbounded"/>
+						<xsd:element name="submissions" type="types:Submission"
+							minOccurs="0" maxOccurs="unbounded" />
+					</xsd:sequence>
+				</xsd:complexType>
+			</xsd:element>
+			
+			<xsd:complexType name="UserData">
+				<xsd:sequence>
+					<xsd:element name="username" type="xsd:string"
+								 nillable="true" />
+					<xsd:element name="password" type="xsd:string" />
+					<xsd:element name="created" type="xsd:dateTime" />
+					<xsd:element name="createdBy" type="xsd:string" />
+					<xsd:element name="expires" type="xsd:dateTime"
+								 nillable="true" />
+					<xsd:element name="lastUsage" type="xsd:dateTime"
+								 nillable="true" />
+					<xsd:element name="numOfSubs" type="xsd:int" />
+					<xsd:element name="realName" type="xsd:string" />
+					<xsd:element name="email" type="xsd:string" />
+					<xsd:element name="emailSecond" type="xsd:string"
+								 nillable="true" />
+					<xsd:element name="homepage" type="xsd:string"
+								 nillable="true" />
+					<xsd:element name="reason" type="xsd:string" />
+					<xsd:element name="notes" type="xsd:string"
+								 nillable="true" />
+					<xsd:element name="state" type="xsd:int" />
+				</xsd:sequence>
+			</xsd:complexType>
+			
+			<xsd:element name="eUserData" type="types:UserData" />
+			
+			<xsd:element name="UserDataArray">
+				<xsd:complexType>
+					<xsd:sequence>
+						<xsd:element name="items" type="types:UserData"
+							minOccurs="0" maxOccurs="unbounded"/>
+					</xsd:sequence>
+				</xsd:complexType>
+			</xsd:element>		
+			
+			<xsd:element name="setUserDataParams">
+				<xsd:complexType>
+					<xsd:sequence>
+						<xsd:element name="userdata" type="types:UserData" />
+						<xsd:element name="oldUsername" type="xsd:string"
+									 nillable="true" />
+					</xsd:sequence>
+				</xsd:complexType>
+			</xsd:element>
+			
+			<xsd:element name="updateUserInfoParams">
+				<xsd:complexType>
+					<xsd:sequence>
+						<xsd:element name="newPassword" type="xsd:string"
+									 nillable="true" />
+						<xsd:element name="newEmailSecond" type="xsd:string"
+									 nillable="true" />
+						<xsd:element name="newHomepage" type="xsd:string"
+									 nillable="true" />
+					</xsd:sequence>
+				</xsd:complexType>
+			</xsd:element>
+			
+			<xsd:complexType name="RequestData">
+				<xsd:sequence>
+					<xsd:element name="validateTime" type="xsd:string"
+								 nillable="true" />
+					<xsd:element name="username" type="xsd:string"
+								 nillable="true" />
+					<xsd:element name="password" type="xsd:string"
+								 nillable="true" />
+					<xsd:element name="realName" type="xsd:string"
+								 nillable="true" />
+					<xsd:element name="email" type="xsd:string"
+								 nillable="true" />
+					<xsd:element name="emailSecond" type="xsd:string"
+								 nillable="true" />
+					<xsd:element name="homepage" type="xsd:string"
+								 nillable="true" />
+					<xsd:element name="reason" type="xsd:string"
+								 nillable="true" />
+					<xsd:element name="notes" type="xsd:string"
+								 nillable="true" />
+				</xsd:sequence>
+			</xsd:complexType>
+			
+			<xsd:element name="RequestDataArray">
+				<xsd:complexType>
+					<xsd:sequence>
+						<xsd:element name="items" type="types:RequestData"
+							minOccurs="0" maxOccurs="unbounded" />
+					</xsd:sequence>
+				</xsd:complexType>
+			</xsd:element>
+			
+			<xsd:element name="eRequestData" type="types:RequestData" />
+
+			<xsd:element name="FinishRequestData">
+				<xsd:complexType>
+					<xsd:sequence>
+						<xsd:element name="oldUsername" type="xsd:string" />
+						<xsd:element name="username" type="xsd:string" />
+						<xsd:element name="password" type="xsd:string"
+									 nillable="true" />
+						<xsd:element name="expires" type="xsd:dateTime" 
+									 nillable="true" />
+						<xsd:element name="realName" type="xsd:string" />
+						<xsd:element name="email" type="xsd:string" />
+						<xsd:element name="emailSecond" type="xsd:string"
+									 nillable="true" />
+						<xsd:element name="homepage" type="xsd:string"
+									 nillable="true" />
+						<xsd:element name="reason" type="xsd:string"
+									 nillable="true" />
+						<xsd:element name="notes" type="xsd:string"
+									 nillable="true" />
+						<xsd:element name="state" type="xsd:int"
+									 nillable="true" />
+						<xsd:element name="mailSubject" type="xsd:string"
+									 nillable="true" />
+						<xsd:element name="mailMessage" type="xsd:string"
+									 nillable="true" />
+					</xsd:sequence>
+				</xsd:complexType>
+			</xsd:element>
+
+			<xsd:complexType name="MailTemplate">
+				<xsd:sequence>
+					<xsd:element name="name" type="xsd:string" />
+					<xsd:element name="subject" type="xsd:string" />
+					<xsd:element name="data" type="xsd:string" />
+				</xsd:sequence>
+			</xsd:complexType>
+
+			<xsd:element name="MailTemplateArray">
+				<xsd:complexType>
+					<xsd:sequence>
+						<xsd:element name="items" type="types:MailTemplate"
+							minOccurs="0" maxOccurs="unbounded" />
+					</xsd:sequence>
+				</xsd:complexType>
+			</xsd:element>
+
+			<xsd:element name="setMailTemplateParams">
+				<xsd:complexType>
+					<xsd:sequence>
+						<xsd:element name="type" type="xsd:int" />
+						<xsd:element name="template"
+							type="types:MailTemplate" />
+					</xsd:sequence>
+				</xsd:complexType>
+			</xsd:element>
+			
+			<xsd:element name="notifyDevelopersParams">
+				<xsd:complexType>
+					<xsd:sequence>
+						<xsd:element name="subject" type="xsd:string" />
+						<xsd:element name="message" type="xsd:string" />
+					</xsd:sequence>
+				</xsd:complexType>
+			</xsd:element>
+			
+			<xsd:element name="setDeveloperStateParams">
+				<xsd:complexType>
+					<xsd:sequence>
+						<xsd:element name="username" type="xsd:string" />
+						<xsd:element name="password" type="xsd:string" />
+						<xsd:element name="developer" type="xsd:boolean" />
+					</xsd:sequence>
+				</xsd:complexType>
+			</xsd:element>
+			
+			<xsd:element name="startSubmissionUploadParams">
+				<xsd:complexType>
+					<xsd:sequence>
+						<xsd:element name="submissionParams"
+							type="types:Option" />
+						<xsd:element name="filesize" type="xsd:int" />
+						<xsd:element name="data" type="xsd:base64Binary" />
+					</xsd:sequence>
+				</xsd:complexType>
+			</xsd:element>
+
+			<xsd:element name="startResultDownloadData">
+				<xsd:complexType>
+					<xsd:sequence>
+						<xsd:element name="filesize" type="xsd:int" />
+						<xsd:element name="data" type="xsd:base64Binary" />
+					</xsd:sequence>
+				</xsd:complexType>
+			</xsd:element>
+			
+			<xsd:element name="continueSubmissionUploadData"
+				type="xsd:base64Binary" />
+
+			<xsd:element name="compareSourceResponseString" type="xsd:string" />
+			<xsd:element name="startSubmissionUploadResponseString"
+				type="xsd:string" />
+			<xsd:element name="continueSubmissionUploadDummyInt"
+				type="xsd:int" />
+			<xsd:element name="startResultDownloadRequestString"
+				type="xsd:string" />
+			<xsd:element name="continueResultDownloadDummyInt" type="xsd:int" />
+			<xsd:element name="continueResultDownloadData"
+				type="xsd:base64Binary" />
+			<xsd:element name="getResultRequestString" type="xsd:string" />
+			<xsd:element name="getStatusRequestString" type="xsd:string" />
+			<xsd:element name="cancelSubmissionRequestString"
+				type="xsd:string" />
+			<xsd:element name="cancelSubmissionDummyInt" type="xsd:int" />
+			<xsd:element name="getUserDataArrayDummyInt" type="xsd:int" />
+			<xsd:element name="setUserDataUsernameN" type="xsd:string"/>
+			<xsd:element name="setUserDataStringN" type="xsd:string"/>
+			<xsd:element name="updateUserInfoDummyInt" type="xsd:int" />
+			<xsd:element name="setUserDataDummyInt" type="xsd:int" />
+			<xsd:element name="requestAccountBoolean" type="xsd:boolean" />
+			<xsd:element name="getAccountRequestsLenOnlyBoolean"
+				type="xsd:boolean" />
+			<xsd:element name="finishAccountRequestDummyInt" type="xsd:int" />
+			<xsd:element name="extendAccountString" type="xsd:string" />
+			<xsd:element name="extendAccountDummyInt" type="xsd:int" />
+			<xsd:element name="getMailTemplatesInt" type="xsd:int" />
+			<xsd:element name="setMailTemplateDummyInt" type="xsd:int" />
+			<xsd:element name="notifyDevelopersDummyInt" type="xsd:int" />
+			<xsd:element name="setDeveloperStateDummyInt" type="xsd:int" />
+		</xsd:schema>
+	</wsdl:types>
+
+<!--=========================================================================-->
+
+	<wsdl:message name="jplagException">
+		<wsdl:part name="faultDetail" element="types:JPlagException" />
+	</wsdl:message>
+
+ 	<wsdl:message name="accessHeader">
+		<wsdl:part name="access" element="types:Access" />
+	</wsdl:message>
+	
+	<wsdl:message name="getServerInfoRequest" />
+	<wsdl:message name="getServerInfoResponse">
+		<wsdl:part name="infos" element="types:ServerInfo" />
+	</wsdl:message>
+
+	<wsdl:message name="compareSourceRequest">
+		<wsdl:part name="arguments" element="types:eOption" />
+		<wsdl:part name="inputZipFile" type="xsd:hexBinary" />
+	</wsdl:message>
+	<wsdl:message name="compareSourceResponse">
+		<wsdl:part name="submissionID"
+			element="types:compareSourceResponseString" />
+	</wsdl:message>
+	
+	<wsdl:message name="startSubmissionUploadRequest">
+		<wsdl:part name="params" element="types:startSubmissionUploadParams" />
+	</wsdl:message>
+	<wsdl:message name="startSubmissionUploadResponse">
+		<wsdl:part name="submissionID"
+			element="types:startSubmissionUploadResponseString" />
+	</wsdl:message>
+
+	<wsdl:message name="continueSubmissionUploadRequest">
+		<wsdl:part name="data" element="types:continueSubmissionUploadData" />
+	</wsdl:message>
+	<wsdl:message name="continueSubmissionUploadResponse">
+		<wsdl:part name="dummy"
+			element="types:continueSubmissionUploadDummyInt" />
+	</wsdl:message>
+
+	<wsdl:message name="getStatusRequest">
+		<wsdl:part name="submissionID" element="types:getStatusRequestString" />
+	</wsdl:message>
+	<wsdl:message name="getStatusResponse">
+		<wsdl:part name="status" element="types:Status" />
+	</wsdl:message>
+
+	<wsdl:message name="getResultRequest">
+		<wsdl:part name="submissionID" element="types:getResultRequestString" />
+	</wsdl:message>
+	<wsdl:message name="getResultResponse">
+		<wsdl:part name="zippedResult" type="xsd:hexBinary" />
+	</wsdl:message>
+
+	<wsdl:message name="startResultDownloadRequest">
+		<wsdl:part name="submissionID"
+			element="types:startResultDownloadRequestString" />
+	</wsdl:message>
+	<wsdl:message name="startResultDownloadResponse">
+		<wsdl:part name="data" element="types:startResultDownloadData" />
+	</wsdl:message>
+
+	<wsdl:message name="continueResultDownloadRequest">
+		<wsdl:part name="dummy"
+			element="types:continueResultDownloadDummyInt" />
+	</wsdl:message>
+	<wsdl:message name="continueResultDownloadResponse">
+		<wsdl:part name="data" element="types:continueResultDownloadData" />
+	</wsdl:message>
+	
+ 	<wsdl:message name="cancelSubmissionRequest">
+		<wsdl:part name="submissionID"
+			element="types:cancelSubmissionRequestString" />
+	</wsdl:message>
+ 	<wsdl:message name="cancelSubmissionResponse">
+		<wsdl:part name="dummy" element="types:cancelSubmissionDummyInt" />
+	</wsdl:message>
+	
+	<wsdl:message name="updateUserInfoRequest">
+		<wsdl:part name="params" element="types:updateUserInfoParams" />
+	</wsdl:message>
+	<wsdl:message name="updateUserInfoResponse">
+		<wsdl:part name="dummy" element="types:updateUserInfoDummyInt" />
+	</wsdl:message>
+	
+	<wsdl:message name="getUserDataArrayRequest">
+		<wsdl:part name="dummy" element="types:getUserDataArrayDummyInt" />
+	</wsdl:message>
+	<wsdl:message name="getUserDataArrayResponse">
+		<wsdl:part name="userdataarray" element="types:UserDataArray" />
+	</wsdl:message>
+
+	<wsdl:message name="setUserDataRequest">
+		<wsdl:part name="params" element="types:setUserDataParams" />
+	</wsdl:message>
+	<wsdl:message name="setUserDataResponse">
+		<wsdl:part name="dummy" element="types:setUserDataDummyInt" />
+	</wsdl:message>
+	
+	<wsdl:message name="requestAccountRequest">
+		<wsdl:part name="params" element="types:eRequestData" />
+	</wsdl:message>
+	<wsdl:message name="requestAccountResponse">
+		<wsdl:part name="noerror" element="types:requestAccountBoolean" />
+	</wsdl:message>
+
+	<wsdl:message name="getAccountRequestsRequest">
+		<wsdl:part name="lengthOnly"
+				   element="types:getAccountRequestsLenOnlyBoolean" />
+	</wsdl:message>
+	<wsdl:message name="getAccountRequestsResponse">
+		<wsdl:part name="requestArray" element="types:RequestDataArray" />
+	</wsdl:message>
+
+	<wsdl:message name="finishAccountRequestRequest">
+		<wsdl:part name="finishData" element="types:FinishRequestData" />
+	</wsdl:message>
+	<wsdl:message name="finishAccountRequestResponse">
+		<wsdl:part name="dummy" element="types:finishAccountRequestDummyInt" />
+	</wsdl:message>
+
+	<wsdl:message name="extendAccountRequest">
+		<wsdl:part name="extendCode" element="types:extendAccountString" />
+	</wsdl:message>
+	<wsdl:message name="extendAccountResponse">
+		<wsdl:part name="dummy" element="types:extendAccountDummyInt" />
+	</wsdl:message>
+	
+	<wsdl:message name="getMailTemplatesRequest">
+		<wsdl:part name="type" element="types:getMailTemplatesInt" />
+	</wsdl:message>
+	<wsdl:message name="getMailTemplatesResponse">
+		<wsdl:part name="templateArray" element="types:MailTemplateArray" />
+	</wsdl:message>
+
+	<wsdl:message name="setMailTemplateRequest">
+		<wsdl:part name="params" element="types:setMailTemplateParams" />
+	</wsdl:message>
+	<wsdl:message name="setMailTemplateResponse">
+		<wsdl:part name="dummy" element="types:setMailTemplateDummyInt" />
+	</wsdl:message>
+
+	<wsdl:message name="notifyDevelopersRequest">
+		<wsdl:part name="params" element="types:notifyDevelopersParams" />
+	</wsdl:message>
+	<wsdl:message name="notifyDevelopersResponse">
+		<wsdl:part name="dummy" element="types:notifyDevelopersDummyInt" />
+	</wsdl:message>
+	
+	<wsdl:message name="setDeveloperStateRequest">
+		<wsdl:part name="params" element="types:setDeveloperStateParams" />
+	</wsdl:message>
+	<wsdl:message name="setDeveloperStateResponse">
+		<wsdl:part name="dummy" element="types:setDeveloperStateDummyInt" />
+	</wsdl:message>
+		
+<!--=========================================================================-->
+
+	<wsdl:portType name="JPlagTyp">
+		<wsdl:operation name="getServerInfo">
+			<wsdl:documentation>
+				Returns some user account informations, the available languages
+				including default values for suffixes and minimum match length,
+				the available country languages and a list of own submissions
+				on the server.
+			</wsdl:documentation>
+			<wsdl:input message="tns:getServerInfoRequest" />
+			<wsdl:output message="tns:getServerInfoResponse" />
+			<wsdl:fault message="tns:jplagException" name="ExceptionDetail" />
+		</wsdl:operation>
+	
+		<wsdl:operation name="compareSource">
+			<wsdl:documentation>
+				Quickchecks the parameters and adds the given submission to the
+				working queue. It takes an Option object and a MimeMultipart
+				object containing a zup file with all the files to be compared
+				in the structure specified by the Option object.
+				Returns the submission identification string.
+				This function is not .NET compatible!
+			</wsdl:documentation>
+			<wsdl:input message="tns:compareSourceRequest" />
+			<wsdl:output message="tns:compareSourceResponse" />
+			<wsdl:fault message="tns:jplagException" name="ExceptionDetail" />
+		</wsdl:operation>
+
+		<wsdl:operation name="startSubmissionUpload">
+			<wsdl:documentation>
+				Quickchecks the parameters and starts taking a submission upload
+				with the first 80 kB part of the zipped submission.
+				If the submission file size is less than or equal to 80 kB, the
+				submission is added to the working queue.
+				The part size may not be less than 80 kB, if there is more than
+				one part to be sent.
+				Returns the submission identification string
+			</wsdl:documentation>
+			<wsdl:input message="tns:startSubmissionUploadRequest" />
+			<wsdl:output message="tns:startSubmissionUploadResponse" />
+			<wsdl:fault message="tns:jplagException" name="ExceptionDetail" />
+		</wsdl:operation>
+
+		<wsdl:operation name="continueSubmissionUpload">
+			<wsdl:documentation>
+				Used to send the next 80 kB part to the server. If the remaining
+				file size is less than or equal to 80 kB, the submission is
+				added to the working queue.
+				The part size may not be less than 80 kB, if this part is not
+				the last part to be sent.				
+			</wsdl:documentation>
+			<wsdl:input message="tns:continueSubmissionUploadRequest" />
+			<wsdl:output message="tns:continueSubmissionUploadResponse" />
+			<wsdl:fault message="tns:jplagException" name="ExceptionDetail" />
+		</wsdl:operation>
+		
+		<wsdl:operation name="getStatus">
+			<wsdl:documentation>
+				Returns the current state and progress of the submission
+				identified by the submissionID as described above
+			</wsdl:documentation>
+			<wsdl:input message="tns:getStatusRequest" />
+			<wsdl:output message="tns:getStatusResponse" />
+			<wsdl:fault message="tns:jplagException" name="ExceptionDetail" />
+		</wsdl:operation>
+		
+		<wsdl:operation name="getResult">
+			<wsdl:documentation>
+				Returns the zipped HTML result files of the requested submission
+				in a MimeMultipart object. You have to call cancelSubmission
+				afterwards to delete the files from the server.
+				This function is not .NET compatible!
+			</wsdl:documentation>
+			<wsdl:input message="tns:getResultRequest" />
+			<wsdl:output message="tns:getResultResponse" />
+			<wsdl:fault message="tns:jplagException" name="ExceptionDetail" />
+		</wsdl:operation>
+		
+		<wsdl:operation name="startResultDownload">
+			<wsdl:documentation>
+				Starts the download of the requested submission and returns the
+				total size of the zipped result files and its first 80 kB part.
+				If it is the only part of the download, the file is deleted from
+				the server afterwards.
+			</wsdl:documentation>
+			<wsdl:input message="tns:startResultDownloadRequest" />
+			<wsdl:output message="tns:startResultDownloadResponse" />
+			<wsdl:fault message="tns:jplagException" name="ExceptionDetail" />
+		</wsdl:operation>
+
+		<wsdl:operation name="continueResultDownload">
+			<wsdl:documentation>
+				Returns the next 80 kB part of the current download.
+				If it is the last part of the download, the size might be less
+				than 80 kB and the file is deleted from the server.
+				The dummy parameter should just be zero.
+			</wsdl:documentation>
+			<wsdl:input message="tns:continueResultDownloadRequest" />
+			<wsdl:output message="tns:continueResultDownloadResponse" />
+			<wsdl:fault message="tns:jplagException" name="ExceptionDetail" />
+		</wsdl:operation>
+
+ 		<wsdl:operation name="cancelSubmission">
+			<wsdl:documentation>
+				Cancels a submission and removes all related files from
+				the server
+			</wsdl:documentation>
+  			<wsdl:input	message="tns:cancelSubmissionRequest" />
+  			<wsdl:output message="tns:cancelSubmissionResponse" />
+ 			<wsdl:fault message="tns:jplagException" name="ExceptionDetail" />
+		</wsdl:operation>
+		
+		<wsdl:operation name="updateUserInfo">
+			<wsdl:documentation>
+				Allows the user to change his password, update his alternative
+				email address and his homepage.
+				A null value means, that the value should remain unchanged.
+				An empty string removes the attribute.
+				The password must have at least 6 characters.
+			</wsdl:documentation>
+			<wsdl:input message="tns:updateUserInfoRequest" />
+			<wsdl:output message="tns:updateUserInfoResponse" />
+			<wsdl:fault message="tns:jplagException" name="ExceptionDetail" />
+		</wsdl:operation>
+		
+		<wsdl:operation name="getUserDataArray">
+			<wsdl:documentation>
+				For JPlag and group admins only:
+					Returns an array of UserData elements containing all
+					information about the users.
+					The dummy parameter can be any int and will be ignored.
+					For JPlag admins all users are returned, for group admins
+					only the users they created are returned and the passwords
+					are empty.
+			</wsdl:documentation>
+			<wsdl:input message="tns:getUserDataArrayRequest" />
+			<wsdl:output message="tns:getUserDataArrayResponse" />
+			<wsdl:fault message="tns:jplagException" name="ExceptionDetail" />
+		</wsdl:operation>
+
+		<wsdl:operation name="setUserData">
+			<wsdl:documentation>
+				For JPlag and group admins only:
+					Changes a single user's data.
+					Group admins can only change users they created and can not
+					change "created", "createdBy", "lastUsage" and "numOfSubs" 
+					(these fields become ignored, though they may not be null).
+					They can also change the user state only to "Normal" and
+					"Deactivated".
+					For JPlag admins, the "createdBy" field has to point to a
+					valid user.
+					If userdata.username==null: User oldUsername becomes
+						permamently removed from the server including
+						all his files.
+					Else if oldUsername==null: A new user userdata.username
+						will be generated.
+					Else: The user oldUsername becomes updated (including a
+						possible name change to userdata.username).
+						If the password is empty, the password field becomes
+						ignored.
+					The usernames must match the pattern "^[\\w\\@\\.]+$".
+			</wsdl:documentation>
+			<wsdl:input message="tns:setUserDataRequest" />
+			<wsdl:output message="tns:setUserDataResponse" />
+			<wsdl:fault message="tns:jplagException" name="ExceptionDetail" />
+		</wsdl:operation>
+		
+		<wsdl:operation name="requestAccount">
+			<wsdl:documentation>
+				For server page use only:
+					- The validateTime field will always be ignored.
+					- If everything but username is null, it returns, whether
+					  the username is OK (true) or it is already used (false).
+					- If everything but password is null and password is the
+					  email validation code sent to the user, the request
+					  becomes available to the AdminTool and an email is
+					  sent to the administrator, if the code is wrong, false is
+					  returned.
+					- If username, password, realname, email and reason is not
+					  null, it saves the data and sends a validation email
+					  to the user.
+					- Otherwise an exception is thrown.
+					The usernames must match the pattern "^[\\w\\@\\.]+$".
+			</wsdl:documentation>
+			<wsdl:input message="tns:requestAccountRequest" />
+			<wsdl:output message="tns:requestAccountResponse" />
+			<wsdl:fault message="tns:jplagException" name="ExceptionDetail" />
+		</wsdl:operation>
+		
+		<wsdl:operation name="getAccountRequests">
+			<wsdl:documentation>
+				For administrational use only:
+					Returns a list of validated account requests.
+					If lengthOnly is true, the array elements will be empty,
+					but represent the number of requests waiting.
+			</wsdl:documentation>
+			<wsdl:input message="tns:getAccountRequestsRequest" />
+			<wsdl:output message="tns:getAccountRequestsResponse" />
+			<wsdl:fault message="tns:jplagException" name="ExceptionDetail" />
+		</wsdl:operation>
+
+		<wsdl:operation name="finishAccountRequest">
+			<wsdl:documentation>
+				For administrational use only:
+					Accepts or dismisses the given users account request and
+					informs the user about it. The request corresponding to the
+					username becomes deleted.
+					An email containing the given message will be sent to the
+					user's primary email address. If the subject is null or
+					empty, no email will be sent.
+					- If the password is null, the request becomes dismissed.
+					- Otherwise the request becomes accepted.
+			</wsdl:documentation>
+			<wsdl:input message="tns:finishAccountRequestRequest" />
+			<wsdl:output message="tns:finishAccountRequestResponse" />
+			<wsdl:fault message="tns:jplagException" name="ExceptionDetail" />
+		</wsdl:operation>
+		
+		<wsdl:operation name="extendAccount">
+			<wsdl:documentation>
+				For server page use only:
+					Extends the account given by the account extending code.
+			</wsdl:documentation>
+			<wsdl:input message="tns:extendAccountRequest" />
+			<wsdl:output message="tns:extendAccountResponse" />
+			<wsdl:fault message="tns:jplagException" name="ExceptionDetail" />
+		</wsdl:operation>
+
+		<wsdl:operation name="getMailTemplates">
+			<wsdl:documentation>
+				For administrational use only:
+					Returns a list of mail templates for the given type.
+					Available types are:
+					  - 0: Accepted request mail
+					  - 1: Declined request mail
+					  - 2: Server mail templates
+					  - 3: Others
+			</wsdl:documentation>
+			<wsdl:input message="tns:getMailTemplatesRequest" />
+			<wsdl:output message="tns:getMailTemplatesResponse" />
+			<wsdl:fault message="tns:jplagException" name="ExceptionDetail" />
+		</wsdl:operation>
+		
+		<wsdl:operation name="setMailTemplate">
+			<wsdl:documentation>
+				For administrational use only:
+					Sets a mail template for the given type (for available
+					types see getMailTemplates).
+					If a mail template with the same type and name already
+					exists, it will be overwritten. If the subject is empty,
+					the template will be removed. If the data is empty, the
+					template will be renamed from name to subject.
+			</wsdl:documentation>
+			<wsdl:input message="tns:setMailTemplateRequest" />
+			<wsdl:output message="tns:setMailTemplateResponse" />
+			<wsdl:fault message="tns:jplagException" name="ExceptionDetail" />
+		</wsdl:operation>
+		
+		<wsdl:operation name="notifyDevelopers">
+			<wsdl:documentation>
+				For administrational use:
+					Sends a mail to all developers.
+			</wsdl:documentation>
+			<wsdl:input message="tns:notifyDevelopersRequest" />
+			<wsdl:output message="tns:notifyDevelopersResponse" />
+			<wsdl:fault message="tns:jplagException" name="ExceptionDetail" />
+		</wsdl:operation>
+		
+		<wsdl:operation name="setDeveloperState">
+			<wsdl:documentation>
+				For server page use only:
+					Sets whether a given user should be considered as a
+					developer or not.
+			</wsdl:documentation>
+			<wsdl:input message="tns:setDeveloperStateRequest" />
+			<wsdl:output message="tns:setDeveloperStateResponse" />
+			<wsdl:fault message="tns:jplagException" name="ExceptionDetail" />
+		</wsdl:operation>
+	</wsdl:portType>
+
+<!--=========================================================================-->
+
+	<wsdl:binding name="JPlagServiceBinding" type="tns:JPlagTyp">
+		<soap:binding style="document"
+			transport="http://schemas.xmlsoap.org/soap/http" />
+
+  		<wsdl:operation name="getServerInfo">
+			<soap:operation	soapAction=
+				"http://jplag.ipd.kit.edu/JPlagService/getServerInfo" />
+			<wsdl:input>
+   				<soap:header message="tns:accessHeader"
+   							 part="access" use="literal"/>
+ 				<soap:body use="literal" />
+			</wsdl:input>
+
+			<wsdl:output>
+				<soap:body use="literal" />
+			</wsdl:output>
+			
+			<wsdl:fault name="ExceptionDetail">
+ 				<soap:fault name="ExceptionDetail" use="literal" />
+			</wsdl:fault>
+		</wsdl:operation>
+			
+		<wsdl:operation name="compareSource">
+			<soap:operation	soapAction=
+				"http://jplag.ipd.kit.edu/JPlagService/compareSource" />
+			<wsdl:input>
+   				<soap:header message="tns:accessHeader"
+   							 part="access" use="literal"/>
+				<mime:multipartRelated>
+					<mime:part>
+						<soap:body parts="arguments" use="literal" />
+					</mime:part>
+					<mime:part>
+						<mime:content part="inputZipFile"
+							type="multipart/zip" />
+					</mime:part>
+				</mime:multipartRelated>
+			</wsdl:input>
+
+			<wsdl:output>
+				<soap:body use="literal" />
+			</wsdl:output>
+
+			<wsdl:fault name="ExceptionDetail">
+ 				<soap:fault name="ExceptionDetail" use="literal" />
+			</wsdl:fault>
+		</wsdl:operation>
+
+		<wsdl:operation name="startSubmissionUpload">
+			<soap:operation	soapAction=
+				"http://jplag.ipd.kit.edu/JPlagService/startSubmissionUpload" />
+			<wsdl:input>
+   				<soap:header message="tns:accessHeader"
+   							 part="access" use="literal"/>
+				<soap:body use="literal" />
+			</wsdl:input>
+
+			<wsdl:output>
+				<soap:body use="literal" />
+			</wsdl:output>
+			
+			<wsdl:fault name="ExceptionDetail">
+ 				<soap:fault name="ExceptionDetail" use="literal" />
+			</wsdl:fault>
+		</wsdl:operation>
+
+		<wsdl:operation name="continueSubmissionUpload">
+			<soap:operation	soapAction=
+				"http://jplag.ipd.kit.edu/JPlagService/continueSubmissionUpload"
+				/>
+			<wsdl:input>
+   				<soap:header message="tns:accessHeader"
+   							 part="access" use="literal"/>
+				<soap:body use="literal" />
+			</wsdl:input>
+
+			<wsdl:output>
+				<soap:body use="literal" />
+			</wsdl:output>
+			
+			<wsdl:fault name="ExceptionDetail">
+ 				<soap:fault name="ExceptionDetail" use="literal" />
+			</wsdl:fault>
+		</wsdl:operation>
+		
+		<wsdl:operation name="getStatus">
+			<soap:operation	soapAction=
+				"http://jplag.ipd.kit.edu/JPlagService/getStatus" />
+			<wsdl:input>
+   				<soap:header message="tns:accessHeader"
+   							 part="access" use="literal"/>
+				<soap:body use="literal" />
+			</wsdl:input>
+
+			<wsdl:output>
+				<soap:body use="literal" />
+			</wsdl:output>
+			
+			<wsdl:fault name="ExceptionDetail">
+ 				<soap:fault name="ExceptionDetail" use="literal" />
+			</wsdl:fault>
+		</wsdl:operation>
+
+		<wsdl:operation name="getResult">
+			<soap:operation	soapAction=
+				"http://jplag.ipd.kit.edu/JPlagService/getResult" />
+			<wsdl:input>
+   				<soap:header message="tns:accessHeader"
+   							 part="access" use="literal"/>
+				<soap:body use="literal" />
+			</wsdl:input>
+
+			<wsdl:output>
+				<mime:multipartRelated>
+					<mime:part>
+						<soap:body use="literal" />
+					</mime:part>
+					<mime:part>
+						<mime:content part="zippedResult"
+							type="multipart/zip" />
+					</mime:part>
+				</mime:multipartRelated>
+			</wsdl:output>
+			
+			<wsdl:fault name="ExceptionDetail">
+ 				<soap:fault name="ExceptionDetail" use="literal" />
+			</wsdl:fault>
+		</wsdl:operation>
+
+		<wsdl:operation name="startResultDownload">
+			<soap:operation	soapAction=
+				"http://jplag.ipd.kit.edu/JPlagService/startResultDownload" />
+			<wsdl:input>
+   				<soap:header message="tns:accessHeader"
+   							 part="access" use="literal"/>
+				<soap:body use="literal" />
+			</wsdl:input>
+
+			<wsdl:output>
+				<soap:body use="literal" />
+			</wsdl:output>
+			
+			<wsdl:fault name="ExceptionDetail">
+ 				<soap:fault name="ExceptionDetail" use="literal" />
+			</wsdl:fault>
+		</wsdl:operation>
+
+		<wsdl:operation name="continueResultDownload">
+			<soap:operation	soapAction=
+				"http://jplag.ipd.kit.edu/JPlagService/continueResultDownload"
+				/>
+			<wsdl:input>
+   				<soap:header message="tns:accessHeader"
+   							 part="access" use="literal"/>
+				<soap:body use="literal" />
+			</wsdl:input>
+
+			<wsdl:output>
+				<soap:body use="literal" />
+			</wsdl:output>
+			
+			<wsdl:fault name="ExceptionDetail">
+ 				<soap:fault name="ExceptionDetail" use="literal" />
+			</wsdl:fault>
+		</wsdl:operation>
+		
+		<wsdl:operation name="cancelSubmission">
+			<soap:operation	soapAction=
+				"http://jplag.ipd.kit.edu/JPlagService/cancelSubmission" />
+			<wsdl:input>
+   				<soap:header message="tns:accessHeader"
+   							 part="access" use="literal"/>
+				<soap:body use="literal" />
+			</wsdl:input>
+			
+			<wsdl:output>
+				<soap:body use="literal" />
+			</wsdl:output>
+
+			<wsdl:fault name="ExceptionDetail">
+ 				<soap:fault name="ExceptionDetail" use="literal" />
+			</wsdl:fault>
+		</wsdl:operation>
+		
+		<wsdl:operation name="updateUserInfo">
+			<soap:operation	soapAction=
+				"http://jplag.ipd.kit.edu/JPlagService/updateUserInfo" />
+			<wsdl:input>
+   				<soap:header message="tns:accessHeader"
+   							 part="access" use="literal"/>
+				<soap:body use="literal" />
+			</wsdl:input>
+			
+			<wsdl:output>
+				<soap:body use="literal" />
+			</wsdl:output>
+
+			<wsdl:fault name="ExceptionDetail">
+ 				<soap:fault name="ExceptionDetail" use="literal" />
+			</wsdl:fault>
+		</wsdl:operation>
+
+		<wsdl:operation name="getUserDataArray">
+			<soap:operation	soapAction=
+				"http://jplag.ipd.kit.edu/JPlagService/getUserDataArray" />
+			<wsdl:input>
+   				<soap:header message="tns:accessHeader"
+   							 part="access" use="literal"/>
+				<soap:body use="literal" />
+			</wsdl:input>
+			
+			<wsdl:output>
+				<soap:body use="literal" />
+			</wsdl:output>
+
+			<wsdl:fault name="ExceptionDetail">
+ 				<soap:fault name="ExceptionDetail" use="literal" />
+			</wsdl:fault>
+		</wsdl:operation>
+
+		<wsdl:operation name="setUserData">
+			<soap:operation	soapAction=
+				"http://jplag.ipd.kit.edu/JPlagService/setUserData" />
+			<wsdl:input>
+   				<soap:header message="tns:accessHeader"
+   							 part="access" use="literal"/>
+				<soap:body use="literal" />
+			</wsdl:input>
+			
+			<wsdl:output>
+				<soap:body use="literal" />
+			</wsdl:output>
+
+			<wsdl:fault name="ExceptionDetail">
+ 				<soap:fault name="ExceptionDetail" use="literal" />
+			</wsdl:fault>
+		</wsdl:operation>
+
+		<wsdl:operation name="requestAccount">
+			<soap:operation	soapAction=
+				"http://jplag.ipd.kit.edu/JPlagService/requestAccount" />
+			<wsdl:input>
+   				<soap:header message="tns:accessHeader"
+   							 part="access" use="literal"/>
+				<soap:body use="literal" />
+			</wsdl:input>
+			
+			<wsdl:output>
+				<soap:body use="literal" />
+			</wsdl:output>
+
+			<wsdl:fault name="ExceptionDetail">
+ 				<soap:fault name="ExceptionDetail" use="literal" />
+			</wsdl:fault>
+		</wsdl:operation>
+
+		<wsdl:operation name="getAccountRequests">
+			<soap:operation	soapAction=
+				"http://jplag.ipd.kit.edu/JPlagService/getAccountRequests" />
+			<wsdl:input>
+   				<soap:header message="tns:accessHeader"
+   							 part="access" use="literal"/>
+				<soap:body use="literal" />
+			</wsdl:input>
+			
+			<wsdl:output>
+				<soap:body use="literal" />
+			</wsdl:output>
+
+			<wsdl:fault name="ExceptionDetail">
+ 				<soap:fault name="ExceptionDetail" use="literal" />
+			</wsdl:fault>
+		</wsdl:operation>
+
+		<wsdl:operation name="finishAccountRequest">
+			<soap:operation	soapAction=
+				"http://jplag.ipd.kit.edu/JPlagService/finishAccountRequest" />
+			<wsdl:input>
+   				<soap:header message="tns:accessHeader"
+   							 part="access" use="literal"/>
+				<soap:body use="literal" />
+			</wsdl:input>
+			
+			<wsdl:output>
+				<soap:body use="literal" />
+			</wsdl:output>
+
+			<wsdl:fault name="ExceptionDetail">
+ 				<soap:fault name="ExceptionDetail" use="literal" />
+			</wsdl:fault>
+		</wsdl:operation>
+		
+		<wsdl:operation name="extendAccount">
+			<soap:operation	soapAction=
+				"http://jplag.ipd.kit.edu/JPlagService/extendAccount" />
+			<wsdl:input>
+   				<soap:header message="tns:accessHeader"
+   							 part="access" use="literal"/>
+				<soap:body use="literal" />
+			</wsdl:input>
+			
+			<wsdl:output>
+				<soap:body use="literal" />
+			</wsdl:output>
+
+			<wsdl:fault name="ExceptionDetail">
+ 				<soap:fault name="ExceptionDetail" use="literal" />
+			</wsdl:fault>
+		</wsdl:operation>
+
+		<wsdl:operation name="getMailTemplates">
+			<soap:operation	soapAction=
+				"http://jplag.ipd.kit.edu/JPlagService/getMailTemplates" />
+			<wsdl:input>
+   				<soap:header message="tns:accessHeader"
+   							 part="access" use="literal"/>
+				<soap:body use="literal" />
+			</wsdl:input>
+			
+			<wsdl:output>
+				<soap:body use="literal" />
+			</wsdl:output>
+
+			<wsdl:fault name="ExceptionDetail">
+ 				<soap:fault name="ExceptionDetail" use="literal" />
+			</wsdl:fault>
+		</wsdl:operation>
+
+		<wsdl:operation name="setMailTemplate">
+			<soap:operation	soapAction=
+				"http://jplag.ipd.kit.edu/JPlagService/setMailTemplate" />
+			<wsdl:input>
+   				<soap:header message="tns:accessHeader"
+   							 part="access" use="literal"/>
+				<soap:body use="literal" />
+			</wsdl:input>
+			
+			<wsdl:output>
+				<soap:body use="literal" />
+			</wsdl:output>
+
+			<wsdl:fault name="ExceptionDetail">
+ 				<soap:fault name="ExceptionDetail" use="literal" />
+			</wsdl:fault>
+		</wsdl:operation>
+
+		<wsdl:operation name="notifyDevelopers">
+			<soap:operation	soapAction=
+				"http://jplag.ipd.kit.edu/JPlagService/notifyDevelopers" />
+			<wsdl:input>
+   				<soap:header message="tns:accessHeader"
+   							 part="access" use="literal"/>
+				<soap:body use="literal" />
+			</wsdl:input>
+			
+			<wsdl:output>
+				<soap:body use="literal" />
+			</wsdl:output>
+
+			<wsdl:fault name="ExceptionDetail">
+ 				<soap:fault name="ExceptionDetail" use="literal" />
+			</wsdl:fault>
+		</wsdl:operation>
+
+		<wsdl:operation name="setDeveloperState">
+			<soap:operation	soapAction=
+				"http://jplag.ipd.kit.edu/JPlagService/setDeveloperState" />
+			<wsdl:input>
+   				<soap:header message="tns:accessHeader"
+   							 part="access" use="literal"/>
+				<soap:body use="literal" />
+			</wsdl:input>
+			
+			<wsdl:output>
+				<soap:body use="literal" />
+			</wsdl:output>
+
+			<wsdl:fault name="ExceptionDetail">
+ 				<soap:fault name="ExceptionDetail" use="literal" />
+			</wsdl:fault>
+		</wsdl:operation>
+	</wsdl:binding>
+
+<!--=========================================================================-->
+
+	<wsdl:service name="JPlagService">
+		<wsdl:port name="JPlagServicePort" binding="tns:JPlagServiceBinding">
+			<soap:address location="https://jplag.ipd.kit.edu/JPlagService/service" />
+			<!-- <soap:address location="http://localhost:2222/JPlagService/service" /> -->
+		</wsdl:port>
+	</wsdl:service>
+</wsdl:definitions>

+ 27 - 0
jplag.webService/src/main/webapp/WEB-INF/jaxrpc-ri.xml

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- Author Emeric Kwemou
+	 This file is the deployment descriptor of the
+	 JPlag web service used in war files  
+-->
+<webServices
+    xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/dd"
+    version="1.0"
+    targetNamespaceBase="urn:jplag"
+    typeNamespaceBase="urn:jplag:types"
+    urlPatternBase="/ws">
+
+    <endpoint
+        name="jplag"
+        displayName="JPlag web service"
+        description="Web service interface for JPlag" 
+        wsdl="/WEB-INF/JPlagService.wsdl"
+        interface="jplagWebService.server.JPlagTyp"  
+        implementation="jplagWebService.serverImpl.JPlagTypImpl" 
+	    model="/WEB-INF/model-wsdl-doclit.xml.gz"/>
+ 
+    <endpointMapping
+        endpointName="jplag"
+        urlPattern="/service"/>
+
+</webServices>

+ 34 - 0
jplag.webService/src/main/webapp/WEB-INF/web.xml

@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE web-app
+    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
+    "http://java.sun.com/j2ee/dtds/web-app_2_3.dtd">
+
+<web-app>
+	<display-name>JPlag web service</display-name>
+	<description>Web service interface for JPlag </description>
+
+	<session-config>
+		<session-timeout>100</session-timeout>
+	</session-config>
+	<security-constraint>
+		<web-resource-collection>
+			<web-resource-name>JPlag
+				WSDL file</web-resource-name>
+			<url-pattern>/service*</url-pattern>
+		</web-resource-collection>
+		<user-data-constraint>
+			<transport-guarantee>NONE</transport-guarantee>
+		</user-data-constraint>
+	</security-constraint>
+	<security-constraint>
+		<web-resource-collection>
+			<web-resource-name>Entire
+				JPlag application</web-resource-name>
+			<url-pattern>/*</url-pattern>
+		</web-resource-collection>
+		<user-data-constraint>
+		<!--  was CONFIDENTIAL  -->
+			<transport-guarantee>NONE</transport-guarantee>
+		</user-data-constraint>
+	</security-constraint>
+</web-app>