SMTP on port 25 stores mails for the domain's accounts as Java reads them anew and tells a local sender of unknown recipients; POP3 on port 110 hands them out. The mailboxes are in the snapshot and go back into the project. EmailRun plays four stories over the network; MailCases feeds Java's workers without one (26 cases, de and en). 18 cases name Java bugs that the core does not take. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
149 lines
6.8 KiB
Java
149 lines
6.8 KiB
Java
// Runs a story of FILIUS' e-mail server (EmailServer with its SMTPServer and
|
|
// POP3Server) on a core scenario's network in Java FILIUS and records what
|
|
// really happens: every frame each card saw, every line the server wrote
|
|
// into its window, every line of the generic client that talks to it, and
|
|
// the server's accounts at the end (each message as Email.toString, whether
|
|
// it is new, whether it is marked for deletion).
|
|
//
|
|
// EmailRun <scenario.json> <story> <milliseconds> <out.json>
|
|
//
|
|
// The stories use mail-lan (client on 192.168.0.20, e-mail server on .30,
|
|
// domain filius.de). The server's accounts come from /mailserver/konten.txt
|
|
// as below (anna with two messages, from bob and from carla; bob without),
|
|
// switched on.
|
|
// FILIUS' generic client (ClientBaustein) on .20 sends the commands as they
|
|
// are, one message each, after a pause for the answer.
|
|
// smtp to anna, then QUIT
|
|
// smtpErrors commands out of order, then a mail to a recipient the
|
|
// domain does not know (a notice goes to the local sender)
|
|
// pop3 log in, STAT, LIST, RETR and DELE both messages, QUIT;
|
|
// again: log in, STAT, QUIT
|
|
// pop3Errors unknown user, PASS before USER, wrong password, NOOP before
|
|
// and after log-in, USER after log-in
|
|
// Java's errors in these dialogues are measured without a network
|
|
// (MailCases), so the stories keep to what Java does right.
|
|
|
|
import filius.software.clientserver.ClientBaustein;
|
|
import filius.software.email.Email;
|
|
import filius.software.email.EmailKonto;
|
|
import filius.software.email.EmailServer;
|
|
import filius.software.system.Datei;
|
|
import filius.software.system.Dateisystem;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
public class EmailRun {
|
|
static final String CLIENT = "00000000-0000-0000-0000-000000000201";
|
|
static final String SERVER = "00000000-0000-0000-0000-000000000203";
|
|
|
|
/** The server's accounts before the run, in EmailServer's file format. */
|
|
static final String ACCOUNTS = "anna;filius.de;geheim;Muster;Anna;#Bob Beispiel <bob@filius.de>$<anna@filius.de>$$$$Hallo Anna$Erste Zeile|Zweite Zeile"
|
|
+ "#Carla <carla@andere.de>$Anna Muster <anna@filius.de>,<bob@filius.de>$<chef@filius.de>$$$Termin$Morgen um 10?\n"
|
|
+ "bob;filius.de;passwort;Beispiel;Bob;\n";
|
|
|
|
static ScenarioNetwork network;
|
|
static ClientBaustein client;
|
|
static final List<Object[]> events = new ArrayList<>();
|
|
|
|
static synchronized void event(String who, String what) {
|
|
events.add(new Object[] { System.currentTimeMillis() - network.start, who, what });
|
|
}
|
|
|
|
static void sleepUntil(long milliseconds) throws InterruptedException {
|
|
long rest = network.start + milliseconds - System.currentTimeMillis();
|
|
if (rest > 0) Thread.sleep(rest);
|
|
}
|
|
|
|
static long at;
|
|
|
|
static void connect(int port) throws InterruptedException {
|
|
sleepUntil(at);
|
|
client.setZielIPAdresse("192.168.0.30");
|
|
client.setZielPort(port);
|
|
event("client", "call verbinden " + port);
|
|
client.verbinden();
|
|
at += 2500;
|
|
}
|
|
|
|
/** Each command after the answer to the one before. */
|
|
static void send(String... messages) throws InterruptedException {
|
|
for (String message : messages) {
|
|
sleepUntil(at);
|
|
event("client", "call senden " + message);
|
|
client.senden(message);
|
|
at += 1500;
|
|
}
|
|
}
|
|
|
|
public static void main(String[] args) throws Exception {
|
|
network = ScenarioNetwork.read(args[0]);
|
|
String story = args[1];
|
|
long end = Long.parseLong(args[2]);
|
|
|
|
network.system(SERVER).installApp("filius.software.email.EmailServer", false);
|
|
EmailServer server = (EmailServer) network.system(SERVER).holeSoftware("filius.software.email.EmailServer");
|
|
Dateisystem files = network.system(SERVER).getDateisystem();
|
|
files.speicherDatei("/mailserver", new Datei("konten.txt", "txt", ACCOUNTS));
|
|
server.setMailDomain("filius.de");
|
|
server.setAktiv(true);
|
|
server.hinzuBeobachter((observable, data) -> event("server", String.valueOf(data)));
|
|
network.system(CLIENT).installApp("filius.software.clientserver.ClientBaustein", false);
|
|
client = (ClientBaustein) network.system(CLIENT).holeSoftware("filius.software.clientserver.ClientBaustein");
|
|
client.hinzuBeobachter((observable, data) -> event("client", String.valueOf(data)));
|
|
network.start();
|
|
at = 500;
|
|
|
|
switch (story) {
|
|
case "smtp":
|
|
connect(25);
|
|
send("HELO 192.168.0.20", "MAIL FROM: <bob@filius.de>", "RCPT TO:<anna@filius.de>", "DATA",
|
|
"From: Bob Beispiel <bob@filius.de>\nTo: <anna@filius.de>\nSubject: Gruß\n\nHallo Anna,\nwie geht's?\r\n.\r\n", "QUIT");
|
|
break;
|
|
case "smtpErrors":
|
|
connect(25);
|
|
send("NOOP", "EHLO", "DATA", "MAIL FROM: bob@filius.de", "RCPT TO:<niemand@filius.de>", "DATA",
|
|
"MAIL FROM:<bob@filius.de>", "RCPT TO: anna", "NOOP", "DATA",
|
|
"From: <bob@filius.de>\nTo: <niemand@filius.de>\nSubject: Wer?\n\nIst da jemand?\n.\n", "QUIT");
|
|
break;
|
|
case "pop3":
|
|
connect(110);
|
|
send("USER anna", "PASS geheim", "STAT", "LIST", "RETR 0", "RETR 1", "DELE 0", "DELE 1", "QUIT");
|
|
at += 1000;
|
|
connect(110);
|
|
send("USER anna", "PASS geheim", "STAT", "QUIT");
|
|
break;
|
|
case "pop3Errors":
|
|
connect(110);
|
|
send("NOOP", "USER niemand", "PASS x", "USER anna", "PASS falsch", "PASS geheim", "NOOP", "USER bob", "QUIT");
|
|
break;
|
|
default:
|
|
throw new IllegalArgumentException("unknown story " + story);
|
|
}
|
|
sleepUntil(Math.max(end, at + 3000));
|
|
|
|
ScenarioNetwork.Json out = new ScenarioNetwork.Json();
|
|
out.open('{');
|
|
network.writeFrames(out);
|
|
synchronized (EmailRun.class) {
|
|
out.key("events").open('[');
|
|
for (Object[] each : events) out.open('[').number((Long) each[0]).string((String) each[1]).string((String) each[2]).close(']');
|
|
out.close(']');
|
|
}
|
|
out.key("accounts").open('[');
|
|
for (EmailKonto account : server.getListeBenutzerkonten()) {
|
|
out.open('[').string(account.getBenutzername());
|
|
for (Email email : account.getNachrichten()) {
|
|
out.open('[').string(email.toString()).string(String.valueOf(email.getNeu())).string(String.valueOf(email.getDelete())).close(']');
|
|
}
|
|
out.close(']');
|
|
}
|
|
out.close(']');
|
|
out.key("file").string(files.holeDatei("/mailserver/konten.txt").getDateiInhalt());
|
|
out.close('}');
|
|
network.stop();
|
|
ScenarioNetwork.write(args[3], out);
|
|
System.exit(0);
|
|
}
|
|
}
|