/*
** 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 filius.Main;
import filius.exception.TimeOutException;
import filius.exception.VerbindungsException;
import filius.rahmenprogramm.EingabenUeberpruefung;
import filius.rahmenprogramm.I18n;
import filius.software.Anwendung;
import filius.software.clientserver.ClientAnwendung;
import filius.software.transportschicht.TCPSocket;
public class SMTPClient extends ClientAnwendung implements I18n {
/** Die Anwendung, die diesen SMTP-Client verwendet
* (das kann eine EmailAnwendung zum Versand einer erstellten
* Nachricht oder ein EMailServer zur Weiterleitung einer
* empfangenen Nachricht sein) */
private Anwendung anwendung = null;
// Konstruktoren
public SMTPClient(Anwendung anwendung) {
super();
Main.debug.println("INVOKED-2 ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (SMTPClient), constr: SMTPClient("+anwendung+")");
this.anwendung = anwendung;
this.setSystemSoftware(anwendung.getSystemSoftware());
}
// Methoden
/**
* Versenden einer Email Hier wird eine defaultServerIP übergeben, die
* anzeigt, diese Methode als Teil einer Weiterleitung aufgerufen wird. Wenn
* hier NULL übergeben wird, dann wird diese Client-Mehtode vom
* SMTP-Mitarbeiter als Teil der Weiterleitung aufgerufen. Ist eine
* DefaultServerIP mit über- geben, dann wird sie von der Email Anwendung
* gesendet, und die hat ihren Standartmailserver in den Einstellungen, der
* somit dann übergeben werden kann.
*
* Neben der Email, die eindeutig ist, werden dann noch alle Empfaenger
* übereben, die rcpts, die aus einem langen String von Empfaenger, Cc, und
* Bcc bestehen, wenn sie von der Email- Anwendung aufgerufen werden, und
* aus einem langen String von rcpts (NUR-NOCH-EMPFAENGER, die ausgesondert
* wurden, je nach dem, ob sie im Teil der Weiterleitung des
* SMTP-Mitarbeiters schon abgespeichert wurden, oder eben nicht und
* deswegen weiter versendet werden.)
*
* <ul>
* <li> evtl. Aufloesen der ServerIP-Adresse (bei weiterleiten, sonst nicht)
* </li>
* <li> initialisieren des Sockets (Verbindungsaufbau) </li>
* <li> Versand der Email </li>
* <li> Verbindungsabbau </li>
* </ul>
*/
public void versendeEmail(String defaultServerIP, Email email, String absender, String rcpts) {
Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (SMTPClient), versendeEmail("+defaultServerIP+","+email+","+absender+","+rcpts+")");
String serverAdresse;
String empfaengerAdresse;
String[] zuSendenAn = rcpts.split(",");
for (int i = 0; i < zuSendenAn.length; i++) {
empfaengerAdresse = (String) zuSendenAn[i];
if (defaultServerIP == null) {
serverAdresse = loeseURLauf(empfaengerAdresse);
Main.debug
.println(getClass()
+ "\n\tServer Adresse bei Weiterleitung fuer Maildomain "
+ empfaengerAdresse + " ist: " + serverAdresse);
}
else {
serverAdresse = defaultServerIP;
}
Object[] args;
args = new Object[2];
args[0] = serverAdresse;
args[1] = new Integer(25);
ausfuehren("initialisiereSocket", args);
args = new Object[3];
args[0] = email;
args[1] = absender;
args[2] = empfaengerAdresse;
ausfuehren("versenden", args);
ausfuehren("schliesseSocket", null);
}
}
/** Diese Methode ist blockierend */
public void initialisiereSocket(String zielAdresse, Integer port) {
Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (SMTPClient), initialisiereSocket("+zielAdresse+","+port+")");
try {
socket = new TCPSocket(getSystemSoftware(), zielAdresse, port);
socket.verbinden();
if (socket.istVerbunden())
anwendung.benachrichtigeBeobachter(messages.getString("sw_smtpclient_msg1")
+" " + socket.holeZielIPAdresse() + ":"
+ socket.holeZielPort() + " " + messages.getString("sw_smtpclient_msg2"));
}
catch (Exception e) {
e.printStackTrace(Main.debug);
socket = null;
anwendung.benachrichtigeBeobachter(e);
}
}
/**
* Diese Methode ist <b>blockierend</b>.
*/
public void schliesseSocket() {
Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (SMTPClient), schliesseSocket()");
if (socket != null) {
socket.schliessen();
anwendung.benachrichtigeBeobachter(messages.getString("sw_smtpclient_msg1")
+ socket.holeZielIPAdresse() + ":" + socket.holeZielPort()
+ messages.getString("sw_smtpclient_msg3"));
socket = null;
}
}
private boolean schickeHelo() throws Exception {
Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (SMTPClient), schickeHelo()");
String empfang;
empfang = socket.empfangen();
if (empfang.startsWith("220")) {
socket.senden("HELO " + this.getSystemSoftware().holeIPAdresse());
empfang = socket.empfangen();
if (empfang.startsWith("250")) {
return true;
}
else {
throw new Exception (empfang);
}
}
else {
return false;
}
}
/**
* Mit dieser Methode wird eine Email versendet. Es wird eine Email
* entgegengenommen, und aus ihr Attribute entnommen, die dann einzeln
* versendet werden nach SMTP-manier. Dazu werden verschiedene Methoden der
* Reihe nach aufgerufen, die in vordefinierter Abfolge die einzelnen
* Elemente einer Email versenden und auf ein Response des Servers warten.
* Nur bei erfolgreichem Response geht es weiter.
*
* @param email
*/
public boolean versenden(Email email, String absender, String rcpts) {
Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (SMTPClient), versenden("+email+","+absender+","+rcpts+")");
boolean erfolg = true;
if (socket == null) return false;
else {
try {
erfolg = schickeHelo();
if (erfolg) erfolg = schickeMailFrom(absender);
if (erfolg) erfolg = schickeRcptTo(rcpts);
if (erfolg) erfolg = schickeDataBeginn();
if (erfolg) erfolg = schickeData(email);
if (erfolg) schickeQuit();
if (erfolg) {
if (anwendung instanceof EmailAnwendung) {
((EmailAnwendung)anwendung).getGesendeteNachrichten().add(email);
}
}
anwendung.benachrichtigeBeobachter();
}
catch (Exception e) {
e.printStackTrace(Main.debug);
anwendung.benachrichtigeBeobachter(e);
erfolg = false;
}
//Main.debug.println("SMTPClient: versenden() beendet");
return erfolg;
}
}
/**
* Dient der Aufloesung der EmailAdressen-Domains auf den Empfaengerangaben
* zu einer Ip-Adresse, damit zum dortigen MTA eine Verbindung aufgebaut
* werden kann. Es muss nur der Domainnamen eingegeben werden, der Socket
* loest sofort auf die IP auf...
*
* @param url
* @return
*/
public String loeseURLauf(String url) {
Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (SMTPClient), loeseURLauf("+url+")");
String[] teileDerEmail = url.split("@");
return getSystemSoftware().holeDNSClient().holeIPAdresseMailServer(teileDerEmail[1]);
}
/**
* Hier wird der MailFrom an den Empfaenger versendet bsp fuer MailFrom:
* "andre.asschoff@web.de", "carsten@gmx.de" Dazu wird aus dem Absender der
* Email der getMailFrom() gebastelt
*
* @param email
* @return
* @throws Exception
*/
private boolean schickeMailFrom(String absender)
throws Exception {
Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (SMTPClient), schickeMailFrom("+absender+")");
if (EingabenUeberpruefung.isGueltig(absender,
EingabenUeberpruefung.musterEmailAdresse)) {
socket.senden("MAIL FROM: <" + absender + ">");
String empfangen = socket.empfangen();
if (empfangen.substring(0, 3).equals("250")) {
return true;
}
else {
throw new Exception(empfangen);
}
}
else {
return false;
}
}
/**
* Hier wird RcptTo und RcptToDomain versendet Es können auch mehrere
* Empfaenger angegeben werden, die durch ein Komma voneinander getrennt
* sind.
*
* @param email
* @return
* @throws Exception
*/
private boolean schickeRcptTo(String rcpts) throws Exception {
Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (SMTPClient), schickeRcptTo("+rcpts+")");
String[] empfaenger = rcpts.split(",");
for (int i = 0; i < empfaenger.length; i++) {
if (EingabenUeberpruefung.isGueltig(empfaenger[i],
EingabenUeberpruefung.musterEmailAdresse)) {
socket.senden("RCPT TO:<" + empfaenger[i] + ">");
String empfangen2 = socket.empfangen();
if (!empfangen2.substring(0, 3).equals("250")
&& !empfangen2.substring(0, 3).equals("251")) {
throw new Exception (empfangen2);
}
}
else {
return false;
}
}
return true;
}
/**
* Hier wird nur "DATA" gesendet, was den nachfolgende Email-Versand
* anzeigt. Da DATA laut RFC 821 nur einen Intermediate Status andeutet,
* bliebe zu ueberlegen, ob nicht das anschliessende senden der Nachricht in
* dieser Methode gleich mit aufgerufen wird...
*
* @return
* @throws Exception
*/
private boolean schickeDataBeginn() throws Exception {
Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (SMTPClient), schickeDataBeginn()");
socket.senden("DATA");
String empfangen3 = socket.empfangen();
if (empfangen3.substring(0, 3).equals("354")) {
return true;
}
else {
throw new Exception(empfangen3);
}
}
/**
* Hier wird der Datenteil der Email an sich versendet.
*
* @param email
* @return
* @throws Exception
*/
private boolean schickeData(Email email) throws Exception {
Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (SMTPClient), schickeData("+email+")");
if (email != null) {
socket.senden(email.toString() + "\r\n.\r\n");
String empfangen34 = socket.empfangen();
if (empfangen34.startsWith("250")) {
return true;
}
else {
throw new Exception (empfangen34);
}
}
else {
return false;
}
}
/**
* Hiermit wird ein Quit gesendet, die zum Beenden der Uebertragung dient.
*
* @return
* @throws TimeOutException
* @throws VerbindungsException
*/
private boolean schickeQuit() throws VerbindungsException, TimeOutException {
Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (SMTPClient), schickeQuit()");
String empfangen;
socket.senden("QUIT");
empfangen = socket.empfangen();
if (empfangen.startsWith("221")) {
return true;
}
else {
return false;
}
}
}