diff options
9 files changed, 122 insertions, 39 deletions
diff --git a/dozentenmodulserver/setup/sat-01-schema.sql b/dozentenmodulserver/setup/sat-01-schema.sql index b0966e73..d44b0ed2 100644 --- a/dozentenmodulserver/setup/sat-01-schema.sql +++ b/dozentenmodulserver/setup/sat-01-schema.sql @@ -136,8 +136,10 @@ CREATE TABLE IF NOT EXISTS `lecturepermission` ( CREATE TABLE IF NOT EXISTS `operatingsystem` ( `osid` int(11) NOT NULL COMMENT 'Defined on the master server, so no auto_increment!', - `displayname` varchar(100) NOT NULL, - `architecture` varchar(14) NOT NULL, + `displayname` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL, + `architecture` varchar(14) COLLATE utf8mb4_unicode_ci NOT NULL, + `maxmem` int(11) NOT NULL, + `maxcpu` int(11) NOT NULL, PRIMARY KEY (`osid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/dozentenmodulserver/setup/sat-01-testdata.sql b/dozentenmodulserver/setup/sat-01-testdata.sql index 4817f401..f01c7024 100644 --- a/dozentenmodulserver/setup/sat-01-testdata.sql +++ b/dozentenmodulserver/setup/sat-01-testdata.sql @@ -8,8 +8,18 @@ SET time_zone = "+00:00"; USE `sat`; -INSERT INTO `operatingsystem` (`osid`, `displayname`, `architecture`) VALUES -(1,'Windows 7 (64 Bit)','AMD64'),(2,'Windows 8 (32 Bit)','x86'),(3,'Windows 8 (64 Bit)','AMD64'),(4,'Ubuntu (32 Bit)','x86'),(5,'Ubuntu (64 Bit)','AMD64'),(6,'OpenSUSE (32 Bit)','x86'),(7,'OpenSUSE (64 Bit)','AMD64'),(8,'Other Linux (32 Bit)','x86'),(9,'Other Linux (64 Bit)','AMD64'),(10,'Windows 7 (32 Bit)','x86'); +INSERT INTO `operatingsystem` (`osid`, `displayname`, `architecture`, `maxmem`, `maxcpu`) VALUES +(1, 'Windows 7 (64 Bit)', 'AMD64', 196608, 256), +(2, 'Windows 8 (32 Bit)', 'x86', 4096, 32), +(3, 'Windows 8 (64 Bit)', 'AMD64', 131072, 256), +(4, 'Ubuntu (32 Bit)', 'x86', 0, 0), +(5, 'Ubuntu (64 Bit)', 'AMD64', 0, 0), +(6, 'OpenSUSE (32 Bit)', 'x86', 0, 0), +(7, 'OpenSUSE (64 Bit)', 'AMD64', 0, 0), +(8, 'Other Linux (32 Bit)', 'x86', 0, 0), +(9, 'Other Linux (64 Bit)', 'AMD64', 0, 0), +(10, 'Windows 7 (32 Bit)', 'x86', 4096, 32), +(11, 'Windows 2000 Professional', 'x86', 4096, 4); INSERT INTO `virtualizer` (`virtid`, `virtname`) VALUES ('vmware', 'VMware'); diff --git a/dozentenmodulserver/src/main/java/fi/iki/elonen/NanoHTTPD.java b/dozentenmodulserver/src/main/java/fi/iki/elonen/NanoHTTPD.java index f8e44fa8..8d38f577 100644 --- a/dozentenmodulserver/src/main/java/fi/iki/elonen/NanoHTTPD.java +++ b/dozentenmodulserver/src/main/java/fi/iki/elonen/NanoHTTPD.java @@ -50,6 +50,7 @@ import java.net.Socket; import java.net.SocketException; import java.net.SocketTimeoutException; import java.net.URLDecoder; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; @@ -66,6 +67,8 @@ import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import org.apache.commons.io.output.ByteArrayOutputStream; +import org.apache.log4j.Logger; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; @@ -121,6 +124,9 @@ import org.joda.time.format.DateTimeFormatter; * licence) */ public abstract class NanoHTTPD implements Runnable { + + private static final Logger LOGGER = Logger.getLogger(NanoHTTPD.class); + /** * Maximum time to wait on Socket.getInputStream().read() (in milliseconds) * This is required as the Keep-Alive HTTP connections would otherwise @@ -150,6 +156,8 @@ public abstract class NanoHTTPD implements Runnable { */ private AsyncRunner asyncRunner; + protected int maxRequestSize = 0; + /** * Constructs an HTTP server on given port. */ @@ -847,6 +855,8 @@ public abstract class NanoHTTPD implements Runnable { if (splitbyte > 0) break; read = inputStream.read(buf, rlen, BUFSIZE - rlen); + if (maxRequestSize != 0 && rlen > maxRequestSize) + throw new SocketException("Request too large"); } } @@ -909,7 +919,6 @@ public abstract class NanoHTTPD implements Runnable { @Override public void parseBody(Map<String, String> files) throws IOException, ResponseException { - final Reader in = new InputStreamReader(inputStream); long size; if (headers.containsKey("content-length")) { size = Integer.parseInt(headers.get("content-length")); @@ -922,36 +931,50 @@ public abstract class NanoHTTPD implements Runnable { // If the method is POST, there may be parameters // in data section, too, read it: if (Method.POST.equals(method)) { - String contentType = ""; + String contentType = null; + String contentEncoding = null; String contentTypeHeader = headers.get("content-type"); StringTokenizer st = null; if (contentTypeHeader != null) { - st = new StringTokenizer(contentTypeHeader, ",; "); + st = new StringTokenizer(contentTypeHeader, ","); if (st.hasMoreTokens()) { - contentType = st.nextToken(); + String part[] = st.nextToken().split(";\\s*", 2); + contentType = part[0]; + if (part.length == 2) { + contentEncoding = part[1]; + } } } + Charset cs = StandardCharsets.ISO_8859_1; + if (contentEncoding != null) { + try { + cs = Charset.forName(contentEncoding); + } catch (Exception e) { + } + } + LOGGER.debug("Content type is '" + contentType + "', encoding '" + cs.name() + "'"); if ("multipart/form-data".equalsIgnoreCase(contentType)) { throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data, which is not supported"); } else { - String postLine = ""; - StringBuilder postLineBuffer = new StringBuilder(); - char pbuf[] = new char[512]; - while (rlen >= 0 && size > 0 && !postLine.endsWith("\r\n")) { - rlen = in.read(pbuf, 0, (int) Math.min(size, 512)); - if (rlen <= 0) + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte pbuf[] = new byte[1000]; + while (size > 0) { + int ret = inputStream.read(pbuf, 0, (int) Math.min(size, pbuf.length)); + if (ret <= 0) + break; + if (ret >= 2 && pbuf[ret - 1] == '\n' && pbuf[ret - 2] == '\r') break; - postLine = String.valueOf(pbuf, 0, rlen); - postLineBuffer.append(postLine); + size -= ret; + baos.write(pbuf, 0, ret); } - postLine = postLineBuffer.toString().trim(); + String postLine = new String(baos.toByteArray(), cs); // Handle application/x-www-form-urlencoded if ("application/x-www-form-urlencoded".equalsIgnoreCase(contentType)) { decodeParms(postLine, parms); - } else if (postLine.length() != 0) { + } else if (files != null && postLine.length() != 0) { // Special case for raw POST data => create a special files entry "postData" with raw content data files.put("postData", postLine); } diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbMailQueue.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbMailQueue.java index f05b70f0..0b3df621 100644 --- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbMailQueue.java +++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbMailQueue.java @@ -17,7 +17,7 @@ public class DbMailQueue { public static void queue(Mail mail) throws SQLException { try (MysqlConnection connection = Database.getConnection()) { - MysqlStatement stmt = connection.prepareStatement("INSERT INTO mailqueue" + MysqlStatement stmt = connection.prepareStatement("INSERT IGNORE INTO mailqueue" + " (mailid, userid, message, failcount, dateline) VALUES" + " (:mailid, :userid, :message, 0, UNIX_TIMESTAMP())"); stmt.setString("mailid", mail.id); @@ -45,7 +45,7 @@ public class DbMailQueue { } MysqlStatement stmt = connection.prepareStatement("SELECT" + " mailid, userid, message FROM mailqueue" - + " WHERE failcount < 2 ORDER BY dateline ASC LIMIT " + batchSize); + + " WHERE failcount < 8 ORDER BY dateline ASC LIMIT " + batchSize); ResultSet rs = stmt.executeQuery(); List<Mail> list = new ArrayList<>(); while (rs.next()) { diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/mail/Mail.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/mail/Mail.java index 60b1cc52..72271b57 100644 --- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/mail/Mail.java +++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/mail/Mail.java @@ -1,11 +1,25 @@ package org.openslx.bwlp.sat.mail; -import java.util.UUID; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import org.openslx.bwlp.thrift.iface.UserInfo; public class Mail { + private static final MessageDigest md; + + static { + MessageDigest tmp; + try { + tmp = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + tmp = null; + } + md = tmp; + } + public final String id; public final String userId; public final String message; @@ -17,7 +31,15 @@ public class Mail { } public Mail(UserInfo recipient, String message) { - this(UUID.randomUUID().toString(), recipient.userId, message); + this(hash(recipient, message), recipient.userId, message); + } + + private static String hash(UserInfo recipient, String message) { + synchronized (md) { + md.update(recipient.userId.getBytes(StandardCharsets.UTF_8)); + md.update(message.getBytes(StandardCharsets.UTF_8)); + return new String(md.digest(), StandardCharsets.US_ASCII); + } } } diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/mail/MailGenerator.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/mail/MailGenerator.java index 932849b8..fd7b5a2b 100644 --- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/mail/MailGenerator.java +++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/mail/MailGenerator.java @@ -120,7 +120,7 @@ public class MailGenerator { return; String message = "Virtuelle Maschine '" + image.imageName + "':"; if (newVersion == null) { - message += " Die letzte verbliebene Version der VM wurde gelöscht; VM deaktiviert."; + message += " Die letzte verbliebene Version der VM wurde gelöscht; VM zur Löschung vorgemerkt."; } else { String uploaderName; try { @@ -129,11 +129,11 @@ public class MailGenerator { } catch (TNotFoundException | SQLException e) { uploaderName = "(unbekannt)"; } - message += "\n Neueste Version ist jetzt vom " + Formatter.date(newVersion.createTime) - + " (erstellt von " + uploaderName + ")"; if (oldVersion != null) { - message += "\n Vorherige Verson war vom " + Formatter.date(oldVersion.createTime); + message += "\n Alte Version vom " + Formatter.date(oldVersion.createTime) + " gelöscht"; } + message += "\n Neueste Version ist jetzt vom " + Formatter.date(newVersion.createTime) + + " (erstellt von " + uploaderName + ")"; } List<UserInfo> relevantUsers = getUserToMail(image); for (UserInfo user : relevantUsers) { diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/maintenance/MailFlusher.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/maintenance/MailFlusher.java index ca4003b4..0c868ec7 100644 --- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/maintenance/MailFlusher.java +++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/maintenance/MailFlusher.java @@ -3,7 +3,6 @@ package org.openslx.bwlp.sat.maintenance; import java.util.concurrent.TimeUnit; import org.apache.log4j.Logger; -import org.joda.time.DateTime; import org.openslx.bwlp.sat.mail.MailQueue; import org.openslx.util.QuickTimer; import org.openslx.util.QuickTimer.Task; @@ -24,15 +23,13 @@ public class MailFlusher implements Runnable { public synchronized static void init() { if (blockedUntil != 0) return; + LOGGER.debug("Initializing mail flusher"); blockedUntil = 1; QuickTimer.scheduleAtFixedRate(new Task() { @Override public void fire() { if (blockedUntil > System.currentTimeMillis()) return; - DateTime now = DateTime.now(); - if (now.getHourOfDay() != 3 || now.getMinuteOfHour() > 15) - return; start(); } }, TimeUnit.MINUTES.toMillis(6), TimeUnit.MINUTES.toMillis(10)); @@ -42,7 +39,7 @@ public class MailFlusher implements Runnable { if (blockedUntil > System.currentTimeMillis()) return; if (Maintenance.trySubmit(instance)) { - blockedUntil = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(1); + blockedUntil = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(10); } } diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/web/WebRpc.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/web/WebRpc.java index 6fdd57b6..1cc71e45 100644 --- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/web/WebRpc.java +++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/web/WebRpc.java @@ -1,7 +1,10 @@ package org.openslx.bwlp.sat.web; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.PrintStream; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; @@ -10,20 +13,18 @@ import java.util.Map; import javax.security.auth.login.LoginException; import org.apache.commons.io.output.ByteArrayOutputStream; -import org.apache.log4j.Logger; import org.openslx.bwlp.sat.mail.SmtpMailer; import org.openslx.bwlp.sat.mail.SmtpMailer.EncryptionMode; import org.openslx.util.Util; +import fi.iki.elonen.NanoHTTPD; import fi.iki.elonen.NanoHTTPD.Response; public class WebRpc { - private static final Logger LOGGER = Logger.getLogger(WebRpc.class); - public static Response handle(String uri, Map<String, String> params) { if (uri.equals("mailtest")) { - mailTest(params); + return mailTest(params); } return WebServer.notFound(); } @@ -42,7 +43,7 @@ public class WebRpc { try { ssl = EncryptionMode.valueOf(params.get("ssl")); } catch (Exception e) { - return WebServer.badRequest("Invalid SSL mode"); + return WebServer.badRequest("Invalid SSL mode '" + params.get("ssl") + "'"); } // Validate if (port < 1 || port > 65535) @@ -59,9 +60,24 @@ public class WebRpc { new PrintStream(baos)); } catch (InvalidKeyException | LoginException | NoSuchAlgorithmException | InvalidKeySpecException | IOException e) { - LOGGER.error("Could not initialize connection to SMTP server. Mails will not be sent", e); + try { + baos.write("Could not connect to mail server".getBytes(StandardCharsets.UTF_8)); + e.printStackTrace(new PrintWriter(baos)); + } catch (IOException e1) { + } + smtpc = null; + } + boolean ret = false; + if (smtpc != null) { + ret = smtpc.send(recipient, "bwLehrpool Mail Test", "Test der Mailkonfiguration.\n\nHost: " + + host + ":" + port + "\nSSL: " + ssl.toString() + "\nLogin: " + username); } - return WebServer.internalServerError(); + try { + baos.write(("\n\n-----------------------------------------\nTestergebnis: " + + (ret ? "" : "nicht ") + "erfolgreich").getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + } + return new NanoHTTPD.Response(NanoHTTPD.Response.Status.OK, "text/plain; charset=utf-8", + new ByteArrayInputStream(baos.toByteArray())); } - } diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/web/WebServer.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/web/WebServer.java index 99ecf4c4..946df9d0 100644 --- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/web/WebServer.java +++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/web/WebServer.java @@ -1,9 +1,11 @@ package org.openslx.bwlp.sat.web; import java.io.ByteArrayInputStream; +import java.io.IOException; import java.sql.SQLException; import org.apache.commons.io.output.ByteArrayOutputStream; +import org.apache.log4j.Logger; import org.openslx.bwlp.sat.database.mappers.DbLecture; import org.openslx.bwlp.sat.fileserv.FileServer; import org.openslx.bwlp.sat.util.Json; @@ -17,6 +19,8 @@ import fi.iki.elonen.NanoHTTPD; public class WebServer extends NanoHTTPD { + private static final Logger LOGGER = Logger.getLogger(WebServer.class); + private static final GenericDataCache<byte[]> lectureListCache = new GenericDataCache<byte[]>(15000) { Serializer serializer = new Persister(); @@ -31,6 +35,7 @@ public class WebServer extends NanoHTTPD { public WebServer(int port) { super("127.0.0.1", port); + maxRequestSize = 65535; } @Override @@ -52,6 +57,14 @@ public class WebServer extends NanoHTTPD { return serveStatus(); } if (session.getMethod() == Method.POST && uri.startsWith("/do/")) { + try { + session.parseBody(null); + } catch (IOException | ResponseException e) { + LOGGER.debug("could not parse request body", e); + return internalServerError(); + } + LOGGER.debug("Is RPC, passing " + uri.substring(4)); + LOGGER.debug(session.getParms()); return WebRpc.handle(uri.substring(4), session.getParms()); } |