Ricezione di messaggi dal server POP3
Il mese scorso si era parlato di come spedire mail usando il protocollo SMTP. Questa volta si parlerà di un protocollo complementare: POP3. POP3, che sta per Post Office Protocol version 3 è un insieme di comandi come SMTP che serve per ricevere la posta da quei server che accettano collegamenti via modem (ciò vuol dire che, in buona parte dei casi, sono esclusi quei server NON usati come provider, come ad esempio ditte private o università). Anche in questo caso è necessario collegarsi con il proprio provider utilizzando una porta Socket. Per essere precisi occorre usare la porta 110. POP3 dispone di alcuni semplici comandi che consentono allutente di loggarsi e richiedere i messaggi. I principali sono:
USERPASSLISTTOP [msg] [n]RETR [msg]QUIT
Essi servono rispettivamente per dare il proprio Login, la propria Password, richiedere quanti messaggi sono presenti nel server, richiedere le prime n righe del messaggio msg, richiedere tutto il messaggio msg e uscire. Può sembrare una panoramica molto affrettata, ma in effetti non cè molto altro da dire. Le risposte ritornate da questi comandi sono di due tipi: Tutto bene (+OK) e Errore (-ERR). È quindi necessario, dopo aver spedito un qualsiasi comandi controllare la sua risposta ed agire di conseguenza. Come nel precedente articolo, spiegherò i vari pezzi di codice e fornirò, alla fine, il codice completo.
Importante però notare una cosa: questo NON è unApplet, quindi non gira da Netscape, ma da riga di comando lanciando il comando Java POP3 \<Host\> \<User\> \<Passwd\>, dove Host è il nome del vostro server, User è il vostro login e Passwd la vostra password.
Vediamo dunque la prima procedura, quella che chiama tutte le altre. Questa funzione (Popper) si connette via socket al vostro provider e chiama la OpenComm per connettersi e, se non ci sono stati errori, la List per richiamare lheader dei messaggi. Questo header è composto da Dimensione del messaggio, Mittente, Data e Soggetto. La procedura Popper è descritta di seguito:
public static void Popper(String Host,String User,String Passwd) throws IOException {
// Procedura che legge lheader della mail. Questa procedura apre un
// socket sulla porta 110 (quella di POP3) e lancia la funzione
// List, la quale provvede fisicamente allazione di lettura Header.
// Apertura Socket e inizializzazione degli Stream di Input e Output
Socket mailSocket=new Socket(Host,110);
os=new PrintStream(mailSocket.getOutputStream());
is=new DataInputStream(mailSocket.getInputStream());
// Se lutente e loggato correttamente, legge gli Header
if(OpenComm(User,Passwd)==true)
List();
// Chiude la comunicazione con POP3 e si disconnette
os.println(QUIT);
is.readLine();
is.close();
os.close();
mailSocket.close();
}
Come si può vedere è tutto molto semplice. Si apre il socket, come per lapplet del mese scorso, si chiamano le funzioni per comunicare e si chiude la comunicazione (QUIT). Fin qui non cè nulla di strano, vediamo ora la funzione OpenComm. Questa funzione provvede a mandare al provider al quale si è connessi il proprio identificativo (Login) e la propria Password. La funzione, inoltre, provvede a verificare se non ci sono errori (Utente non presente nel sistema, Password non valida) e ritorna true se tutto è andato bene, false in caso contrario. La OpenComm è descritta di seguito:
public static boolean OpenComm(String User,String Passwd) throws IOException {
// Questa procedura si logga con il server, fornendo UserName e Password.
// Visualizza messaggi di errore nel caso UserName o Password siano errati
// Ritorna TRUE se si e loggato, FALSE altrimenti
String s;
boolean Ret=true;
s=is.readLine();
os.println(USER +User);
s=is.readLine();
if(s.substring(0,3).compareTo(+OK)==0) {
os.println(PASS +Passwd);
s=is.readLine();
if(s.substring(0,3).compareTo(+OK)!=0) {
System.out.println(Errore: Password Errata);
Ret=false;
}
else
System.out.println(Connesso come +User);
}
else {
System.out.println(Errore: User-ID Errato);
Ret=false;
}
return(Ret);
}
Qui cè qualcosa da dire. Per prima cosa si manda, tramite lo Stream aperto dalla Popper la stringa USER seguita dal nome utente, poi si controlla, tramite laltro Stream aperto, se il messaggio di ritorno è un errore o meno. Nel caso tutto sia andato a buon fine si procede a spedire la password, eseguendo gli stessi controlli. Dopo essersi loggati nel sistema è possibile quindi agire sulla propria casella postale. Nel caso di questo esempio ci limiteremo a prendere le prime righe del messaggio e costruire lheader, ma se si volesse leggerlo tutto non cè nientaltro da fare che spedire un comando RETR al posto del comando TOP. Vediamo ora nel dettaglio il comportamento della funzione List, che è il cuore di tutto il programma. Il listato è il seguente:
public static void List() throws IOException {
// Questa procedura e il cuore del programma.
// Provvede a spedire i comandi POP3 e a visualizzarne i risultati.
// Si veda lapposita sezione dove spiego i comandi POP3 per
// comprendere il flusso dei dati.
String s,s1,From,Date,Subj;
int NMsg,l;
long Dim;
From=Date=Subj=;
// Chiede quanti messaggi ci sono nel server
os.println(LIST);
s=is.readLine();
s1=s.substring(4);
NMsg=atoi(s1);
for(l=0;l<NMsg;l++)
s=is.readLine();
s=is.readLine();
System.out.println(+NMsg+Messaggi);
System.out.println();
// Cicla per ogni messaggio
for(l=0;l<NMsg;l++) {
System.out.println(Messaggio n +(l+1));
// Prende le prime 13 righe del messaggio
numero (l+1)
os.println(TOP +(l+1)+ 13");
s=is.readLine();
// Viene subito ritornata anche la dimensione della Mail
s1=s.substring(4);
Dim=atol(s1);
s=is.readLine();
while(is.available()!=0) {
s=is.readLine();
// Cerca i campi From, Date e Subject
if(s.length()>5) {
if(s.substring(0,6).compareTo(From: )==0)
From=s.substring(6);
if(s.substring(0,6).compareTo(Date: )==0)
Date=s.substring(6);
}
if(s.length()>8)
if(s.substring(0,9).compareTo(Subject: )==0)
Subj=s.substring(9);
}
}
System.out.println(Da: +From);
System.out.println(Data: +Date);
System.out.println(Soggetto: +Subj);
System.out.println(Dimensione: +Dim+ bytes);
System.out.println();
}
Come si può notare le operazioni sono piuttosto semplici. Per prima cosa mando il comando LIST, che mi ritorna il numero di messaggi presenti, mi salvo quindi questo numero in una variabile. Da notare che vengono usate due funzioni, atoi e atol, che NON sono comprese nel pacchetto di classi forniti a corredo del linguaggio, ma che ho scritto io, prendendo spunto dalle loro omonime del linguaggio C. Sarà possibile vederle nel listato in fondo a questo articolo, in ogni caso, dirò che la atoi converte una stringa in un intero, mentre le atol converte la stringa in un long. Dopo questa breve considerazione cicliamo per tutti i messaggi, chiedendo al server le prime 13 righe del messaggio. In queste righe, in posizioni che variano con il provider e il tipo di mailer usato per la spedizione, troviamo il Mittente, la Data e il Soggetto, mentre la dimensione è sempre ritornata subito. Quindi ci si limita a controllare tutte le righe che ci vengono passate e verificare se contengono le informazioni richieste. In ogni caso, il prefisso (From, Date, Subject) è standard, quindi è sufficiente controllare se la riga inizia con uno di questi per concludere che si ha linformazione desiderata. Alla fine di tutto si stampano le informazioni e si conclude il ciclo.
Bene, questo è tutto! Come si può vedere, anche il protocollo utilizzato per richiamare i messaggi è semplice quasi quanto quello per spedirlo e, comunque, non presenta nessuna particolare difficoltà di implementazione. Anche in questo caso, gran parte del lavoro è svolto dalle classi che gestiscono i Socket e gli Stream, quindi dobbiamo ancora ringraziare i tecnici della Sun.
Di seguito riporto, per completezza, il listato commentato del programma. Per ogni dubbio o problema è possibile consultare la RFC1225, oppure contattarmi direttamente.
Listato completo del programma POP3
import java.awt.*;
import java.io.*;
import Java.net.*;
public class POP3 {
public static long atol(Strings) {
// Converte la string passato in un long
Long tempInt;
String s1;
int l,k;
long q=0;
for(l=-1,k=0;(k<s.length()) &&(l==-1);k++)
if((s.charAt(k)<0) || (s.charAt(k)>9'))
l=k;
if(l==-1)
s1=s;
else
if(l!=0) {
s1=s.substring(0,l);
tempInt = Long.valueOf(s1);
q=tempInt.longValue();
}
return(q);
}
public static int atoi(Strings){
// Converte la string passato in un integer
Integer tempInt;
String s1;
int k,l;
for(l=-1,k=0;(k<s.length()) &&(l==-1);k++)
if((s.charAt(k)<0) || (s.charAt(k)>9'))
l=k;
if(l==-1)
s1=s;
else
if(l!=0) {
s1=s.substring(0,l);
tempInt = Integer.valueOf(s1);
k=tempInt.intValue();
}
else k=0;
return(k);
}
public static void List() throws IOException {
// Questa procedura e il cuore del programma.
// Provvede a spedire i comandi POP3 e a visualizzarne
// i risultati. Si veda lapposita sezione dove spiego
// i comandi POP3 per comprendere il flusso dei dati
String s,s1,From,Date,Subj;
int NMsg,l;
long Dim;
From=Date=Subj=;
// Chiede quanti messaggi ci sono nel server
os.println(LIST);
s=is.readLine();
s1=s.substring(4);
NMsg=atoi(s1);
for(l=0;l<NMsg;l++)
s=is.readLine();
s=is.readLine();
System.out.println(+NMsg+Messaggi);
System.out.println();
// Cicla per ogni messaggio
for(l=0;l<NMsg;l++){
System.out.println(Messaggio n +(l+1));
// Prende le prime 13 righe del messaggio
numero (l+1)
os.println(TOP +(l+1)+ 13");
s=is.readLine();
// Viene subito ritornata anche la dimensione della MaiL
s1=s.substring(4);
Dim=atol(s1);
s=is.readLine();
while(is.available()!=0) {
s=is.readLine();
// Cerca i campi From, Date e Subject
if(s.length()>5) {
if(s.substring(0,6).compareTo(From: )==0)
From=s.substring(6);
if(s.substring(0,6).compareTo(Date: )==0)
Date=s.substring(6);
}
if(s.length()>8)
if(s.substring(0,9).compareTo(Subject: )==0)
Subj=s.substring(9);
}
System.out.println(Da: +From);
System.out.println(Data: +Date);
System.out.println(Soggetto:+Subj);
System.out.println(Dimensione: +Dim+ bytes);
System.out.println();
}
public static DataInputStream is;
public static PrintStream os;
public static boolean OpenComm(String User,String Passwd) throws IOException {
// Questa procedura si logga con il server,fornendo
// UserName e Password. Visualizza messaggi di errore
// nel caso UserName o Password siano errati
// Ritorna TRUE se si e loggato, FALSE altrimenti
String s;
boolean Ret=true;
s=is.readLine();
os.println(USER +User);
s=is.readLine();
if(s.substring(0,3).compareTo(+OK)==0) {
os.println(PASS+Passwd);
s=is.readLine();
if(s.substring(0,3).compareTo(+OK)!=0) {
System.out.println(Errore: Password Errata);
Ret=false;
}
else
System.out.println(Connesso come +User);
}
else {
System.out.println(Errore: User-ID Errato);
Ret=false;
}
return(Ret);
}
public static void Popper(String Host,String User,String Passwd) throws IOException {
// Procedura che legge lheader della mail.
// Questa procedura apre un socket sulla porta 110
// (quella di POP3) e lancia la funzione List, la
// quale provvede fisicamente allazione di lettura Header.
// Apertura Socket e inizializzazione degli Stream di Input e Output
Socket mailSocket=new Socket(Host,110);
os=new PrintStream(mailSocket.getOutputStream());
is=new DataInputStream(mailSocket.getInputStream());
// Se lutente e loggato correttamente, legge gli Header
if(OpenComm(User,Passwd)==true)
List();
// Chiude la comunicazione con POP3 e si disconnette
os.println(QUIT);
is.readLine();
is.close();
os.close();
mailSocket.close();
}
public static void main(String args[]) {
try{
// Chiama la funzione Popper passando Host,
//UserName e Password
Popper(args[0],args[1],args[2]);
}
catch(Exception e) {
System.out.println(Eccezione +e);
}
}
}
