Menu

  • Icona Java Invio email in JAVA

    Per descrivere il funzionamento di questo software è fondamentale:

    • - La conoscenza delle reti con Java (vedi capitolo 12 del corso).
    • - Capire il funzionamento dei componenti principali che regolano la posta elettronica.

    Dunque dando per assodato che sappiate già come avviene lo scambio di informazioni tra due PC, concentriamoci sugli elementi che permettono di spedire un messaggio, che sono:

    • - I programmi di posta elettronica, chiamati tecnicamente user agent.
    • - SMTP che sta per Simple Mail Transfer Protocol, in sostanza un protocollo di comunicazione che permette di recapitare messaggi di posta elettronica.
    • - I server di posta elettronica, dall'inglese mail server.


    Per descrivere questi tre componenti è necessario fare un esempio pratico, ipotizziamo che Francesca voglia inviare un messaggio a Giovanni.
    I software di posta elettronica permettono (sia a Francesca così come anche a Giovanni) di leggere, salvare, inoltrare, eliminare e comporre messaggi; nel caso del nostro esempio, appare evidente che Francesca si servirà di un programma per scrivere il suo messaggio.
    Una volta che il messaggio è stato inviato, il software si occuperà di recapitarlo al server di posta mittente, che lo inoltrerà al server del destinatario.
    Il server di Francesca deve anche considerare dei possibili guasti nel server di Giovanni, quindi se il server di Giovanni è temporaneamente fuori servizio, il messaggio di Francesca rimarrà nel suo server, in attesa di essere inviato più tardi.

    Dunque la situazione ipotizzata, sarà analoga alla seguente rappresentazione:

    Invio email tramite SMTP

    Notiamo dalla figura che il protocollo che serve ad inoltrare l’e-mail fino al server di destinazione è SMTP, un protocollo che sarà implementato nel nostro applicativo e che si servirà di TCP per il trasporto dati affidabile.
    Quando un server di posta si prende in carico un messaggio e lo invia ad un altro server di posta, si dice che agisce come client SMTP, mentre quando riceve un messaggio dagli altri server, si comporta come server SMTP.
    Il nostro obiettivo sarà quello di comunicare con il server SMTP di Francesca, che in sostanza si comporterà come client SMTP.

    Per comunicare con successo con qualsiasi server SMTP, al fine di fornirgli tutte le istruzioni necessarie per recapitare un messaggio, sarà necessario avviare una comunicazione molto simile a quella verbale tra due persone.


    Fase di comunicazione

    Esempio verbale

    1. Riconoscimento

    F: Ciao sono Francesca
    S: Ciao sono Libero.it, ci conosciamo?

    1. Autenticazione

    F: Ho gli occhi blu, labbra rosse, etc..
    S: Francesca, certo che mi ricordo di te.

    1. Specifica del mittente

    F: Digli da parte mia…
    S: Si, ma con chi devo parlare?

    1. Specifica del destinatario

    F: con Giovanni…
    S: Ok e cosa gli devo dire?

    1. Invio del messaggio

    F: Che ci vediamo domani sera.
    S: Va bene, parlerò con lui.

    F = Francesca
    S = Server SMTP

    Proprio come nella comunicazione verbale, anche i server SMTP fanno uso di diversi dialetti, infatti, la fase di riconoscimento può variare a seconda del server; dal semplice “saluto” HELO della prima versione di SMTP senza autenticazione, a EHLO dei nuovi server che fanno uso di diversi tipi di autenticazione. Non gestire l'autenticazione dei mittenti come previsto dalla prima versione di SMTP, oltre al rischio di spam, ci espone alla possibilità di inviare e-mail facendo apparire come mittente l'indirizzo corrispondente ad un altro account.
    Considerando che non esistono più server SMTP insicuri, il nostro software comunicherà con un server SMTP protetto, la scelta del server è stata effettuata tenendo conto del mio provider di connessione a internet ed è ricaduta sul mail server del portale Libero.it di Wind Infostrada.

    Perciò ipotizzando che l’email di Francesca sia del tipo ***@libero.it e l’email di Giovanni del tipo ***@yahoo.it; connettiamoci con il server SMTP di Libero attraverso il comando “telnet smtp.libero.it 25”.
    Una volta connessi al mail server, la vera comunicazione sarà la seguente:


    Comandi SMTP

    Sono cerchiati in rosso i comandi da noi impartiti, ogni nostro comando si conclude la pressione del tasto Invio e ogni risposta ci giunge dal server SMTP di Libero. La comunicazione con il server SMTP di Yahoo! (di Giovanni) sarà automatica e si verificherà subito dopo l’invio del messaggio.

    Dall’immagine del Prompt dei comandi di Windows si evince che, il nostro client dà sei comandi importanti:


    EHLO myName

    Si tratta del comando che permette al server di riconoscere il client, al posto di myName si dovrebbe inserire il nome di dominio del proprio fornitore di DNS dinamico, oppure inserire il proprio indirizzo IP.

    AUTH LOGIN

    Il comando permette di effettuare l’autenticazione.
    La risposta 334 VXNlcm5hbWU6 ci informa che bisogna inserire la nostra username di libero, in formato esteso “username@libero.it” e in base64.
    Una volta inserita la username, la risposta sarà 334 UGFzc3dvcmQ6 che ci indica che questa volta bisogna inserire la nostra password, sempre in formato base64.
    Se tutto va a buon fine otteniamo 235 ... authentication succeeded.

    MAIL FROM <username @libero.it>

    Permette di specificare il mittente del messaggio, in caso di verifica positiva otteniamo in risposta 250 <username@libero.it> sender ok.

    RCPT TO: <username @yahoo.it>

    Permette di specificare il destinatario del messaggio, in caso di verifica positiva otteniamo in risposta 250 <username@yahoo.it> recipient ok.

    DATA

    Permette di inviare il corpo del messaggio nello standard RFC2822 (https://tools.ietf.org/html/rfc2822).

    QUIT

    Chiude la connessione con il server SMTP.


    Importante precisare che il flusso delle informazioni non è cifrato, pertanto se ci sono in ascolto nella vostra rete dei malintenzionati, è possibile che possano captare anche i vostri dati di accesso alla mail.

    Passiamo adesso alla definizione dell’interfaccia grafica del programma di posta elettronica, che ci servirà per eseguire, attraverso comodi bottoni, tutte le istruzioni necessarie per inviare il messaggio al nostro client SMTP.

    In primis il metodo main() si occuperà di istanziare, quindi di eseguire il metodo costruttore che posizionerà gli elementi grafici nella finestra principale del software.

    // Metodo principale che esegue l'istanza
    public static void main(String[] args) {
    new InvioMail();
    }

    È bene precisare che la definizione degli elementi presenti sullo schermo, è specificata nella classe InvioMail() ma non nel metodo costruttore, notiamo altresì la presenza di un elemento personalizzato che eredita da TextField chiamato TextFieldLimitato, l’elemento genera un campo di testo che limita il numero di caratteri immessi dall’utente (nel nostro caso a 40).

    public class InvioMail extends Frame implements WindowListener, ActionListener {
    // Dichiarazine elementi di controllo
    Panel controllo = new Panel();
    Label txt_username = new Label("Username (es. user@libero.it):");
    TextFieldLimitato username = new TextFieldLimitato("", 0, 40);
    Label txt_password = new Label("Password:");
    TextFieldLimitato password = new TextFieldLimitato("", 0, 40);
    Label txt_destinatorio = new Label("Destinatario mail:");
    TextFieldLimitato destinatario = new TextFieldLimitato("", 0, 40);
    Label txt_oggetto = new Label("Oggetto:");
    TextFieldLimitato oggetto = new TextFieldLimitato("", 0, 40);

    // Dichiarazine elementi del corpo della mail
    TextArea messaggio = new TextArea("", 0, 0, TextArea.SCROLLBARS_VERTICAL_ONLY);

    // Dichiarazine elementi di azione
    Panel azione = new Panel();
    Button invia = new Button("Invia");
    Button cancella = new Button("Cancella");

    Nel metodo costruttore saranno disposti e inseriti gli elementi della finestra, con un'opportuna istruzione definiremo l’interattività della finestra stessa (super.addWindowListener(this);), quella per il TextFieldLimitato (this.username.addKeyListener(this.username);) e quella per i singoli bottoni, come “Invia” (this.invia.addActionListener(this);) e “Cancella” (this.cancella.addActionListener(this);), le azioni invocate dalla finestra e dai bottoni saranno gestite all’interno della stessa classe InvioMail.

    // Metodo costruttore che genera la finestra
    public InvioMail() {
    super("Nuovo messaggio"); // Titolo della finestra

    // Creo pannello di controllo e l'aggiungo alla finestra
    this.controllo.setLayout(new GridLayout(4, 2)); // Crea una griglia 4x2
    this.txt_username.setBackground(Color.GRAY); // Colore al Label
    this.controllo.add(this.txt_username); // Aggiunge il testo in 1 riga, 1 colonna
    this.username.addKeyListener(this.username); // Aggiunge eventi tastiera al TextFieldLimitato
    this.controllo.add(this.username); // Aggiunge TextFieldLimitato in 1 riga, 2 colonna
    this.txt_password.setBackground(Color.GRAY); // Colore al Label
    this.controllo.add(this.txt_password); // Aggiunge il testo in 2 riga, 1 colonna
    this.password.addKeyListener(this.password); // Aggiunge eventi tastiera al TextFieldLimitato
    this.password.setEchoChar('*'); // Aggiunge * per caratteri digitati in password
    this.controllo.add(this.password); // Aggiunge TextFieldLimitato in 2 riga, 2 colonna

    this.controllo.add(this.txt_destinatorio); // Aggiunge il testo in 3 riga, 1 colonna
    this.destinatario.addKeyListener(this.destinatario); // Aggiunge eventi tastiera al TextFieldLimitato
    this.controllo.add(this.destinatario); // Aggiunge TextFieldLimitato in 3 riga, 2 colonna
    this.controllo.add(this.txt_oggetto); // Aggiunge il testo in 4 riga, 1 colonna
    this.oggetto.addKeyListener(this.oggetto); // Aggiunge eventi tastiera al TextFieldLimitato
    this.controllo.add(this.oggetto); // Aggiunge TextFieldLimitato in 4 riga, 2 colonna
    super.add(this.controllo, BorderLayout.NORTH); // Inserisce pannello auth in alto

    // Creo area del messaggio e l'aggiungo alla finestra
    super.add(this.messaggio, BorderLayout.CENTER); // Inserisce corpo del messaggio al centro

    // Creo pannello di azione e l'aggiungo alla finestra
    this.azione.setLayout(new GridLayout(1, 2)); // Crea una griglia 1x2
    this.invia.addActionListener(this); // Aggiunge eventi al Bottone invia
    this.azione.add(this.invia); // Aggiunge il bottone in 1 riga, 1 colonna
    this.cancella.addActionListener(this); // Aggiunge eventi al Bottone cancella
    this.azione.add(this.cancella); // Aggiunge il bottone in 1 riga, 2 colonna
    super.add(this.azione, BorderLayout.SOUTH); // Inserisce pannello di azione in basso

    // Proprietà della finestra
    super.setResizable(false); // No al redimensionamento
    super.setSize(400, 350); // Dimensione finestra
    super.setLocation(200, 200); // Posizione
    super.addWindowListener(this); // Interazione
    super.setVisible(true); // Visibilità
    }

    Una volta che l’utente compila i campi richiesti e aziona un bottone, il gesto deve essere catturato, proprio per questo dovrà essere implementato un metodo presente nell’interfaccia ActionListener, questo metodo chiamato actionPerformed ci permetterà di gestire le azioni dell’utente sull’interfaccia; se l’utente preme il tasto “Cancella”, i campi di testo saranno resettati, mentre se preme il tasto “Invia”, verrà inviata l’e-mail attraverso il metodo statico inviaMessaggio presente nella classe TCPClient.

    public void actionPerformed(ActionEvent e) {
    String comando = e.getActionCommand(); // Preleva il comando

    // Cancella la TextArea
    if (comando.equals("Cancella")) {
    // Imposta i campi in base a cosa c'è scritto dentro
    this.destinatario.setText​(this.destinatario.getText());
    this.oggetto.setText(this.oggetto.getText());
    this.messaggio.setText​(this.messaggio.getText());

    // Reimposta i campi
    this.destinatario.setText("");
    this.oggetto.setText("");
    this.messaggio.setText("");
    }

    // Invia il messaggio
    if (comando.equals("Invia")) {
    // Imposta i campi in base a cosa c'è scritto dentro
    this.username.setText(this.username.getText());
    this.password.setText(this.password.getText());
    this.destinatario.setText​(this.destinatario.getText());
    this.oggetto.setText(this.oggetto.getText());
    this.messaggio.setText​(this.messaggio.getText());

    // Invia il messaggio
    TCPClient.inviaMessaggio​(this.username.getText(), this.password.getText(), this.destinatario.getText(), this.oggetto.getText(), this.messaggio.getText());

    // Reimposta i campi
    this.destinatario.setText("");
    this.oggetto.setText("");
    this.messaggio.setText("");
    }
    }

    Il metodo statico inviaMessaggio() della classe TCPClient, si occuperà di instaurare una connessione TCP tra il programma e il client SMTP, vediamo un frammento di codice significativo il tal senso:

    public static void inviaMessaggio(String username, String password, String destinatario, String oggetto, String messaggio) {
    // Sequenza dei comandi da inviare
    String comandi[] = new String[15];
    comandi[0] = "EHLO client";
    comandi[1] = "AUTH LOGIN";
    comandi[2] = Convertitore.inBase64(username);
    comandi[3] = Convertitore.inBase64(password);
    comandi[4] = "MAIL FROM: <" + username + ">";
    comandi[5] = "RCPT to: <" + destinatario + ">";
    comandi[6] = "DATA";
    comandi[7] = "From: " + username;
    comandi[8] = "To: " + destinatario;
    comandi[9] = "Subject: " + oggetto;
    comandi[10] = "";
    comandi[11] = messaggio;
    comandi[12] = ".";
    comandi[13] = "QUIT";


    String risposta = null; // Stringa di caratteri

    try {
    Socket clientSocket = new Socket("smtp.libero.it", 25); // Connessione al server SMTP di libero con handshake
    DataOutputStream scritturaServer = new DataOutputStream​(clientSocket.getOutputStream()); // Buffer di scrittura sul server
    BufferedReader letturaServer = new BufferedReader(new InputStreamReader​(clientSocket.getInputStream())); // Buffer di lettura dal server

    scritturaServer.writeBytes(comandi[0] + '\n'); // Scrive sul server comando di riconoscimento

    // Legge risposte di presentazione del server
    for (int i = 0; i < 8; i++) {
    risposta = letturaServer.readLine(); // Legge dal server
    System.out.println("Risposta del server: " + risposta); // Visualizzazione dei dati ricevuti dal server
    }

    // Scrive e legge dal server per la specifica del mittente e del destinatario
    for (int i = 1; i < 7; i++) {
    scritturaServer.writeBytes(comandi[i] + '\n'); // Scrive sul server
    risposta = letturaServer.readLine(); // Legge dal server
    System.out.println("Risposta del server: " + risposta); // Visualizzazione dei dati ricevuti dal server
    }

    Possiamo notare come è stato inizializzato un array di stringhe con i comandi necessari per dialogare con il client SMTP, inoltre, è stato ritenuto oppurtuno dichiarare una variabile di risposta di tipo String per registrare le risposte ricevute dal server e mostrarle a video.

    Per l'approfondimento dei comandi successivi si rimanda al Capitolo 10 del corso, ad ogni modo si tratta di un insieme d'istruzioni che servono ad autenticarsi al server e inviare il messaggio.


    Per quanto riguarda la codifica degli elementi in formato base64, ho preferito utilizzare una libreria chiamata commons-codec-1.2, inoltre, ho predisposto un'apposita classe chiamata Convertitore che si occupa della codifica (e decodifica) dell'username e password dell'utente.

    Nel file scaricabile è disponibile il progetto completo con relativo codice sorgente.