Visualizzazione dei risultati da 1 a 7 su 7
  1. #1
    Moderatore di Programmazione L'avatar di LeleFT
    Registrato dal
    Jun 2003
    Messaggi
    17,162

    [PILLOLA][Java] Inviare mail con Java

    Come richiesto, avvio questa nuova pillola.
    L'argomento, come indicato nel titolo, è l'invio delle mail utilizzando Java.
    La classe che posterò sarà in grado di inviare una mail a più destinatari (anche in copia) e di aggiungere un (uno solo!) eventuale allegato.

    Premessa: l'invio delle mail avviene tramite connessione con un server SMTP. Questo significa che tutto il traffico sarà gestito dal protocollo SMTP (Rif: RFC 821 e RFC 2821).

    Vediamo come funzionerà la classe: la classe contiene un solo metodo pubblico statico che prende in ingresso i seguenti parametri:

    01) Nome o indirizzo IP del server SMTP
    02) Elenco di destinatari
    03) Elenco di eventuali destinatari in copia
    04) Oggetto della mail
    05) Messaggio (come elenco di righe)
    06) Nome dell'eventuale file allegato
    07) Nome utente per eventuale autenticazione
    08) Password per eventuale autenticazione
    09) Nome da visualizzare nel campo "From"
    10) Indirizzo da utilizzare nel campo "From"
    11) Nome da utilizzare per il file allegato
    12) Indirizzo a cui inviare l'eventuale conferma di lettura
    13) Un parametro che indica se è necessaria l'autenticazione
    14) Un parametro che indica se è richiesta la conferma di recapito

    e li utilizza, ovviamente, per fare le sue considerazioni.
    La classe, per poter effettuare l'autenticazione, necessita di una seconda classe da richiamare per ottenere le stringhe di accesso codificate, se l'accesso prevede autenticazione CRAM-MD5 (Ref: RFC 2195 e RFC 1321).
    Non sto a spiegare nel dettaglio come avviene la comunicazione SMTP fra la classe ed il server (a tale scopo, per chi fosse interessato, ho aggiunto il riferimento alle RFC).
    A grandi linee avviene questo: la classe effettua una connessione sulla porta 25 con il server; questo si presenta ed inizia il dialogo. La classe si presenta, il server risponde indicando tutte le feature che mette a disposizione, quindi la classe invia la richiesta per una nuova mail. Invia tutta la mail utilizzando lo standard MIME (RFC 1521 e RFC 2045), quindi chiude la connessione con il server.

    Il codice della classe principale è il seguente:
    codice:
    import java.io.*;
    import java.net.*;
    
    public class SendMail {
       static  int SMTPport =  25;
       static  Socket  socket;
       static  boolean[] authMechanism = null;
    
       public static void sendMail(String mailServer,
                                   String[] recipient,
                                   String[] cc,
                                   String subject,
                                   String[] messaggio,
                                   String fileName,
                                   String userName,
                                   String password,
                                   String nomeUtente,
                                   String from,
                                   String nomeFileVis,
                                   String confLettura,
                                   boolean requireLogin,
                                   boolean confReceipt) {
          String linea = "";
          boolean autenticated = false;
          authMechanism = new boolean[3];
          authMechanism[0] = false;   // CRAM-MD5
          authMechanism[1] = false;   // LOGIN
          authMechanism[2] = false;   // PLAIN
    
          try {
             Socket s = new Socket(mailServer, 25);
    
             BufferedReader in = new BufferedReader( new InputStreamReader(s.getInputStream(), "8859_1") );
             BufferedWriter out = new BufferedWriter( new OutputStreamWriter(s.getOutputStream(), "8859_1") );
    
             linea = in.readLine();
    
             String boundary = "Dat_Sep_Str_#COD#";   // -- Data Separator String --
             sendln(in, out, "EHLO " + InetAddress.getLocalHost().getHostName());   // + userName
    
             recuperaMechanism( in );
    
             if ( requireLogin ) {
                for(int i=0; i<authMechanism.length; i++) {
                   if ( authMechanism[i] && !autenticated ) {
                      autenticated = true;
                      sendLogin(0, in, out, userName, password, i);
                   }
                }
                if ( !autenticated ) {
                   System.out.println("Autenticazione non riuscita: nessun metodo di autenticazione rilevato o metodo non supportato!");
                }
             }
    
             sendln(in, out, "MAIL FROM: <"+ InetAddress.getLocalHost().getHostName() + ">");
             for (int i=0; i<recipient.length; i++)
                sendln(in, out, "RCPT TO: <" + recipient[i] + ">" + (confReceipt ? " NOTIFY=SUCCESS" : "") );
             for (int i=0; i<cc.length; i++)
                sendln(in, out, "RCPT TO: <" + cc[i] + ">");
             sendln(in, out, "DATA");
             sendln(out, "MIME-Version: 1.0");
             sendln(out, "Subject: " + subject);
             sendln(out, "From: " + userName + " <" + from + ">");
             for (int i=0; i<recipient.length; i++)
                sendln(out, "To: <" + recipient[i] + ">");
             for (int i=0; i<cc.length; i++)
                sendln(out, "Cc: <" + cc[i] + ">");
             sendln(out, "Disposition-Notification-To: "+confLettura);
             sendln(out, "X-Confirm-Reading-To: "+confLettura);
             sendln(out, "Content-Type: multipart/mixed; boundary=\"" + boundary +"\"");
             sendln(out, "\r\n--" + boundary);
    
             // Send the body
             sendln(out, "Content-Type: text/plain; charset=\"us-ascii\"\r\n");
             for (int i=0; i<messaggio.length; i++)
                sendln(out, messaggio[i]);
             sendln(out, "\r\n--" +  boundary );
    
             // send the attachment
             String nomeFile = (new File(nomeFileVis)).getName();
             sendln(out, "Content-Type:Application/Octet-stream; name="+nomeFile);
             sendln(out, "Content-Disposition: attachment;filename=\""+nomeFile+"\"");
             sendln(out, "Content-transfer-encoding: base64\r\n");
             MIMEBase64.encode(fileName, out);
    
             sendln(out, "\r\n\r\n--" + boundary + "--\r\n");
             sendln(in, out,".");
             sendln(in, out, "QUIT");
             s.close();
          } catch (Exception e) {
             e.printStackTrace();
          }
       }
    
       private static void recuperaMechanism(BufferedReader in) throws Exception {
          String linea = "";
          boolean termina = false;
    
          while( !termina ) {
             linea = in.readLine();
             termina = linea.charAt(3) != '-';
             authMechanism[2] = authMechanism[2] || ((linea.indexOf("AUTH") > 0) && (linea.indexOf("LOGIN") > 0));
             authMechanism[1] = authMechanism[1] || ((linea.indexOf("AUTH") > 0) && (linea.indexOf("PLAIN") > 0));
             authMechanism[0] = authMechanism[0] || ((linea.indexOf("AUTH") > 0) && (linea.indexOf("CRAM-MD5") > 0));
          }
       }
    
       public static void sendLogin(int tipo, BufferedReader in, BufferedWriter out, String utente, String password, int metodo) {
          String resp = "";
          try {
             switch( metodo ) {
                case 0:   // AUTH CRAM-MD5
    
                   out.write("AUTH CRAM-MD5" + "\r\n");
                   out.flush();
                   resp = in.readLine();
    
                   if ( !resp.startsWith("334") ) {
                      System.out.println("Errore: il server non ha risposto in modo atteso alla richiesta di AUTH CRAM-MD5: " + resp);
                   } else {
                      resp = resp.substring(4, resp.length());
                      resp = CRAM_MD5.getLoginString(utente, password, resp);
                      out.write(resp + "\r\n");
                      out.flush();
                      resp = in.readLine();
    
                      if ( !resp.startsWith("235") ) System.out.println("Errore: il server non ha accettato l'autenticazione: " + resp);
                   }
                   break;
    
                case 1:   // AUTH LOGIN
    
                   out.write("AUTH LOGIN" + "\r\n");
                   out.flush();
                   resp = in.readLine();
    
                   if ( !resp.equals("334 VXNlcm5hbWU6") ) System.out.println("Errore: il server non ha risposto in modo atteso alla richiesta di AUTH LOGIN: " + resp);
    
                   out.write((new sun.misc.BASE64Encoder()).encode(utente.getBytes("UTF8")) + "\r\n");
                   out.flush();
                   resp = in.readLine();
    
                   if ( !resp.equals("334 UGFzc3dvcmQ6") ) System.out.println("Errore: il server non ha accettato il nome utente: " + resp);
    
                   if ( !password.equals("") ) {
                      out.write((new sun.misc.BASE64Encoder()).encode(password.getBytes("UTF8")) + "\r\n");
                      out.flush();
                      resp = in.readLine();
    
                      if ( !resp.startsWith("235") ) System.out.println("Errore: il server non ha accettato il nome utente: " + resp);
                   }
                   break;
    
                case 2:   // AUTH PLAIN
    
                   out.write("AUTH PLAIN" + "\r\n");
                   out.flush();
                   resp = in.readLine();
    
                   if ( !resp.startsWith("334") ) System.out.println("Errore: il server non ha risposto in modo atteso alla richiesta di AUTH PLAIN: " + resp);
    
                   resp = "\0" + utente + "\0" + password;
                   out.write((new sun.misc.BASE64Encoder()).encode(resp.getBytes("UTF8")) + "\r\n");
                   out.flush();
                   resp = in.readLine();
    
                   if ( !resp.startsWith("235") ) System.out.println("Errore: il server non ha accettato l'autenticazione: " + resp);
                   break;
             }
    
          } catch (Exception e) { e.printStackTrace(); }
          
       }
    
       private static void sendln(BufferedReader in, BufferedWriter out, String s) {
          try {
             out.write(s + "\r\n");
             out.flush();
             s = in.readLine();
          } catch (Exception e) {
             e.printStackTrace();
          }
       }
    
       private static void sendln(BufferedWriter out, String s) {
          try {
             out.write(s + "\r\n");
             out.flush();
          } catch (Exception e) {
             e.printStackTrace();
          }
       }
    }
    "Perchè spendere anche solo 5 dollari per un S.O., quando posso averne uno gratis e spendere quei 5 dollari per 5 bottiglie di birra?" [Jon "maddog" Hall]
    Fatti non foste a viver come bruti, ma per seguir virtute e canoscenza

  2. #2
    Moderatore di Programmazione L'avatar di LeleFT
    Registrato dal
    Jun 2003
    Messaggi
    17,162

    (continua)

    Mentre il codice della classe CRAM_MD5 (utilizzata per l'autenticazione) è il seguente:
    codice:
    import javax.crypto.*;
    import javax.crypto.spec.*;
    import java.security.*;
    
    public class CRAM_MD5 {
    
       private static void bcopy(byte[] in, byte[] out) {
          for(int j=0; j<in.length; j++) out[j] = in[j];
       }
    
       private static String norm(String s) { return (s.length() < 2) ? "0" + s : s; }
    
       private static int conv(int val) { return (val < 0) ? 256 + val : val;}
    
       private static String toHexDigest(byte[] dig) {
          String res = "";
          for(int j=0; j<dig.length; j++) res += norm(Integer.toHexString(conv(dig[j])));
          return res;
       }
    
       public static String getLoginString(String user, String pass, String challenge) throws Exception {
    
          byte[] decMsg = (new sun.misc.BASE64Decoder()).decodeBuffer(challenge);
          byte[] k_ipad = new byte[64];
          byte[] k_opad = new byte[64];
          byte[] digest = null;
    
          bcopy(pass.getBytes("UTF8"), k_ipad);
          bcopy(pass.getBytes("UTF8"), k_opad);
    
          for(int j=0; j<64; j++) {
             k_ipad[j] ^= (byte) 0x36;
             k_opad[j] ^= (byte) 0x5c;
          }
    
          // Inner MD5
          MessageDigest md = MessageDigest.getInstance("MD5");
          md.update( k_ipad );
          md.update( decMsg );
          digest = md.digest();
    
          // Outer MD5
          MessageDigest md2 = MessageDigest.getInstance("MD5");
          md2.update( k_opad );
          md2.update( digest );
    
          String result = toHexDigest( md2.digest() );
    
          return (new sun.misc.BASE64Encoder()).encode( (user + " " + result).getBytes("UTF8") );
       }
    }
    Delle considerazioni vanno fatte su alcuni dei parametri:
    1) L'elenco dei destinatari in copia può essere vuoto (può non essere necessario inviare la mail in copia a qualcuno). Per far questo si passa alla classe un array di dimensione 0:
    codice:
    String[] cc = new String[0]
    2) fileName e nomeFileVis possono essere diversi: il primo indica il file fisico su disco (con tutto il percorso), mentre il secondo indica il nome che verrà visualizzato nella mail; questo significa che il file che io voglio inviare (quello in mio possesso) può chiamarsi "pippo.txt", ma voglio che il destinatario lo veda come se si chiamasse "Pluto.txt";

    3) nomeUtente indica il nome che verrà visualizzato nel campo "From:" del destinatario (ad esempio, "LeleFT"), mentre from indica l'indirizzo e-mail associato al mittente;

    4) Mentre per la conferma di lettura è possibile specificare un indirizzo a cui inviarla, per la conferma di recapito ciò non può essere fatto: la conferma di recapito torna indietro all'indirizzo e-mail del mittente. Per questo è previsto solamente un parametro booleano (confReceipt) che specifica se richiederla o meno.


    Correzioni e suggerimenti sono sempre ben accetti.


    Ciao.
    "Perchè spendere anche solo 5 dollari per un S.O., quando posso averne uno gratis e spendere quei 5 dollari per 5 bottiglie di birra?" [Jon "maddog" Hall]
    Fatti non foste a viver come bruti, ma per seguir virtute e canoscenza

  3. #3

  4. #4
    Moderatore di Programmazione L'avatar di LeleFT
    Registrato dal
    Jun 2003
    Messaggi
    17,162

    Esempio di utilizzo

    Nella fretta ho scordato una cosa abbastanza importante: il classico esempio di utilizzo della classe.
    Vediamo in sostanza, come può essere banalmente utilizzata la classe.

    Supponiamo di disporre di un server SMTP che non necessita di autenticazione (tipo i server intranet delle aziende) all'indirizzo 192.168.1.2 e di voler inviare una mail dall'indirizzo "info@miodominimo.it" all'indirizzo destinatario "pippo@altrohost.it".
    codice:
    String mailServer = "192.168.1.2";
    String[] recipient {"pippo@altrohost.it"};
    String[] cc = new String[0];   // Non necessito di destinatari in copia
    String subject = "Messaggio di prova";   // L'oggetto della mail
    String[] messaggio = {"Ciao Pippo!", "Come va?", "Questa è una mail di prova."};
    String fileName = "C:\\Allegato.zip";   // Allego un file ZIP
    String userName = "";   // Non necessito di autenticazione
    String password = "";   // Non necessito di autenticazione
    String nomeUtente = "Azienda";   // Il nome che verrà visualizzato al destinatario
    String from = "info@miodominio.it";
    String nomeFileVis = "Ordine.zip";   // Il destinatario vedrà un allegato che si chiama "Ordine.zip"
    String confLettura = "info@miodominio.it";   // Voglio la conferma di lettura
    boolean requireLogin = false;   // Non necessito di login
    boolean confReceipt = false;   // Non mi interessa la notifica di recapito
    
    // Ora invio la mail:
    SendMail.sendMail(mailServer, recipient, cc, subject, messaggio, fileName, userName, password, nomeUtente, from, nomeFileVis, confLettura, confReceipt);
    La versione della classe che ho postato non tiene conto che l'utente potrebbe non voler spedire alcun allegato... le correzioni da apportare per poter offrire all'utente tale possibilità sono poche: è sufficiente inserire delle apposite condizioni sulla presenza o meno di un valore consistente per i campi relativi all'allegato.

    Se, invece, disponiamo di un server che richiede autenticazione (esempio, un server a disposizione da parte del nostro ISP), il precedente esempio diventerebbe:
    codice:
    String mailServer = "192.168.1.2";
    String[] recipient {"pippo@altrohost.it"};
    String[] cc = new String[0];   // Non necessito di destinatari in copia
    String subject = "Messaggio di prova";   // L'oggetto della mail
    String[] messaggio = {"Ciao Pippo!", "Come va?", "Questa è una mail di prova."};
    String fileName = "C:\\Allegato.zip";   // Allego un file ZIP
    String userName = "nome_utente";   // Necessito di autenticazione
    String password = "password";   // Necessito di autenticazione
    String nomeUtente = "Azienda";   // Il nome che verrà visualizzato al destinatario
    String from = "info@miodominio.it";
    String nomeFileVis = "Ordine.zip";   // Il destinatario vedrà un allegato che si chiama "Ordine.zip"
    String confLettura = "info@miodominio.it";   // Voglio la conferma di lettura
    boolean requireLogin = true;   // Necessito di login
    boolean confReceipt = false;   // Non mi interessa la notifica di recapito
    
    // Ora invio la mail:
    SendMail.sendMail(mailServer, recipient, cc, subject, messaggio, fileName, userName, password, nomeUtente, from, nomeFileVis, confLettura, confReceipt);
    Ciao.
    "Perchè spendere anche solo 5 dollari per un S.O., quando posso averne uno gratis e spendere quei 5 dollari per 5 bottiglie di birra?" [Jon "maddog" Hall]
    Fatti non foste a viver come bruti, ma per seguir virtute e canoscenza

  5. #5
    quante email è possibile inviare? se ad esempio avessi 1000 iscritti al sito che devono ricevere newsletter non corro il rischio di un crash?
    Per una battaglia sono sempre a disposizione

  6. #6
    a me da questi errori


    codice:
    Description	Resource	Path	Location	Type Access restriction: The constructor BASE64Decoder() is not accessible due to restriction on required library C:\Program Files\Java\jre6\lib\rt.jar	CRAM_MD5.java	/External_ip/src	line 24	Java Problem Access restriction: The constructor BASE64Encoder() is not accessible due to restriction on required library C:\Program Files\Java\jre6\lib\rt.jar	CRAM_MD5.java	/External_ip/src	line 50	Java Problem Access restriction: The constructor BASE64Encoder() is not accessible due to restriction on required library C:\Program Files\Java\jre6\lib\rt.jar	SendMail.java	/External_ip/src	line 139	Java Problem Access restriction: The constructor BASE64Encoder() is not accessible due to restriction on required library C:\Program Files\Java\jre6\lib\rt.jar	SendMail.java	/External_ip/src	line 146	Java Problem Access restriction: The constructor BASE64Encoder() is not accessible due to restriction on required library C:\Program Files\Java\jre6\lib\rt.jar	SendMail.java	/External_ip/src	line 163	Java Problem Access restriction: The method decodeBuffer(String) from the type CharacterDecoder is not accessible due to restriction on required library C:\Program Files\Java\jre6\lib\rt.jar	CRAM_MD5.java	/External_ip/src	line 24	Java Problem Access restriction: The method encode(byte[]) from the type CharacterEncoder is not accessible due to restriction on required library C:\Program Files\Java\jre6\lib\rt.jar	CRAM_MD5.java	/External_ip/src	line 50	Java Problem Access restriction: The method encode(byte[]) from the type CharacterEncoder is not accessible due to restriction on required library C:\Program Files\Java\jre6\lib\rt.jar	SendMail.java	/External_ip/src	line 139	Java Problem Access restriction: The method encode(byte[]) from the type CharacterEncoder is not accessible due to restriction on required library C:\Program Files\Java\jre6\lib\rt.jar	SendMail.java	/External_ip/src	line 146	Java Problem Access restriction: The method encode(byte[]) from the type CharacterEncoder is not accessible due to restriction on required library C:\Program Files\Java\jre6\lib\rt.jar	SendMail.java	/External_ip/src	line 163	Java Problem Access restriction: The type BASE64Decoder is not accessible due to restriction on required library C:\Program Files\Java\jre6\lib\rt.jar	CRAM_MD5.java	/External_ip/src	line 24	Java Problem Access restriction: The type BASE64Encoder is not accessible due to restriction on required library C:\Program Files\Java\jre6\lib\rt.jar	CRAM_MD5.java	/External_ip/src	line 50	Java Problem Access restriction: The type BASE64Encoder is not accessible due to restriction on required library C:\Program Files\Java\jre6\lib\rt.jar	SendMail.java	/External_ip/src	line 139	Java Problem Access restriction: The type BASE64Encoder is not accessible due to restriction on required library C:\Program Files\Java\jre6\lib\rt.jar	SendMail.java	/External_ip/src	line 146	Java Problem Access restriction: The type BASE64Encoder is not accessible due to restriction on required library C:\Program Files\Java\jre6\lib\rt.jar	SendMail.java	/External_ip/src	line 163	Java Problem

  7. #7
    Moderatore di Programmazione L'avatar di alka
    Registrato dal
    Oct 2001
    residenza
    Reggio Emilia
    Messaggi
    23,890

    Moderazione

    Originariamente inviato da P_il_musicante
    a me da questi errori
    Gli interventi all'interno di una pillola dovrebbero essere limitati alla sua integrazione o all'aggiunta di nuovo materiale, non per la discussione di problemi riscontrati nell'utilizzo della pillola stessa (che potrebbero essere benissimo legati a errori del programmatore, e non a quanto indicato qui).

    Apri una discussione separata per trattare il tuo problema.

    Puoi fare riferimento alla pillola qualora contenga informazioni utili per descrivere meglio il tuo problema, che devi comunque dettagliare per fornire un quadro completo delle cose che hai fatto.
    MARCO BREVEGLIERI
    Software and Web Developer, Teacher and Consultant

    Homepage | Blog | Delphi Podcast | Altri link...

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2020 vBulletin Solutions, Inc. All rights reserved.