/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl <filius@c.fameibl.de> ** and Stefan Freischlad ** Filius is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 2 of the License, or ** (at your option) version 3. ** ** Filius is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied ** warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR ** PURPOSE. See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with Filius. If not, see <http://www.gnu.org/licenses/>. */ package filius.software.email; import java.util.LinkedList; import java.util.ListIterator; import java.util.StringTokenizer; import javax.swing.tree.DefaultMutableTreeNode; import filius.Main; import filius.exception.CreateAccountException; import filius.exception.DeleteAccountException; import filius.rahmenprogramm.EingabenUeberpruefung; import filius.rahmenprogramm.I18n; import filius.software.Anwendung; import filius.software.system.Datei; import filius.software.system.InternetKnotenBetriebssystem; /** * * @author Andre Asschoff * * Der emailServer muss wohl neu aufgerollt werden, da ich hier überhaupt kein Thread brauche. * Dies wird durch POP3- und SMTPServer realisiert. Daher wird der nun überarbeitet. * 13.12.2006 */ public class EmailServer extends Anwendung implements I18n { // Attribute private LinkedList listeBenutzerkonten = new LinkedList(); private String mailDomain = "filius.de"; private POP3Server pop3; private SMTPServer smtp; /** Dieses Attribut gibt an, ob der Server bereit ist, auf eingehende * Verbindungsanfragen zu antworten. */ private boolean aktiv = false; private DefaultMutableTreeNode verzeichnis; // Konstruktoren // keine gesondert implementierten /** Ob der Server eingehende Verbindungsanfragen annmimmt. * Das gilt sowohl fuer SMTP wie auch fuer POP3 */ public boolean isAktiv() { return aktiv; } /** Ob der Server eingehende Verbindungsanfragen annmimmt. * Das gilt sowohl fuer SMTP wie auch fuer POP3. <br /> * In dieser Methode wird also sowohl das Attribut dieser * Klasse gesetzt, das fuer SMTP und POP3 gilt, wie auch * die jeweiligen Attribute des POP3Server und des * SMTPServer. */ public void setAktiv(boolean aktiv) { this.aktiv = aktiv; if (pop3 != null) pop3.setAktiv(aktiv); if (smtp != null) smtp.setAktiv(aktiv); } // Methoden /** Hier wird die ueberladene Methode der Oberklasse aufgerufen, * die Benutzerkonten gespeichert und POP3Server und SMTPServer beendet. */ public void beenden() { Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (EmailServer), beenden()"); super.beenden(); kontenSpeichern(); if (pop3 != null) pop3.beenden(); if (smtp != null) smtp.beenden(); } /** Diese Methode wird beim Wechsel vom Entwurfs- in den * Aktionsmodus aufgerufen. Hier wird zuerst die ueberladene * Methode der Oberklasse aufgerufen. Dann werden die Benutzerkonten * aus der Datei des Servers geladen. Danach wird ein neuer * POP3Server und ein neuer SMTPServer erzeugt und initialisiert. * D. h., dass sie auch deren starten()-Methode aufgerufen wird * und das Attribut 'aktiv' gesetzt wird, das angibt, ob ein * Server auf eingehende Verbindungsanfragen wartet. */ public void starten() { Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (EmailServer), starten()"); super.starten(); kontenLaden(); pop3 = new POP3Server(110, this); pop3.setSystemSoftware(getSystemSoftware()); pop3.starten(); pop3.setAktiv(aktiv); smtp = new SMTPServer(25, this); smtp.setSystemSoftware(getSystemSoftware()); smtp.starten(); smtp.setAktiv(aktiv); } /** FUNKTIONIERT * In dieser Methode wird ein neuer Benutzer hinzugefuegt, d.h. ein neues Konto in der * Liste "listeBenutzerkonten" aller Nutzer erstellt. Dazu muessen der Methode alle * für einen Account relevanten Daten uebergeben werden. Zunaechst wird dann abgefragt, * ob es in dieser Liste bereits den Benutzernamen gibt, den der Client für sein Konto * moechte, wenn ja, gibts einen Fehler, wenn nein, ist er akzeptiert. Hier ist darauf * zu achten, dass sich der Benutzername aus dem benutzernamen + "@" + Name des Email- * Servers zusammensetzt. Dies muss explizizt abgefragt werden (siehe letzter Satz) * Danach muss abgefragt werden, ob nicht alle anderen Felder wie Vorname, Strasse, usw. * auch bereits einem existierenden Kontakt in der Liste entsprechen. Unterscheiden sich * diese ganzen sekundaeren Angaben in mindestens einem Punkt, kann der Kontakt schliesslich * vervollstaendigt und der "listeBenutzerkonten" hinzugefuegt werden. Der Benutzername ent- * spricht dann dem gewuenschten Begriff oder Namen gefolgt von einem "@" und dem Namen * des EmailServers. * * @param benutzername * @param passwort * @param nachname * @param vorname * @return boolean * @throws CreateAccountException */ public boolean benutzerHinzufuegen(String benutzername,String passwort,String nachname,String vorname ) throws CreateAccountException { Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (EmailServer), benutzerHinzufuegen("+benutzername+","+passwort+","+nachname+","+vorname+")"); try { EmailKonto konto = new EmailKonto(); for(ListIterator iter = getListeBenutzerkonten().listIterator();iter.hasNext();) { EmailKonto kontoAusListe = (EmailKonto)iter.next(); if (kontoAusListe.getBenutzername().equalsIgnoreCase(benutzername)) { return false; } else if (kontoAusListe.getBenutzername().equals(benutzername) && (kontoAusListe.getNachname().equalsIgnoreCase(nachname)) && (kontoAusListe.getVorname().equals(vorname))) { return false; } else if ((kontoAusListe.getNachname().equals("")) || (kontoAusListe.getVorname().equals(""))) { return false; } } konto.setBenutzername(benutzername); konto.setPasswort(passwort); konto.setNachname(nachname); konto.setVorname(vorname); synchronized(getListeBenutzerkonten()){ getListeBenutzerkonten().add(konto); } benachrichtigeBeobachter(); } catch (Exception e) { throw new CreateAccountException("-ERR This account could not be created. Please try again!"); } kontenSpeichern(); return true; } /** FUNKIONIERT * Diese Methode loescht einen Benutzeraccount aus der Liste "listeBenutzerkonten" des EmailServers. * Dazu wird die gesamte Liste durchlaufen und nach dem entsprechenden Benutzernamen gesucht, der * geloescht werden soll. Danach wird das Passwort abgefragt, die auch als Parameter der Methode * uebergeben werden. Sind beide richtig, wird das Konto aus der Liste entfernt. * Klappt das wird true zurückgegeben, andernfalls eine Exception geworfen. * Hier muss auch wieder beachtet werden dass sich der vollständige Benutzername aus dem eigentlichen * benutzernamen, den der User angibt, + "@" + Name des EmailServers ergibt. Der User muss aber nur * seinen Benutzernamen eingeben, der wird dann vervollständigt. * * @param benutzername * @param passwort * @return boolean * @throws DeleteAccountException */ public boolean kontoLoeschen(String benutzername, String passwort) throws DeleteAccountException { Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (EmailServer), kontoLoeschen("+benutzername+","+passwort+")"); try { for (ListIterator iter = getListeBenutzerkonten().listIterator(); iter.hasNext();) { EmailKonto konto = (EmailKonto) iter.next(); if(konto.getBenutzername().equals(benutzername)) { if(konto.getPasswort().equals(passwort)) { synchronized(getListeBenutzerkonten()){ getListeBenutzerkonten().remove(konto); } benachrichtigeBeobachter(); kontenSpeichern(); return true; } } } } catch (Exception e) { throw new DeleteAccountException(messages.getString("sw_emailserver_msg1")); } return false; } /** FUNKTIONIERT * Hier wird ein EmailKonto zu einer bestehenden Verbindung gesucht. Es wird eine VerbindungsId, die beim * Aufbau einer neuen Verbindung gespeichert wird, uebergeben. Mit ihr wird zunaechst die Liste aller Ver- * bindungen durchlaufen, und abgefragt, ob es eine Verbindung mit dieser uebergebenen Id gibt. Trifft das * zu, wird aus ihr der Benuztername(bname) und das Passwort herausgelesen und temporaer in der Methode ge- * speichert. Mit diesen beiden nun wird die "listeBenutzerkonten", die Liste aller EmailPostfächer, bzw. * registrierter Benutzer dieses EmailServers durchlaufen, um zu suchen, ob es einen Nutzer gibt, der dieses * Konto mit dem bname und Passwort hat. Gibt es das, wird dieses Konto zurueckgegeben. * * @param verbindungsId * @return EmailKonto */ public EmailKonto sucheKonto(String benName, String passwd) { Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (EmailServer), sucheKonto("+benName+","+passwd+")"); EmailKonto ergebnisKonto = new EmailKonto(); for(ListIterator iter = listeBenutzerkonten.listIterator(); iter.hasNext();) { EmailKonto konto = (EmailKonto)iter.next(); if (konto.getBenutzername().equalsIgnoreCase(benName) && konto.getPasswort().equals(passwd)) { ergebnisKonto = konto; //Main.debug.println("=====================SUCHE KONTO============================="); //Main.debug.println("=======GESUCHT: BenName: "+benName+" Passwd: "+passwd+" ====="); //Main.debug.println("=======GEFUNDEN: KontoBen: "+ergebnisKonto.getBenutzername()+" KontoPW: "+ergebnisKonto.getPasswort()+"======================================================"); //Main.debug.println("============================================================="); } } return ergebnisKonto; } /** * Methode, die das Konto anhand des Namens sucht. * @param benName * @return */ public EmailKonto sucheKonto(String benName) { Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (EmailServer), sucheKonto("+benName+")"); EmailKonto konto = new EmailKonto(); for(ListIterator iteratorBenutzerkonten = listeBenutzerkonten.listIterator(); iteratorBenutzerkonten.hasNext();) { EmailKonto tempKonto = (EmailKonto)iteratorBenutzerkonten.next(); if(tempKonto.getBenutzername().equalsIgnoreCase(benName)) { konto = tempKonto; } } return konto; } /** NACHTRAG 2008: Methode ist void, gibt aber boolean erfolg aus, außerdem wird sie in * "nachrichtenKommunikation" aufgerufen, die selbst keinerlei Aufgabe mehr hat! * Hier werden die Benutzerkonten zunaechst in einer Datei gespeichert, anschließend * in das virtuelle Baumverzeichnis eingefuegt * */ public void kontenSpeichern() { Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (EmailServer), kontenSpeichern()"); Datei konten; String tmp; boolean erfolg; tmp = listeBenutzerkontenZuString(listeBenutzerkonten); konten = new Datei("konten.txt", "txt", tmp); erfolg = getSystemSoftware().getDateisystem().speicherDatei(verzeichnis, konten); } private String nltobr(String originalText) { return originalText.replaceAll("\n", "|"); } private String brtonl(String originalText) { return originalText.replaceAll("\\|", "\n"); } private String undoSpecialChar(String escapeStr) { String result = escapeStr.replaceAll("&&00036&&","\\$"); result = result.replaceAll("&&00059&&",";"); result = result.replaceAll("&&00124&&","\\|"); result = result.replaceAll("&&00035&&","\\#"); return result; } private String replaceSpecialChar(String mailtext) { String result = mailtext.replaceAll("\\$", "&&00036&&"); result = result.replaceAll(";", "&&00059&&"); result = result.replaceAll("\\|", "&&00124&&"); result = result.replaceAll("#", "&&00035&&"); return result; } /** NACHTRAG 2008: 2te "while-Schleife" scheint ueberfluessig, scheint dennoch zu gehen. * Hier wird ein Benutzerkonto aus der Liste der Benutzerkonten in einen String umgewandelt. * Der String hat die Form benutzername+kontoDomain+passwort+nachname+vorname+nachrichten. * Dies brauche ich fuer SPEICHERUNG * @param benutzerkonten * @return */ private String listeBenutzerkontenZuString(LinkedList benutzerkonten) { Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (EmailServer), listeBenutzerkontenZuString("+benutzerkonten+")"); String ergebnis = ""; ListIterator iter = benutzerkonten.listIterator(); while (iter.hasNext()) { String emailStr = ""; EmailKonto konto = (EmailKonto) iter.next(); ListIterator iterN = konto.getNachrichten().listIterator(); while(iterN.hasNext()) { Email email = (Email)iterN.next(); emailStr = emailStr+"#"+ (email.getAbsender()!=null ? email.getAbsender() : "") +"$"+llzuStr(email.getEmpfaenger())+"$"+ llzuStr(email.getCc())+"$"+llzuStr(email.getBcc())+"$"+email.getDateReceived()+"$"+ (email.getBetreff()!=null ? email.getBetreff() : "") +"$"+nltobr(replaceSpecialChar(email.getText())); } ergebnis = ergebnis+konto.getBenutzername()+";"+this.mailDomain+";"+konto.getPasswort()+";"+ konto.getNachname()+";"+konto.getVorname()+";"+emailStr+"\n"; } return ergebnis; } /** * Das brauche ich fuers LADEN, ist der Gegensatz zu listeBenutzerkontenZuString. * @param speicherung * @return */ private LinkedList stringZuListeBenutzerkonten(String speicherung) { Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (EmailServer), stringZuListeBenutzerkonten("+speicherung+")"); LinkedList temp = new LinkedList(); String[] strArray; StringTokenizer speicherungTokenizer = new StringTokenizer(speicherung, "\n"); while(speicherungTokenizer.hasMoreTokens()) { EmailKonto konto = new EmailKonto(); String emailKontoInString = speicherungTokenizer.nextToken(); StringTokenizer emailKontoInStringTokenizer = new StringTokenizer(emailKontoInString, ";"); konto.setBenutzername(emailKontoInStringTokenizer.nextToken()); emailKontoInStringTokenizer.nextToken(); konto.setPasswort(emailKontoInStringTokenizer.nextToken()); konto.setNachname(emailKontoInStringTokenizer.nextToken()); konto.setVorname(emailKontoInStringTokenizer.nextToken()); if (emailKontoInStringTokenizer.hasMoreTokens()) { String nachrichten = emailKontoInStringTokenizer.nextToken(); // emails kommen in der Form... // emailStr = emailStr+"$"+ email.getAbsender()+"$"+email.getEmpfaenger()+"$"+email.getDateReceived()+"$"+ // email.getBetreff()+"$"+email.getText(); try { String[] nachrichtenArray = nachrichten.split("#"); for (int i=0; i<nachrichtenArray.length; i++) { strArray = nachrichtenArray[i].split("\\$"); if (strArray.length == 7) { Email email = new Email(); email.setAbsender(strArray[0]); email.setEmpfaenger(strzuLL(strArray[1])); email.setCc(strzuLL(strArray[2])); email.setBcc(strzuLL(strArray[3])); email.setDateReceived(strArray[4]); email.setBetreff(strArray[5]); email.setText(undoSpecialChar(brtonl(strArray[6]))); konto.getNachrichten().add(email); } // else throw new Exception("mail string has more/less than 7 tokens (="+strArray.length+")!"); } } catch (Exception e) { Main.debug.println("EXCEPTION ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (EmailServer), stringZuListeBenutzerkonten: mail does not have enough or too many entries"); e.printStackTrace(Main.debug); } } temp.add(konto); } return temp; } /** * Hier werden die Benutzerkonten wieder geladen * */ public void kontenLaden() { Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (EmailServer), kontenLaden()"); Datei konten; konten = getSystemSoftware().getDateisystem().holeDatei(verzeichnis, "konten.txt"); if (konten != null) { setListeBenutzerkonten(stringZuListeBenutzerkonten(konten.getDateiInhalt())); } else { Main.debug.println("ERROR ("+this.hashCode()+"): Konten laden fehlgeschlagen"); } } /** * Diese Mehtode wandelt eine LL in einen String, die einzelnen Listenelemente * durch Kommata getrennt. * @param args * @return */ private String llzuStr (LinkedList args) { Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (EmailServer), llzuStr("+args+")"); String str = ""; for(ListIterator iter = args.listIterator(); iter.hasNext();) { str = str+((String)iter.next()); if (iter.hasNext()) str+=","; } return str; } /** * Wandelt einen String in eine LinkedList, jeder * der durch Kommata getrennten Werte des Strings * wird ein eigenes Listenelement. * @param args * @return */ private LinkedList strzuLL (String args) { Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (EmailServer), strzuLL("+args+")"); LinkedList ergebnis = new LinkedList(); String [] argument = args.split(","); for(int i = 0; i < argument.length; i++) { ergebnis.add(argument[i]); } return ergebnis; } // Get- und Set-Methoden public void setSystemSoftware(InternetKnotenBetriebssystem bs) { Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (EmailServer), setSystemSoftware("+bs+")"); super.setSystemSoftware(bs); /** * TODO check wechmachen */ boolean check/*this.verzeichnis*/ = getSystemSoftware().getDateisystem().erstelleVerzeichnis(getSystemSoftware().getDateisystem(). getRoot(), "mailserver"); this.verzeichnis = getSystemSoftware().getDateisystem().verzeichnisKnoten(getSystemSoftware().getDateisystem(). getRoot(),"mailserver"); } public synchronized void setListeBenutzerkonten(LinkedList listeBenutzerkonten) { this.listeBenutzerkonten = listeBenutzerkonten; } public void setPOP3Server(POP3Server pop3) { this.pop3 = pop3; } public void setSMTPServer(SMTPServer smtp) { this.smtp = smtp; } public synchronized LinkedList getListeBenutzerkonten() { return listeBenutzerkonten; } public POP3Server getPOP3Server() { return pop3; } public SMTPServer getSMTPServer() { return smtp; } public POP3Server holePop3() { return pop3; } public SMTPServer holeSmtp() { return smtp; } public String getMailDomain() { return mailDomain; } public void setMailDomain(String mailDomain) { if (mailDomain != null && EingabenUeberpruefung.isGueltig(mailDomain, EingabenUeberpruefung.musterDomain)) this.mailDomain = mailDomain; } public void emailWeiterleiten(Email email, String absender, String rcpt) { Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (EmailServer), emailWeiterleiten("+email+","+absender+","+rcpt+")"); Object[] args; args = new Object[3]; args[0] = email; args[1] = absender; args[2] = rcpt; ausfuehren("weiterleiten", args); } /** * hier wird die Email versendet, dazu wird der SMTPClient benutzt, dessen * verwende Methode aufgerufen wird. * 1. erstellen eines neuen SMTPClienten * 2. starten des Clienten * 3. versenden * Es wird die Email übergeben und genau ein Empfaenger, an den die Email weiter- * geleitet werden soll. Dies wird so oft wiederholt in "verarbeiteEmail", bis alle * Empfaenger, an die weitergeleitet werden soll, durchgelaufen sind. <br /> * Diese Methode <b> blockiert </b> den Thread! * @param email * @param rcpt */ public void weiterleiten(Email email, String absender, String rcpt) { Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (EmailServer), weiterleiten("+email+","+absender+","+rcpt+")"); // erstelle Client für den Versand SMTPClient clientFuerWeiterleitung = new SMTPClient(this); clientFuerWeiterleitung.starten(); clientFuerWeiterleitung.versendeEmail(null, email, absender, rcpt); } }