JavaConsolidating Email using Java

Consolidating Email using Java

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Java Programming Notes # 2400





Preface


Multiple Email accounts?


How many different Email accounts do you have?  Do you check them all regularly?  Do you have an easy way to consolidate them so that you only have to check one Email account in order to read all of your Email?


If you have multiple Email accounts and would like to consolidate all of your Email messages onto a single Email server, then this lesson is for you.


Viewing tip


You may find it useful to open another copy of this lesson in a separate browser window.  That will make it easier for you to scroll back and forth among the different listings and figures while you are reading about them.


First lesson in a series


This is the first lesson in a series of at least three lessons on the general topic of moving Email messages around among servers and local computers.


Supplementary material


I recommend that you also study the other lessons in my extensive collection of online Java tutorials.  You will find those lessons published at Gamelan.com.  However, as of the date of this writing, Gamelan doesn’t maintain a consolidated index of my Java tutorial lessons, and sometimes they are difficult to locate there.  You will find a consolidated index at www.DickBaldwin.com.


General Background Information


Multiple Email accounts


If you are like me, you have probably accumulated several different Email accounts over the years.  Unfortunately, the managers of some of my most important Email accounts refuse to forward my Email messages to a single account for consolidation.  As a result, I have had to check several Email accounts several times each day for many years.


Consolidate on local hard drive


Like most people, my operating mode has been to download the mail from several accounts into a single client program on my computer.  I would then process the mail locally.  This processing usually consisted of:



  • Deleting the vast majority of messages which were clearly spam
  • Reading the remainder of the messages
  • Deleting some of the messages after reading them to conserve disk space
  • Filing the rest of the messages away in an elaborate system of Email folders

Over time, my system of Email folders has grown to consume more than 500 MBytes of disk space, so I was always faced with a decision as to whether or not a particular message should be saved.


Let’s see, where did I file that message?


This all went pretty well until I needed to find a message later.  Likely as not, when I needed a message later, I couldn’t remember where I had filed it.  Searching for the message was clearly out of the question due to the slow speed of the search capability.


What once was a nuisance …


Until recently, I considered having to check multiple Email accounts and having to search frantically through hundreds of Email folders to find a message to be simply a nuisance.


Gmail to the rescue


However, in May of 2005, mainly out of curiosity, I added Google’s Gmail to my list of Email accounts.  Because of its advanced features, large capacity, and lightening-fast search capability, Gmail quickly became my primary Email account.


As of the date of this writing, I consider Gmail to be the best approach to Email that I have ever seen.  Consequently, I have arranged for as many of my other Email accounts as possible to automatically forward my Email messages to Gmail.



(Access to a Gmail account is available by invitation only.  People who already have an account can invite others to open accounts.  As of this writing, I have coupons for fifty invitations.  If you would like to have a Gmail account, send an email message to baldwin@dickbaldwin.com notifying me of that fact and I will submit your email address to get an invitation.  To keep my spam blocker from discarding your message, include Gmail in the subject of the message.)


Some refuse to forward


Unfortunately, as I mentioned earlier, I have a few important accounts where the managers refuse to forward my mail.  As a result, I have been searching for ways to deal with that issue in order to consolidate all of my Email into a single Gmail account.


Consolidation services are available


I looked into the possibility of subscribing to one of the available services that will download all of my Email and consolidate it for a fee.  However, I wasn’t too keen on paying a monthly fee for the rest of my life.  I also wasn’t too keen on giving the passwords to all of my Email accounts to some company that I know nothing about.


I decided to write my own consolidation program


As a result all of the above, I decided to write my own Email consolidation program and to share it with others who may have the same need.


This program runs locally to download Email messages from one POP3 server and to forward those messages to another Email address.


Although I am using the program to consolidate Email messages from several different POP3 servers onto the Gmail server, it should also be useful for collecting Email messages from any group of POP3 servers and forwarding those messages to any other Email accounts


Preview


An Email forwarding program


In this lesson, I will provide and explain the following two Java classes, which work together as a program to download Email messages from one POP3 server and to forward those messages to another Email account:



  • BigDog04g
  • BigDog04i

Command-line parameters


All of the user-specific information required by the program is provided in the form of the following eight command-line parameters:



  1. pop3Server:  Server from which the messages will be downloaded
  2. userName:  On pop3Server
  3. password:  On pop3Server
  4. destinationAddress:  Email address to which the messages will be forwarded
  5. smtpServer:  An available SMTP server that can be used to forward the messages
  6. workingDir:  Messages are temporarily stored here
  7. archiveDir:  Messages are moved to here from the workingDir for long-term storage if desired (optional)
  8. fwdTag:  String prepended to the subject when the message is forwarded. (Can be an empty string.)

Automation is my long-term goal


I’m aware that parameter input from command-line parameters isn’t nearly as sexy as input via a graphical user interface.  However, it is much easier to automate command-line input, and complete automation is my long-term goal.


I have a different batch file set up for each different POP3 server so that all I have to do to move messages from any particular POP3 server to my Gmail account is to double-click on the appropriate batch file.


There are two simple GUIs


There are two very simple GUIs involved in this version of the program (although I expect them to disappear in a future version).  When the program first starts running, the GUI shown in Figure 1 appears on the screen.  The program pauses until the user clicks the button labeled Download Msgs.  Then the program proceeds to download the messages.







Figure 1


Forwarding the messages


When all of the messages have been downloaded, the GUI shown in Figure 2 appears on the screen.  The program pauses until the user clicks the button labeled Forward Msgs before proceeding to forward the messages to the destination Email address.







Figure 2


These first two button clicks will almost certainly be eliminated from a future version of the program.


Deleting messages from the POP3 server


Once all of the messages have been forwarded to the destination Email address, the program pauses once more and waits for the user to click the button labeled Delete Msg On Server before actually deleting the messages from the POP3 server.  This gives the user an opportunity to take a look at the messages on the destination server before deleting them from the POP3 server.


An overabundance of caution


You might say that the use of these three stages of user interaction were the result of an overabundance of caution on my part.  I wanted to have some degree of control over the program when I first started running it in order to satisfy myself that messages weren’t being lost in the process.


I will also probably eliminate all of this user interaction once I become completely confident that all messages are being reliably transferred from one Email server to the other.


Running automatically on a scheduled basis


At that point in time, there will be no required user interaction other than to start the program running.  Once I reach that point, it shouldn’t be too difficult to set the batch file up in a task scheduler to cause the program to run automatically without user interaction on a scheduled basis, such as once per hour for example.  That is my ultimate goal.


Status and progress information


Figure 1 and Figure 2 also show a TextArea in the GUI.  Some status and progress information is displayed in these two areas, as well as in the command-line window while the program is running.


Operation is extremely easy


With this version of the program, all the user has to do to cause the program to move the available Email messages from a POP3 server to another Email account is to double click on a batch file, and then click on three GUI buttons in succession.  Program operation could hardly be simpler.


A batch-file command


A typical batch file command is shown in Figure 3.



(Note that it was necessary for me to manually enter line breaks into Figure 3 to force the command to fit in this narrow publication format.)






java BigDog04g pop.austincc.edu user password
myAcct@gmail.com smtp-server.austin.rr.com
./temp/ c:/z-MailArchives/ “ACC-“

Figure 3


You should be able to compare the individual command-line parameter in Figure 3 with the description of the parameters discussed earlier.



Discussion and Sample Code

This program consists of the following two classes:

  • BigDog04g
  • BigDog04i

The class named BigDog04g is a Java application and serves as the driver for the entire program.


Overall control


BigDog04g downloads all of the available messages from a POP3 server and writes them as individual files in a local working directory.  Then it instantiates an object of the class BigDog04i, which forwards the messages to a specified Email address.


Deleting messages and moving message files


Messages are not deleted from the POP3 server until they have been forwarded to the specified Email address and the user has directed that they be deleted.  When the messages are deleted from the POP3 server, the message files are also removed from the working directory.


By enabling and disabling blocks of code prior to compilation, a user can customize the program to cause the message files to be moved from the working directory into an archive directory or to simply be deleted from the working directory without saving them in an archive directory.


Command-line parameters


The following information is provided by the user in the form of command-line parameters:



  1. pop3Server: server from which the messages will be downloaded
  2. userName: on pop3Server
  3. password: on pop3Server
  4. destinationAddress: Email address to which the messages are to be forwarded
  5. smtpServer: an available SMTP server that can be used to forward the messages
  6. workingDir: messages are temporarily stored here
  7. archiveDir: messages are moved to here from the workingDir for long-term storage if desired (optional)
  8. fwdTag: String prepended to the subject when the message is forwarded

Directories must already exist


Note that both of the directories listed above must already exist when the program is run for the first time.


If message files are not being saved in an archive directory, the seventh parameter is a dummy parameter, which is essentially ignored by the program.  Even though it is ignored, it must be provided as a command-line parameter to cause the number of parameters to be correct.


Tagging the forwarded messages


The eighth parameter is a string that is prepended onto the beginning of the Subject line in the message before the message is forwarded.  For example, it can be used to indicate the originating Email account.  If you don’t want to tag the Subject of the forwarded message, just enter an empty string, “”, as the eighth command-line parameter.


POP3 technical information


For technical information on POP3, see RFC 1725at http://www.cse.ohio-state.edu/cgi-bin/rfc/rfc1725.html


A POP3 Command Summary is provided in the program comments in Listing 45 near the end of the lesson.


Program testing


The program was successfully tested using JDK 1.5.0_01 running under WinXP.  JDK 1.5 or later is required because of the use of generics in the program.


Will discuss in fragments


I will break both classes down and discuss them in fragments.  Complete listings of both classes are provided in Listing 45 and Listing 46 near the end of the lesson.


BigDog04g is a Frame


The class definition for BigDog04g begins in Listing 1.  As you can see, the class extends Frame, so an object of the class is a Frame.  The Frame that constitutes BigDog04g was shown earlier in Figure 1.






class BigDog04g extends Frame{
static String workingDir;
int numberMsgs = 0;
int msgNumber;
String uidl = “”;//unique msg ID
BufferedReader inputStream;
PrintWriter outputStream;
Socket socket;
String pathFileName;
static String[] params;

Listing 1


The code in Listing 1 simply declares some necessary variables.


The main method


BigDog04g is a Java application and therefore requires a main method.  The main method begins in Listing 2.


As mentioned earlier, certain critical information is provided to the program by way of eight command-line parameters.  The code in Listing 2 tests to confirm that the user has entered eight command-line parameters.  If not, usage information is displayed and the program terminates at that point.






  public static void main(String[] args){
if(args.length != 8){
System.out.println(
“Wrong number of parameters”);
System.out.println(“Usage: java BigDog04g ”
+ “npop3Server nuserName npassword ”
+ “ndestinationAddress nsmtpServer”
+ “nworkingDir narchiveDir nfwdTag”);
System.out.println(“If not saving msg ”
+ “files in archive, seventh parameter ”
+ “is a dummy parameter.”);
System.out.println(“Terminating program.”);
System.exit(0);
}//end if

Listing 2


Save command-line parameter values


Assuming that the number of command-line parameters is correct, the program saves some of them and uses others.


Listing 3 saves a reference to the args array so that it will be available later.


Listing 3 also identifies and saves the name of the working directory where the message files will be temporarily stored awaiting forwarding to the destination Email address.





    params = args;
workingDir = args[5];

Listing 3


(Once again, note that for proper operation, the working directory and the archive directory must both exist when the program is run.  The program is not designed to create those directories if they don’t already exist.)


Instantiate a BigDog04g object


Finally, as shown in Listing 4, the main method uses the first three command-line parameters to instantiate an object of the BigDog04g class.





    new BigDog04g(args[0],args[1],args[2]);
}//end main

Listing 4

Once constructed, this object will download and save the messages on the specified POP3 Email server as individual files in the local working directory.


The BigDog04g object will, in turn, instantiate an object of the BigDog04i class, which will forward the messages in the working directory to the destination Email address.


The BigDog04g constructor


The BigDog04 constructor begins in Listing 5.  The first task of the constructor is to get a connection to the POP3 Email server using the three incoming parameters.






  BigDog04g(String pop3Server,String userName,
String password){
int port = 110; //pop3 mail port
try{
socket = new Socket(pop3Server,port);

Listing 5


Listing 5 begins by getting a Socket connection to the specified POP3 server on port 110, which is the standard POP3 Email port.


Get input and output streams


Next, as shown in Listing 6, the constructor gets input and output streams on the Socket, which can be used to communicate with the POP3 server.  This should be old stuff to you by now.  If not, see my earlier lessons 550 through 568 on Network Programming.






      //Get an input stream from the socket
inputStream = new BufferedReader(
new InputStreamReader(
socket.getInputStream()));
//Get an output stream to the socket.
// Note that this stream will autoflush.
outputStream = new PrintWriter(
new OutputStreamWriter(
socket.getOutputStream()),true);

Listing 6


The validateOneLine method


At this point, I am going to set the discussion of the constructor aside and explain a utility method named validateOneLine.



(The BigDog04g and BigDog04i classes both contain copies of this utility method.)


This method is shown in its entirety in Listing 7.






  private String validateOneLine(){
try{
String response = inputStream.readLine();
if(response.startsWith(“+OK”)){
return response;
}else{
System.out.println(response);
//Terminate the session.
outputStream.println(“QUIT”);
socket.close();
System.out.println(
“Premature QUIT on -ERR”);
System.exit(0);
}//end else
}catch(IOException ex){
ex.printStackTrace();
}//end catch
//The following return statement is required
// to satisfy the compiler, but it can never
// be reached.
return “Make compiler happy”;
}//end validateOneLine()

Listing 7


Purpose of the validateOneLine method


The purpose of the validateOneLine method is to get and to validate a one-line message received from the POP3 server.  That message is the POP3 server’s response to a command that was sent to the server by the program.


The response may begin with the substring +OK or it may begin with the substring -ERR.



(The difference between the two should be fairly obvious.  There may be other possibilities as well, but if so this program doesn’t specifically take them into account.)


Validating the response


If the response of the POP3 server to the previous command was +OK, the validateOneLine method considers the command to have been successful and returns the string.


If the response was not +OK, it is assumed that the response was -ERR (or possibly something else indicating that the previous command was not successful).  In that case, the method displays the string received from the POP3 server and terminates the session with the server.


Code is straightforward


The actual code in Listing 7 is fairly straightforward and shouldn’t require further explanation.



(Note that the code in Listing 7 uses the inputStream from Listing 6 to read a single line of text that is sent by the server.)


Display connection message


Now, getting back to the constructor for the BigDog04g class, the constructor has just attempted to get a Socket connection to the POP3 server, (along with input and output streams on that socket) by which the program can communicate with the server.


The code in Listing 8 invokes the validateOneLine method to get and to display the initial response sent by the POP3 server to the connection request.






      String connectMsg = validateOneLine();
System.out.println(
“Connected to pop3Server ” + connectMsg);

Listing 8


Typical connection response


Figure 4 shows a typical response to a connection request that might be received from a POP3 server.



(Note that I manually entered line breaks into figure 4 to force it to fit into this narrow publication format.)






Connected to pop3Server
+OK POP3 mailbox.austincc.edu
v2001.78 server ready

Figure 4


Establish the user on the POP3 server


The next task is to establish a valid user on the POP3 server. 


The communication process between the program and the POP3 server is now in the AUTHORIZATION state.  The next step is to send the username and password to the POP3 server.


Commands are sent in uppercase plain text to the POP3 server.  Some commands require an argument following the command, as is the case with the USER command.


Send a USER command


The code in Listing 9 uses the outputStream object obtained in Listing 6 to send the USER command along with its argument to the POP3 server.


Then the code in Listing 9 invokes the validateOneLine method to get, validate, and display the response from the POP3 server.






      outputStream.println(“USER ” + userName);
String userResponse = validateOneLine();
System.out.println(“USER ” + userResponse);

Listing 9


Typical response to a USER command


Figure 5 shows a typical response to a USER command, provided of course that the user name that is provided as an argument to the command is a valid user.






USER +OK User name accepted, password please
Figure 5


As you can see from Figure 5, the next required step is to provide the password that matches the user name.


Send the PASS command


The code in Listing 10 sends the PASS command along with the password to the POP3 server.


Then Listing 10 invokes the validateOneLine method to get, validate, and display the response to that command.






      outputStream.println(“PASS ” + password);
System.out.println(
“PASS ” + validateOneLine());

System.out.println(“Downloading Messages”);
}catch(Exception e){e.printStackTrace();}

Listing 10


Typical response to the PASS command


Figure 6 shows a typical response to the PASS command assuming that the password that is provided is a valid password.






PASS +OK Mailbox open, 1 messages
Figure 6


As you can see, the response in Figure 6 not only opened the mailbox for business, it also notified the program of the number of available messages.  However, providing the number of available messages at this point is peculiar to this particular POP3 server and is not consistent from one server to the next.



(In fact, much of the text in the server response following the +OK characters will vary from one server to the next for different commands.)


Keeping track of what is going on


The last print statement in Listing 10 simply helps the user keep track of things as they occur.


Register a WindowListener object


The code in Listing 11 registers an anonymous WindowListener on the Frame to support the close button in the upper-right corner.






    this.addWindowListener(
new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}//end windowClosing
}//end WindowAdapter()
);//end addWindowListener

Listing 11


If you are unfamiliar with the code in Listing 11, see lessons 1632 through 1642 in my earlier publications.


Instantiate GUI objects


Listing 12 instantiates the Button and TextArea objects shown in the GUI in Figure 1.






    final Button downLoadButton =
new Button(“Download Msgs”);
final TextArea textArea = new TextArea(5,30);

Listing 12


The references to these objects must be final because they are accessed from within an anonymous class definition.  This is also explained in my earlier publications referred to above.


Register an ActionListener object


In this version of the program, message downloading begins when the user clicks the button shown in Figure 1.  The actual downloading of the messages is controlled by code contained in the actionPerformed method of an ActionListener object registered on that button.


The code in Listing 13 is the beginning of the code required to define, instantiate, and register an anonymous ActionListener object on the Button shown in Figure 1.


The purpose of this object is to download all of the available messages on the POP3 server when the user clicks the button.






    downLoadButton.addActionListener(
new ActionListener(){
public void actionPerformed(
ActionEvent e){

Listing 13


Listing 13 shows the beginning of the anonymous class definition, as well as the beginning of the actionPerformed method, which is executed when the user clicks the button to download the messages.


Get number of messages


The communication process between the program and the POP3 server is now in the TRANSACTION state.  While in this state, the ActionListener object will get and save the messages as individual files in the working directory.


The first step is to get the number of available messages on the server in a format that is useful.  The code in Listing 14 begins by sending a STAT command to the POP3 server to get a string that includes the number of messages.  A typical response to the STAT command is shown below, indicating that there are five messages available:


+OK 5 62677






          try{
outputStream.println(“STAT”);
String stat = validateOneLine();
//Get the number of messages as
// a String.
String numberMsgsStr =
stat.substring(
4,stat.indexOf(” “,5));
//Convert the String to an int.
numberMsgs = Integer.parseInt(
numberMsgsStr);

Listing 14


Parse the string


Then the code in Listing 14 parses the string to extract a substring that specifies the number of messages as a String.


Then the code in Listing 14 invokes the parseInt method of the Integer class to convert that string to a numeric value of type int.


Download the messages


Having gotten permission to download the messages, and having determined how many messages are available for downloading, the next step is to actually download the messages and to save each message in a separate file in the working directory.


This process is accomplished using nested while loops.  The outer loop iterates on messages.  The inner loop iterates on lines within an individual message.


I will explain this process from the outside in.  In other words, I will explain the details of the outer loop while ignoring the details of the inner loop.  Then I will go back and explain the details of the inner loop.


The outer loop


The outer loop begins in Listing 15.  This loop iterates on message numbers in terms of the POP3 server’s definition of message numbers.



(Note that message numbers begin with 1 instead of 0, so the loop is constructed accordingly.)






            msgNumber = 1;

while(msgNumber <= numberMsgs){
//Process the next message.
outputStream.println(
“UIDL ” + msgNumber);
uidl = validateOneLine();

Listing 15


A unique identifier


Each message on the POP3 server is identified by a unique identifier in addition to the message number, (known as uidl in this program).  The uidl is used by this program to construct a local file name for the message.



(Although the POP3 server guarantees that no two messages on that server will have the same uidl, there is no guarantee that two messages on different POP3 servers won’t have the same uidl.  Therefore, it is not wise to combine message files from two different POP3 servers in the same directory.  While not very likely, it is possible that this would result in two identical file names.  This program was not designed to protect against such conflicts.)


The code in Listing 15 gets, validates, and saves the uidl for the next message.  It sends a UIDL command along with the message number to the server to cause the server to send back the uidl.  An example of the server’s response to this command is shown below:


+OK 12 40d1c47e00012b11
More than just the unique identifier

As you can see, the server’s response, which is saved as uidl by this program, includes more than just the unique identifier.  The actual unique identifier is the sixteen-character hexadecimal value at the right end of the string.


The response also contains the message number and the +OK indicating that it is a good response.


Open an output file

The code in Listing 16 uses the contents of uidl to open an output file into which the message will be stored.  This results in a series of unique file names similar to that shown above.






              pathFileName = workingDir + uidl;
DataOutputStream dataOut =
new DataOutputStream(
new FileOutputStream(
pathFileName));

Listing 16


Retrieve the message


Next, the code in Listing 17 sends a RETR command to the POP3 server to retrieve the message identified by msgNumber from the server.






              outputStream.println(
“RETR ” + msgNumber);
//Validate the response.
String retrResponse =
validateOneLine();
//Inner loop removed for brevity.

Listing 17


Response to the RETR command


In response, the server sends a string similar to that shown below back to the program.


+OK 6052 octets


As you can see, this response contains the size of the message.  However, this program doesn’t make use of that information.



(Although I have seen this size information for two different servers, I don’t know if this format is consistent from one server to the next.)


This program simply invokes the validateOneLine method to confirm that the response begins with +OK.  Then it executes the inner loop, (which was deleted for brevity from Listing 17), to get and save the message in a local file in the working directory.



(As mentioned earlier, I will explain the code in the inner loop shortly.)


When the inner loop terminates …


Once the inner loop terminates, the three statements in Listing 18 are executed for the purpose of:



  • Closing the file.  At this point, the message is stored in a local file in the working directory with a file name that is based on the unique identifier provided by the POP3 server.
  • Displaying a message number on the command-line screen to indicate the state of progress to the user.
  • Incrementing the message number in preparation for executing the next iteration of the outer loop to download the next available message.





              dataOut.close();
System.out.print(msgNumber + ” “);
msgNumber++;
}//end while(msgNumber <= numberMsgs)

Listing 18


Listing 18 also signals the end of the outer loop.


Retrieve the contents of each message


Listing 19 shows the inner loop, which is used to retrieve the contents of each message.






              String msgLine =
inputStream.readLine();
while(!(msgLine.equals(“.”))){
dataOut.writeBytes(
msgLine + “n”);

msgLine = inputStream.readLine();
}//end while(!(msgLine.equals(“.”)

Listing 19


Each message consists of at least two lines of text.  The last line of text consists of a single period character as the first character in the line.  This special line format signals the end of the message.


Typical message text


In case you are interested, Figure 7 consists of the first four lines of text from a typical message. 



(Note, however, that it was necessary for me to manually enter several line breaks into Figure 7 to force it to fit into this narrow publication format.  As a result, Figure 7 shows more than four lines of text.)







Return-Path: <abc@def.com>
Received: from mailgateway.austincc.edu
(root@ACCess1.austincc.edu [198.213.3.20]
(may be forged))
by mailbox.austincc.edu (8.12.3/8.12.3/
Debian-6.6) with ESMTP id j7^261Gmail262^018934;
Sun, 14 Aug 2005 11:32:07 -0500
Figure 7


Begin with a priming read


The code in Listing 19 begins with a priming read to read the first line of text.  Then it enters a while loop to process that line, and to read and process each succeeding line, until the terminating line containing the single period is received.


Processing consists of writing each line of text into the output file, inserting a newline character at the end of each line in the output file.


Disable the download button


When the outer loop terminates, all of the available messages have been downloaded.  The code in Listing 20 disables the button labeled Download Msgs in Figure 1 to prevent the user from attempting to download the same messages a second time.



(In retrospect, I probably should have placed this code near the beginning of the actionPerformed method to prevent the user from fiddling with the button in the event of a long pause in the download process.)






            downLoadButton.setEnabled(false);
textArea.append(
“Messages downloaded”);

Listing 20


Listing 20 also displays a message in the GUI of Figure 1 to notify the user that the messages have been downloaded.


Terminate the POP3 session


The code in Listing 21



  • Displays a disconnect message on the command-line screen to inform the user as to what is happening.
  • Sends a QUIT command to the POP3 server to terminate the session.
  • Gets, validates, and displays the response from the POP3 server (see Figure 8).
  • Closes the socket connection to the POP3 server.





            System.out.println(
“nDisconnect from pop3Server.”);

outputStream.println(“QUIT”);
String quitResponse =
validateOneLine();
System.out.println(
“QUIT ” + quitResponse);
System.out.println();//blank line

socket.close();

Listing 21


Typical termination response


The text in Figure 8 is typical of that which is produced by the code in Listing 21.  Note that the actual text in the response will vary from one POP3 server to the next, however.






Disconnect from pop3Server.
QUIT +OK Sayonara
Figure 8


Sound an audible alert


The code in Listing 22 sounds an audible alert to get the attention of the user, because in this non-automated version of the program, it is time for the user to click another button.  (As discussed earlier, this requirement will be eliminated in a future version of the program.)






            Toolkit.getDefaultToolkit().beep();
try{
Thread.currentThread().sleep(500);
}catch(Exception ex){
System.out.println(ex);
}//end catch
Toolkit.getDefaultToolkit().beep();

Listing 22


Instantiate a BigDog04i object


At this point, all of the messages have been downloaded from the POP3 server and have been saved as individual files in the working directory.  It is time to forward those messages to the destination Email address.  The forwarding task is handled by an object of the class named BigDog04i.


The code in Listing 23 instantiates an object of the BigDog04i class to cause the message forwarding process to take place.






            new BigDog04i(
params[0],//pop3Server
params[1],//userName
params[2],//password
params[3],//destinationAddress
params[4],//smtpServer
params[5],//workingDir
params[6],//archiveDir
params[7]);//fwdTag
}//end try
catch(Exception ex){
ex.printStackTrace();
}//end catch
}//end actionPerformed
}//end ActionListener
);//end addActionListener

Listing 23


Cleanup code


Listing 23 also signals the end of the anonymous definition and instantiation of the ActionListener class and its actionPerformed method.


Once again, if you are unfamiliar with the cleanup code in Listing 23, see lessons 1632 through 1642 in my earlier publications.


Configure the GUI


The remaining code in the constructor shown in Listing 24 configures the GUI shown in Figure 1 by placing the various components onto the Frame, setting the size, and making the whole thing visible.






    add(downLoadButton);
add(textArea);
textArea.setText(“”);
setLayout(new FlowLayout());
setTitle(“Copyright 2005, R.G.Baldwin”);
setBounds(0,0,250,200);
//Make the GUI visible.
setVisible(true);
}//end constructor
//===========================================//
}//end class BigDog04g

Listing 24


Listing 24 also signals the end of the BigDog04g class.


The bottom line regarding the BigDog04g object


Once the BigDog04g GUI becomes visible on the screen, the user can cause the program to connect to the specified POP3 server and to download the available messages by clicking the button labeled Download Msgs shown in Figure 1.


The BigDog04i class


Now that you understand how the program downloads the messages from the POP3 server, it is time to learn how it forwards those messages to the destination Email address.


An object of the BigDog04i class is instantiated by the code in Listing 23 to tag and forward a set of message files that were written into the working directory by the BigDog04g object.


Tag and forward the messages


This BigDog04i object tags the messages with a tag defined by the user as a command-line parameter to the main method of the class named BigDog04g.  Then the object forwards the messages to a destination Email account that is specified as a command-line parameter.  It uses an SMTP server that is also specified as a command-line parameter.


The DELE command


This object uses the DELE command to delete messages from the POP3 server from which the messages were originally downloaded.



(Deletion of the messages from the server can be temporarily disabled for test purposes by disabling a block of code before compiling the program.  Search for the word disable in the source code to identify the code that needs to be disabled for this purpose.)


Program testing


The BigDog04i class was tested using JDK 1.5.0_01 running under WinXP.  JDK version 1.5 or later is required due to the use of generics in the program.


The BigDog04i class


The class definition begins in Listing 25.  As you can see, the class extends Frame.  An object of this class is shown in Figure 2.






class BigDog04i extends Frame{
//User-specific information is stored here.
// This information is provided in the form of
// constructor parameters.
//ID of the destination Email account.
String destinationAddress;
//An smtp server through which the user is
// authorized to send Email messages.
String smtpServer;
//Local folder where message files are stored
// awaiting processing.
final String workingDir;
//Local folder for archiving message files.
final String archiveDir;
//Tag that is prepended to the Subject line of
// the message before forwarding.
String fwdTag;
//Following are working variables used by the
// program for various purposes.
BufferedReader inputStream;
PrintWriter outputStream;
Socket socket;
String pathFileName;
Vector <String> msgToDelete =
new Vector<String>();
Button forwardButton =
new Button(“Forward Msgs”);
Button deleteButton = new Button(
“Delete Msg On Server”);
TextArea textArea = new TextArea(5,30);
String uidl;
boolean okToDelete = false;
int msgNumber = 0;

Listing 25


The code in Listing 25 declares a large number of variables, initializing some of them.  This code should be self-explanatory and shouldn’t require further explanation.


The constructor for BigDog04i


The constructor begins in Listing 26.






  BigDog04i(final String pop3Server,
final String userName,
final String password,
String destinationAddress,
String smtpServer,
final String workingDir,
final String archiveDir,
String fwdTag){
this.destinationAddress = destinationAddress;
this.smtpServer = smtpServer;
this.workingDir = workingDir;
this.archiveDir = archiveDir;
this.fwdTag = fwdTag;

System.out.println(“Forwarding messages:”);

Listing 26


Once again, the code in Listing 26 should be self-explanatory and shouldn’t require further explanation.


Register a WindowListener object


Listing 27 registers a WindowListener object to service the close button on the Frame.






    this.addWindowListener(
new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}//end windowClosing
}//end WindowAdapter()
);//end addWindowListener

Listing 27


Register an ActionListener


The forwarding of messages begins when the user clicks the button labeled Forward Msgs in Figure 2.  Listing 28 shows the beginning of the code that is used to anonymously define, instantiate, and register an ActionListener object on that button.






    forwardButton.addActionListener(
new ActionListener(){
public void actionPerformed(
ActionEvent e){
forwardButton.setEnabled(false);

Listing 28


Clicking this button causes the messages stored as files in the working directory to be forwarded to the destination Email address. 


Don’t fiddle with that button


The code in the actionPerformed method begins by disabling the button to prevent the user from attempting to forward the same set of messages twice.



(See the retrospective comment that I made regarding the ActionListener object that is registered on the button in Figure 1.  This is more important here, because it usually take much longer to forward the messages than it does to download them.  During the forwarding process, there is often plenty of time for the user to fiddle with the button.)


Get a directory listing


In order to be able to forward the messages, the object must have a list of the message files that are to be forwarded.


The code in Listing 29 gets a directory listing of the message files in the working directory.  This code creates a directory listing containing only those files with names that begin with +OK.  This is an anonymous implementation of a class that implements FilenameFilter.






          String[] dirList =
new File(workingDir).list(
new FilenameFilter(){
public boolean accept(
File dir,String name){
if(!(new File(dir,name).
isFile())) return false;
return name.startsWith(“+OK”);
}//end accept
}//end FilenameFilter
);//end list

Listing 29


Process all of the message files


Listing 30 shows the beginning of a for loop that is used to process all of the message files in the working directory, one message at a time.






          for(int msgCounter = 0;
msgCounter < dirList.length;
msgCounter++){
String fileName =
dirList[msgCounter];
pathFileName = workingDir + fileName;

Listing 30


The code in listing 30 constructs a pathFileName string as the concatenation of the path to the working directory and the name of the file being processed.


Get and save the message number


The code in Listing 31 parses the file name to get, save, and display the message number originally used by the POP3 server to identify the message.


Displaying this message number serves as a progress indicator for the user.  This is important because, depending on the speed of the user’s Internet service, there can sometime be noticeable pauses between the sending of individual messages to the destination Email address.





            String strMsgNumber =
fileName.substring(
fileName.indexOf(” “),
fileName.lastIndexOf(” “))
.trim();
msgNumber =
Integer.parseInt(strMsgNumber);
System.out.print(
“” + msgNumber + ” “);

Listing 31


Process the message


Listing 32 invokes the method named processTheMsg to perform the actual processing of the message.





            processTheMsg();
}//end for loop on directory length

Listing 32


Listing 32 also signals the end of the for loop.


At this point, I am going to set aside the discussion of the actionPerformed method and discuss the method named processTheMsg.  I will return and finish the discussion of the actionPerformed method later.


The processTheMsg method


The processTheMsg method is shown in its entirety in Listing 33.  The purpose of this method is to initiate the processing of the message.






  void processTheMsg(){
//Get and save the unique identifier assigned
// by the pop3Server.
uidl = pathFileName.substring(
pathFileName.lastIndexOf(” “));
okToDelete = forwardEmailMsg(
destinationAddress,
smtpServer,
fwdTag,
pathFileName);
if(okToDelete){
msgToDelete.add(pathFileName);
}//end if
}//end processTheMsg

Listing 33


The code in Listing 33 forwards the message to the destination Email account and adds the identification of the message to the list of messages scheduled for deletion from the POP3 server later.


The actual forwarding of the message is accomplished by invoking the method named forwardEmailMsg.


Tagging the message


The third parameter to the forwardEmailMsg method can be used to tag the Subject line of the forwarded message to show that it was forwarded, or can be used for any other purpose for that matter.  This parameter value is provided by the user as a command-line parameter to the main method of the BigDog04g class.


If you don’t want to tag the message, just pass an empty string, “”, as a command-line parameter to the main method of the BigDog04g class when you start the program running.


Prepare to delete messages from POP3 server 


The forwardEmailMsg method returns true if the message was successfully forwarded, and returns false otherwise.


The code in Listing 33 adds the message to a list of messages to be deleted from the POP3 server only if it was successfully forwarded.  If the message was not successfully forwarded, it will not be deleted and will remain on the POP3 server to be processed again later.


The forwardEmailMsg method


This is a fairly complex method.  You can view the code for the method in Listing 46 near the end of the lesson.  You will find a detailed explanation of the method in one of my previously published lessons numbered 2180 and entitled Enlisting Java in the War Against Email Viruses.  Therefore, I won’t repeat that explanation here.


Back to the discussion of the actionPerformed method


With the termination of the for loop in Listing 32, all of the messages have been forwarded to the destination Email address.  The remaining code in the actionPerformed method:



  • Enables the button labeled Delete Msg On Server shown in Figure 2.
  • Displays a message to the user in the TextArea shown in Figure 2.
  • Sounds an audible alert to make the user aware that all messages have been forwarded and it is time to make a decision regarding the deletion of the messages from the POP3 server.

This code is shown in Listing 34.





          deleteButton.setEnabled(true);
textArea.append(
“Delete msgs from pop3Server?n”);
//Sound an audio alert
try{
Toolkit.getDefaultToolkit().beep();
Thread.currentThread().sleep(500);
Toolkit.getDefaultToolkit().beep();
Thread.currentThread().sleep(500);
Toolkit.getDefaultToolkit().beep();
}catch(Exception ex){
ex.printStackTrace();}
}//end actionPerformed
}//end ActionListener
);//end addActionListener

Listing 34


Listing 34 also signals the end of the anonymous definition and instantiation of the ActionListener object that is registered on the button labeled Forward Msgs shown in Figure 2.


The bottom line on the Forward Msgs button


When the user clicks this button (while it is enabled) all of the messages previously stored in the working directory will be forwarded to the destination Email address.  In addition, a list is populated identifying the messages that were successfully forwarded.  This list will be used later to delete those messages from the POP3 server.


Register another ActionListener object


Continuing with the explanation of the constructor for the BigDog04i class, the code in Listing 35 shows the beginning of the definition, instantiation, and registration of an ActionListener object on the button labeled Delete Msg On Server in Figure 2.





    deleteButton.addActionListener(
new ActionListener(){
public void actionPerformed(
ActionEvent e){
deleteButton.setEnabled(false);
textArea.append(“n”);
System.out.println(
“nDelete Msgs from POP3 server.”);

Listing 35


The primary purpose of this listener object is to do the following when the user clicks this button:



  • Delete the messages from the POP3 server.
  • Move the corresponding message files from the working directory to an archive directory, or optionally simply delete them from the working directory.

By now, the code in Listing 35 should be self-explanatory.


Connect to the POP3 server


In order to delete the messages from the POP3 server, the BigDog04i object must get a connection to the POP3 server from which the messages were originally downloaded.  The code to accomplish this is shown in Listing 36.






          //Get connected to the Email pop3Server
int port = 110; //pop3 mail port
try{
//Get a socket, connected to the
// specified pop3Server on the
// specified port.
socket = new Socket(pop3Server,port);
//Get an input stream from the socket
inputStream = new BufferedReader(
new InputStreamReader(
socket.getInputStream()));
//Get an output stream to the socket
outputStream = new PrintWriter(
new OutputStreamWriter(
socket.getOutputStream()),true);
//Display the msg received from the
// pop3Server on the command-line
// screen immediately following
// connection.
String connectMsg =
validateOneLine();
System.out.println(
“nConnected to pop3Server ”
+ connectMsg);
//The communication process is now in
// the AUTHORIZATION state. Send the
// user name and password to the
// pop3Server.
outputStream.println(“USER ”
+ userName);
//Get response and confirm that the
// response was +OK and was not -ERR.
String userResponse =
validateOneLine();
//Display the response on the
// command-line screen.
System.out.println(“USER ”
+ userResponse);
//Send the password to the pop3Server
outputStream.println(“PASS ”
+ password);
//Validate the pop3Server’s response
// as +OK. Display the response in
// the process.
System.out.println(“PASS ”
+ validateOneLine());
}catch(Exception ex){
ex.printStackTrace();}

Listing 36


This code is essentially the same as code that was explained earlier in conjunction with Listing 5 through Listing 10. Therefore, I won’t explain it again.


Process the msgToDelete collection


At this point in the execution of the program, the reference variable named msgToDelete contains a reference to a Vector object populated with references to String objects.  Each such String object identifies a message that is to be deleted from the POP3 server.


The deletion is accomplished by executing a for loop that iterates on the contents of the Vector object.  That for loop begins in Listing 37.





          System.out.println(
“nDeleting Msgs from POP3 server.”);

for(int cnt = 0;
cnt < msgToDelete.size();cnt++){
pathFileName = msgToDelete.
elementAt(cnt);
String strMsgNumber = pathFileName.
substring(pathFileName.indexOf(” “),
pathFileName.lastIndexOf(” “)).
trim();
int msgNumber = Integer.parseInt(
strMsgNumber);

Listing 37



During each iteration of the for loop, the code in Listing 37 retrieves a string from the collection and parses that string to extract the msgNumber of the message that is to be deleted.


How to actually delete a message


Deletion of a message from the POP3 server is accomplished by marking the message for deletion while in the TRANSACTION state.  The message is marked for deletion by sending a DELE command with the message number as an argument to the command.


The message is actually deleted later when the client sends a QUIT command to the POP3 server causing the POP3 server to enter the UPDATE state.  If the program aborts prematurely before sending a QUIT command, marked messages will not be deleted from the POP3 server.


Temporarily disabling deletion of messages


It is sometimes useful to temporarily disable the deletion of messages from the server while testing or troubleshooting the program.  I will show you how to do this later.


If you elect to temporarily disable the deletion of messages from the POP3 server, you should enable the code shown in Listing 38.  (That code is disabled as shown in Listing 38.)





/*
//Disable the following statement if
// you enable message deletion.
textArea.append(
“nMessage deletion disabled”);
*/

Listing 38


Mark the message for deletion


The code in Listing 39 uses the DELE command along with the message number to mark a message for deletion.  This code also gets, validates, and displays the POP3 server’s response to the DELE command.






            //Disable this code to disable
// deletion of messages from
// pop3Server
outputStream.println(“DELE ”
+ msgNumber);
//Validate the response and display
// it on the GUI.
textArea.append(
“Msg: ” + msgNumber + ” ”
+ validateOneLine()+”n”);
textArea.append(
“Deleted:” + msgNumber + “n”);
//Disable to here to disable
// deletion of messages from
// pop3Server

Listing 39


The validateOneLine method


Note that the code in Listing 39 invokes a method named validateOneLine to get and validate the server’s response as was the case with the object of type BigDog04g.


Disabling the deletion of messages


If you elect to disable the deletion of messages from the POP3 server, you should disable all of the code in Listing 39.


Handling local message files


Once you have forwarded the messages to the destination Email address, you can either use a method named moveFile to move the local message files from the working directory into an archive directory, or you can simply delete them from the working directory.  You must make this decision and appropriately enable and disable certain blocks of code prior to compilation of the program.


Code for handling local message files


The code to accomplish this is shown in Listing 40, and should be self-explanatory.





            //To move message files to the
// archive directory, enable this
// block of code and disable the next
// block of code.
{//Start move block
moveFile(pathFileName,archiveDir);
}//End move block
/*
//To delete message files without
// saving them in an archive
// directory, enable this block of
// code and disable the previous
// block of code.
{//Start delete block
File file = new File(pathFileName);
boolean isDeleted = file.delete();
if(!isDeleted)System.out.println(
“Unable to delete ” + file);
}//End delete block
*/
}//end for loop on msgToDelete.size()

Listing 40


This version saves the message files


The version of the program shown in Listing 40 moves the message files from the working directory into the archive directory.  If you do this, you should manually delete the files in the archive directory on a regular schedule to free up disk space.


Listing 40 also signals the end of the for loop that iterates on the list of messages to be deleted.


Terminate the POP3 server session


The code in Listing 41 sends a QUIT command to the POP3 server to terminate the session.  This code is essentially the same as that discussed earlier in conjunction with Listing 21.





          System.out.println(
“Disconnect from POP3 server”);
outputStream.println(“QUIT”);
String quitResponse =
validateOneLine();
//Display the response on the
// command-line screen.
System.out.println(
“QUIT ” + quitResponse);
//Server is now in the UPDATE mode.
// It will delete all files marked
// with the DELE command earlier
// in the execution of the program.
//Close the socket
try{
socket.close();
}catch(Exception ex){
ex.printStackTrace();
}//end catch
textArea.append(“nnMessages deleted ”
+ “from pop3Server.n”
+ “Press X button to terminate.”);
}//end actionPerformed
}//end ActionListener
);//end addActionListener

Listing 41


One operational difference


There is one operational difference that isn’t apparent in Listing 41.  In the earlier case shown in Listing 21, messages had been downloaded from the server but were not marked for deletion.  Therefore, those messages remained on the server after the session was terminated.  In this case, messages have been marked for deletion, and will be deleted by the server when the session is properly terminated.


Listing 41 also signals the end of the anonymous definition, instantiation, and registration of an ActionListener object on the button labeled Delete Msg On Server in Figure 2.  When this button has been enabled and is clicked by the user, the messages that have been identified in the Vector object referred to by msgToDelete will be deleted from the POP3 server.


Configure the GUI


The remaining code in the constructor for the class named BigDog04i is shown in Listing 42.






    add(forwardButton);
add(deleteButton);
deleteButton.setEnabled(false);
add(textArea);
textArea.setText(“”);
setLayout(new FlowLayout());
setTitle(“Copyright 2005, R.G.Baldwin”);
setBounds(250,0,250,200);
//Make the GUI visible.
setVisible(true);
}//end constructor

Listing 42


This code configures the GUI shown in Figure 2 by placing components on the Frame object, setting the size, and making it visible.


The moveFile method


Listing 43 shows the moveFile method in it entirety.  This method is used in Listing 40 to move a file from its current location specified by pathFileName to a new location specified by archiveDir.





  public void moveFile(String pathFileName,
String archiveDir){
String fileName = pathFileName.substring(
pathFileName.lastIndexOf(‘/’) + 1);
String archiveDirFileName =
archiveDir + fileName;
boolean moved =
new File(pathFileName).renameTo(
new File(archiveDirFileName));
if(!moved)System.out.println(
“Unable to move ” + new File(pathFileName)
+ “nto ” + new File(archiveDirFileName));
}//end moveFile method
}//end class BigDog04i

Listing 43


If the code in Listing 43 is unfamiliar to you, I have explained code similar to this in numerous previously published lessons.


Listing 43 also signals the end of the definition of the class named BigDog04i.


Run the Program

I encourage you to copy the code from Listings 45 and 46 into your text
editor, compile it, and execute it.  Experiment with it, making
changes, and observing the results of your changes.

I do recommend, however, that before executing the code, you make the changes
described in the earlier section entitled
Temporarily disabling
deletion of messages
so that the messages won’t be deleted from your POP3
server.  That way, if something goes wrong, the messages will still be
there for you to access using your regular Email client program.  Once you
are satisfied with the behavior of the program and are ready to use it on a
routine production basis, you can re-enable the deletion of messages from your
POP3 server.

Also please be aware of the following disclaimer:

THIS PROGRAM IS PROVIDED TO YOU AT NO COST.  BY USING THIS PROGRAM TO
PROCESS YOUR EMAIL, YOU AGREE THAT YOU ARE USING IT AT YOUR OWN RISK.  THE
AUTHOR OF THE PROGRAM, RICHARD G. BALDWIN, ACCEPTS NO LIABILITY FOR ANY LOSS
THAT YOU MAY INCUR THROUGH THE USE OF THIS PROGRAM.

Summary

In this lesson, I have provided and explained two Java
classes, which work together as a program to download Email messages from one
POP3 server and to forward those messages to another Email account.

What’s Next?

In the next lesson in this series, I will show you how to write a program that can be used to upload legacy Email
messages from local
MBOX files
to an Email account.

Such a program is particularly useful for persons like
me who have fallen in love with Gmail and who would like to upload our large
collection of legacy
Email messages onto the Gmail server so that we can take advantage of the extremely fast
search capability that it provides.

In the lesson following that one, I will show you how to modify the program that I provided in this lesson so that it
can be run in a fully automated unattended mode.  This will make it
possible to use a task scheduler to automatically move Email messages from one
POP3 server to another Email account on an unattended regularly scheduled basis,
such as once per hour for example.

Complete Program Listings


Complete listings of the classes discussed in this lesson are shown in Listings
45 and 46 below.
 

/*File BigDog04g.java Copyright 2005, R.G.Baldwin
Rev 08/13/05
DISCLAIMER:  THIS PROGRAM IS PROVIDED TO YOU AT 
NO COST.  BY USING THIS PROGRAM TO PROCESS YOUR 
EMAIL, YOU AGREE THAT YOU ARE USING IT AT YOUR 
OWN RISK.  THE AUTHOR OF THE PROGRAM, RICHARD G. 
BALDWIN, ACCEPTS NO LIABILITY FOR ANY LOSS THAT 
YOU MAY INCUR THROUGH THE USE OF THIS PROGRAM.
This program downloads all messages from a
pop3Server and writes them as individual files in
a local working directory.  Then it instantiates 
an object of the class BigDog04i, which forwards
the messages to a specified Email address.
Messages are not deleted from the pop3Server 
until they have been forwarded to the specified 
Email address and the user has directed that they
be deleted.  When the messages are deleted from 
the pop3Server, the message files are also 
removed from the working directory.
By enabling and disabling blocks of code prior to
compilation, you can customize the program to 
cause the  message files to be moved from the
working directory to an archive directory or 
simply deleted from the working directory without
saving them in an archive directory.
The following information is provided as 
command-line parameters:
pop3Server: server from which the messages will 
  be downloaded
userName: on pop3Server
password: on pop3Server
destinationAddress: Email address to which the 
  messages are to be forwarded
smtpServer: an available SMTP server that can be 
  used to forward the messages
workingDir: messages are temporarily stored here
archiveDir: messages are moved to here from the 
  workingDir for long-term storage if desired
fwdTag: String prepended to the subject when the 
  message is forwarded.
Note that both of the directories listed above 
must already exist when the program is run for 
the first time.
If message files are not being saved in an 
archive directory, the seventh parameter is a 
dummy parameter, which is essentially ignored by 
the program, but it must be provided.
If you don't want to tag the subject of the 
forwarded msg, just enter "" as the eighth 
command-line parameter.
For technical information on POP3, see RFC 1725
at
http://www.cis.ohio-state.edu/htbin/rfc/rfc1725.
html
A POP3 Command Summary follows based on the
information at that web site.
Minimal POP3 Commands:
USER name
PASS string
QUIT
STAT
LIST [msg]
RETR msg
DELE msg
NOOP
RSET
QUIT
Optional POP3 Commands:
APOP name digest
TOP msg n
UIDL [msg]
POP3 Replies:
+OK
-ERR
Tested using JDK 1.5.0_01 under WinXP
************************************************/
import java.net.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
class BigDog04g extends Frame{
  static String workingDir;
  int numberMsgs = 0;
  int msgNumber;
  String uidl = "";//unique msg ID
  BufferedReader inputStream;
  PrintWriter outputStream;
  Socket socket;
  String pathFileName;
  static String[] params;
  public static void main(String[] args){
    if(args.length != 8){
      System.out.println(
                   "Wrong number of parameters");
      System.out.println("Usage: java BigDog04g "
         + "npop3Server nuserName npassword "
         + "ndestinationAddress nsmtpServer"
         + "nworkingDir narchiveDir nfwdTag");
      System.out.println("If not saving msg "
        + "files in archive, seventh parameter "
        + "is a dummy parameter.");
      System.out.println("Terminating program.");
      System.exit(0);
    }//end if
    
    //Save the command-line parameters for later 
    //use by the object of the class BigDog04i
    params = args;
    //Establish the name of the working
    // directory.  Note that this directory must
    // already exist.
    workingDir = args[5];
    //Instantiate an object that will download
    // and save the messages as individual files.
    // This object will in turn instantiate an
    // object of the class BigDog04i, which will
    // forward the messages to the destination
    // Email address.
    new BigDog04g(args[0],args[1],args[2]);
  }//end main
  //===========================================//
  //Constructor
  BigDog04g(String pop3Server,String userName,
                                String password){
    int port = 110; //pop3 mail port
    try{
      //Get a socket, connected to the specified
      // pop3Server on the specified port.
      socket = new Socket(pop3Server,port);
      //Get an input stream from the socket
      inputStream = new BufferedReader(
                     new InputStreamReader(
                       socket.getInputStream()));
      //Get  an output stream to the socket.
      // Note that this stream will autoflush.
      outputStream = new PrintWriter(
               new OutputStreamWriter(
                 socket.getOutputStream()),true);
      //Display the msg received from the
      // pop3Server on the command-line screen
      // immediately following connection.
      String connectMsg = validateOneLine();
      System.out.println(
        "Connected to pop3Server " + connectMsg);
      //The communication process is now in the
      // AUTHORIZATION state.  Send the user
      // name and password to the pop3Server.
      //Commands are sent in plain text, upper
      // case to the pop3Server.  Some commands
      // require an argument following the
      // command, as is the case with USER.
      //Send the command.
      outputStream.println("USER " + userName);
      //Get response and confirm that the
      // response was +OK and was not -ERR.
      String userResponse = validateOneLine();
      //Display the response on the command-
      // line screen.
      System.out.println("USER " + userResponse);
      //Send the password to the pop3Server
      outputStream.println("PASS " + password);
      //Validate the pop3Server's response as +OK
      // Display the response in the process.
      System.out.println(
                    "PASS " + validateOneLine());
      System.out.println("Downloading Messages");
    }catch(Exception e){e.printStackTrace();}
    //Register a window listener to service
    // the close button on the Frame.
    this.addWindowListener(
      new WindowAdapter(){
        public void windowClosing(WindowEvent e){
          System.exit(0);
        }//end windowClosing
      }//end WindowAdapter()
    );//end addWindowListener
    //Note that the compiler requires the
    // reference to the following components to
    // be final because they are accessed from
    // within an anonymous class definition.
    final Button downLoadButton =
                     new Button("Download Msgs");
    final TextArea textArea = new TextArea(5,30);
    //Register an ActionListener on the
    // downLoadButton.
    downLoadButton.addActionListener(
      new ActionListener(){
        public void actionPerformed(
                                  ActionEvent e){
          //Download all messages on the
          // pop3Server.
          try{
            //The communication process is now
            // in the TRANSACTION state.
            //Retrieve and save messages
            outputStream.println("STAT");
            String stat = validateOneLine();
            //Get the number of messages as
            // a String.
            String numberMsgsStr =
                     stat.substring(
                          4,stat.indexOf(" ",5));
            //Convert the String to an int.
            numberMsgs = Integer.parseInt(
                                  numberMsgsStr);
            //NOTE: Msg numbers begin with 1,
            // not 0.
            //Retrieve and save each
            // message.  Each msg ends with a
            // period on a new line.
            msgNumber = 1;
            while(msgNumber <= numberMsgs){
              //Process the next message.
              //Get and save a unique identifier
              // for the message from the
              // pop3Server and validate the
              // response.
              outputStream.println(
                            "UIDL " + msgNumber);
              uidl = validateOneLine();
              //Open an output file to save
              // the message.  Use the UIDL
              // as the file name.
              pathFileName = workingDir + uidl;
              DataOutputStream dataOut =
                          new DataOutputStream(
                            new FileOutputStream(
                                  pathFileName));
              //Send a RETR command to begin
              // the message retrieval process
              outputStream.println(
                            "RETR " + msgNumber);
              //Validate the response.
              String retrResponse =
                               validateOneLine();
              //Read the first line in the
              // message from the pop3Server.
              String msgLine =
                          inputStream.readLine();
              //Continue reading lines until
              // a "." is encountered as the
              // first char in a line.  That
              // signals the end of the msg.
              while(!(msgLine.equals("."))){
                //Write the line to the output
                // file and read the next
                // line.  Insert newline
                // characters when writing the
                // output to the file.
                dataOut.writeBytes(
                                 msgLine + "n");
                msgLine = inputStream.readLine();
              }//end while(!(msgLine.equals(".")
              
              //Close the output file.  The
              // message is now stored in a
              // local file with a file name
              // based on the unique ID
              // provided by the pop3Server.
              dataOut.close();
              //Show progress on the command-line
              // screen
              System.out.print(msgNumber + " ");
              //Increment the message number
              // in preparation for processing
              // the next message.
              msgNumber++;
            }//end while(msgNumber <= numberMsgs)
            //No more messages. Disable the
            //downLoadButton.
            downLoadButton.setEnabled(false);
            textArea.append(
                        "Messages downloaded");
                       
            //Terminate the session with the 
            // pop3Server.
            System.out.println(
              "nDisconnect from pop3Server.");
            outputStream.println("QUIT");
            String quitResponse =
                             validateOneLine();
            //Display the response on the
            // command-line screen.
            System.out.println(
                       "QUIT " + quitResponse);
            System.out.println();//blank line
            
            socket.close();
            
            //Sound an alarm.
            Toolkit.getDefaultToolkit().beep();
            try{
              Thread.currentThread().sleep(500);
            }catch(Exception ex){
              System.out.println(ex);
            }//end catch
            Toolkit.getDefaultToolkit().beep();
                         
            //All messages have been
            // downloaded and saved as local
            // files.  Instantiate an object
            // that will forward each of the
            // messages to the specified Email
            // address.
            new BigDog04i(
                params[0],//pop3Server
                params[1],//userName
                params[2],//password
                params[3],//destinationAddress
                params[4],//smtpServer
                params[5],//workingDir
                params[6],//archiveDir
                params[7]);//fwdTag
          }//end try
          catch(Exception ex){
            ex.printStackTrace();
          }//end catch
        }//end actionPerformed
      }//end ActionListener
    );//end addActionListener
    //Configure the GUI by placing the various
    // components on it, setting the size and
    // making it visible.
    add(downLoadButton);
    add(textArea);
    textArea.setText("");
    setLayout(new FlowLayout());
    setTitle("Copyright 2005, R.G.Baldwin");
    setBounds(0,0,250,200);
    //Make the GUI visible.
    setVisible(true);
  }//end constructor
  //===========================================//
  //Validate a one-line response.
  //The purpose of this method is to confirm that
  // the pop3Server returned +OK and not -ERR to
  // the previous command.
  //If +OK, the method returns the string
  // returned by the pop3Server.
  //If -ERR, the method displays the string
  // returned by the pop3Server and terminates
  // the session.
  private String validateOneLine(){
    try{
      String response = inputStream.readLine();
      if(response.startsWith("+OK")){
        return response;
      }else{
        System.out.println(response);
        //Terminate the session.
        outputStream.println("QUIT");
        socket.close();
        System.out.println(
                       "Premature QUIT on -ERR");
        System.exit(0);
      }//end else
    }catch(IOException ex){
      ex.printStackTrace();
    }//end catch
    //The following return statement is required
    // to satisfy the compiler, but it can never
    // be reached.
    return "Make compiler happy";
  }//end validateOneLine()
  //===========================================//
}//end class BigDog04g
//=============================================//
Listing 45

 

/*File BigDog04i.java
Copyright 2005, R.G.Baldwin
Rev 08/12/05
DISCLAIMER:  THIS PROGRAM IS PROVIDED TO YOU AT 
NO COST.  BY USING THIS PROGRAM TO PROCESS YOUR 
EMAIL, YOU AGREE THAT YOU ARE USING IT AT YOUR 
OWN RISK.  THE AUTHOR OF THE PROGRAM, RICHARD G.
BALDWIN, ACCEPTS NO LIABILITY FOR ANY LOSS THAT 
YOU MAY INCUR THROUGH THE USE OF THIS PROGRAM.
An object of this class is instantiated by the 
program named BigDog04g to process a set of 
message files written by the program named 
BigDog04g.    
This object tags messages with a tag defined by 
the user as a command-line parameter to the 
constructor for the class named BigDog04g.  Then 
the object forwards the messages to a destination
email account that I specified as a command-line 
parameter using an SMTP server that is specified 
as a command-line parameter.
See additional comments at the beginning of 
BigDog04g.java.
This program uses the DELE command to delete
messages from the POP3 server from which the 
messages were originally downloaded.  Deletion of
the messages from the server can be disabled for 
test purposes by disabling a block of code before
compiling the program.
Certain portions of this program have been
disabled for test purposes.  Search for the word
disable to identify those portions.
Tested using JDK 1.5.0_01 under WinXP
************************************************/
import java.net.*;
import java.io.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import sun.net.smtp.SmtpClient;
class BigDog04i extends Frame{
  //User-specific information is stored here.
  // This information is provided in the form of
  // constructor parameters.
  //ID of the destination email account.
  String destinationAddress;
  //An smtp server through which the user is
  // authorized to send email messages.
  String smtpServer;
  //Local folder where message files are stored
  // awaiting processing.
  final String workingDir;
  //Local folder for archiving message files.
  final String archiveDir;
  //Tag that is prepended to the Subject line of
  // the message before forwarding.
  String fwdTag;
  //Following are working variables used by the
  // program for various purposes.
  BufferedReader inputStream;
  PrintWriter outputStream;
  Socket socket;
  String pathFileName;
  Vector <String> msgToDelete = 
                            new Vector<String>();
  Button forwardButton = 
                      new Button("Forward Msgs");
  Button deleteButton = new Button(
                         "Delete Msg On Server");
  TextArea textArea = new TextArea(5,30);
  String uidl;
  boolean okToDelete = false;
  int msgNumber = 0;
  //===========================================//
  //Constructor
  BigDog04i(final String pop3Server,
            final String userName,
            final String password,
            String destinationAddress,
            String smtpServer,
            final String workingDir,
            final String archiveDir,
            String fwdTag){
    this.destinationAddress = destinationAddress;
    this.smtpServer = smtpServer;
    this.workingDir = workingDir;
    this.archiveDir = archiveDir;
    this.fwdTag = fwdTag;
    
    System.out.println("Forwarding messages:");
    //Register a window listener to service
    // the close button on the Frame.
    this.addWindowListener(
      new WindowAdapter(){
        public void windowClosing(WindowEvent e){
          System.exit(0);
        }//end windowClosing
      }//end WindowAdapter()
    );//end addWindowListener
    //Register an ActionListener on the
    // forwardButton.
    forwardButton.addActionListener(
      new ActionListener(){
        public void actionPerformed(
                                  ActionEvent e){
          forwardButton.setEnabled(false);
          //Get a directory listing
          //The following code creates a
          // directory listing containing only
          // those files that begin with +OK.
          //This is an anonymous implementation
          // of a class that implements
          // FilenameFilter.
          String[] dirList = 
                       new File(workingDir).list(
            new FilenameFilter(){
              public boolean accept(
                           File dir,String name){
                if(!(new File(dir,name).
                  isFile())) return false;
                return name.startsWith("+OK");
              }//end accept
            }//end FilenameFilter
          );//end list
          //Now process the files in the
          // directory
          for(int msgCounter = 0;
                     msgCounter < dirList.length;
                                   msgCounter++){
            String fileName =
                             dirList[msgCounter];
            pathFileName = workingDir + fileName;
            //Get the original message number
            // used by the pop3Server to ID the
            // msg.
            String strMsgNumber =
                   fileName.substring(
                     fileName.indexOf(" "),
                       fileName.lastIndexOf(" "))
                                         .trim();
            msgNumber =
                  Integer.parseInt(strMsgNumber);
            System.out.print(
                           "" + msgNumber + " ");
            //Process the message
            processTheMsg();
          }//end for loop on directory length
          //Make it possible for the user to
          // delete all processed messages from
          // the pop3Server, and notify the user
          // that the time has come for a
          // deletion decision.
          deleteButton.setEnabled(true);
          textArea.append(
               "Delete msgs from pop3Server?n");
          //Sound an audio alert
          try{
            Toolkit.getDefaultToolkit().beep();
            Thread.currentThread().sleep(500);
            Toolkit.getDefaultToolkit().beep();
            Thread.currentThread().sleep(500);
            Toolkit.getDefaultToolkit().beep();
          }catch(Exception ex){
                           ex.printStackTrace();}
        }//end actionPerformed
      }//end ActionListener
    );//end addActionListener
    //Register an action listener on the delete
    // button
    deleteButton.addActionListener(
      new ActionListener(){
        public void actionPerformed(
                                  ActionEvent e){
          deleteButton.setEnabled(false);
          textArea.append("n");
          System.out.println(
              "nDelete Msgs from POP3 server.");
          //Get connected to the email pop3Server
          int port = 110; //pop3 mail port
          try{
            //Get a socket, connected to the
            // specified pop3Server on the
            // specified port.
            socket = new Socket(pop3Server,port);
            //Get an input stream from the socket
            inputStream = new BufferedReader(
                     new InputStreamReader(
                       socket.getInputStream()));
            //Get  an output stream to the socket
            outputStream = new PrintWriter(
               new OutputStreamWriter(
                 socket.getOutputStream()),true);
            //Display the msg received from the
            // pop3Server on the command-line
            // screen immediately following
            // connection.
            String connectMsg =
                               validateOneLine();
            System.out.println(
                    "nConnected to pop3Server "
                                   + connectMsg);
            //The communication process is now in
            // the AUTHORIZATION state.  Send the
            // user name and password to the
            // pop3Server.
            outputStream.println("USER "
                                     + userName);
            //Get response and confirm that the
            // response was +OK and was not -ERR.
            String userResponse =
                               validateOneLine();
            //Display the response on the
            // command-line screen.
            System.out.println("USER "
                                 + userResponse);
            //Send the password to the pop3Server
            outputStream.println("PASS "
                                     + password);
            //Validate the pop3Server's response
            // as +OK. Display the response in
            // the process.
            System.out.println("PASS "
                            + validateOneLine());
          }catch(Exception ex){
                           ex.printStackTrace();}
          //Process the files in the msgToDelete
          // collection and delete those messages
          // from the email pop3Server
          System.out.println(
            "nDeleting Msgs from POP3 server.");
          for(int cnt = 0;
                 cnt < msgToDelete.size();cnt++){
            pathFileName = msgToDelete.
                                  elementAt(cnt);
            String strMsgNumber = pathFileName.
             substring(pathFileName.indexOf(" "),
                  pathFileName.lastIndexOf(" ")).
                                          trim();
            int msgNumber = Integer.parseInt(
                                   strMsgNumber);
            //Deletion of a message from the
            // pop3Server is accomplished by
            // marking the message for deletion
            // while in the TRANSACTION state.
            // The message is actually deleted
            // when the client sends a QUIT
            // command to the pop3Server causing
            // the pop3Server to enter the UPDATE
            // state.  If the program aborts
            // prematurely before sending a QUIT
            // command, marked messages are not
            // deleted from the pop3Server.
            //Mark the message for deletion.
/*
            //Disable the following statement if
            // you enable message deletion.
            textArea.append(
                  "nMessage deletion disabled");
*/
            //Disable this code to disable
            // deletion of messages from
            // pop3Server
            outputStream.println("DELE "
                                    + msgNumber);
            //Validate the response and display
            // it on the GUI.
            textArea.append(
                    "Msg: " + msgNumber + " "
                       + validateOneLine()+"n");
            textArea.append(
                  "Deleted:" + msgNumber + "n");
            //Disable to here to disable
            // deletion of messages from
            // pop3Server
            //You can customize the program to
            // either move the message files from
            // the working directory into an
            // archive directory, or to simply
            // delete the message files from the
            // working directory without saving
            // them in an archive directory.
            
            //To move message files to the
            // archive directory, enable this
            // block of code and disable the next
            // block of code.
            {//Start move block
              moveFile(pathFileName,archiveDir);
            }//End move block
/*
            //To delete message files without
            // saving them in an archive
            // directory, enable this block of
            // code and disable the previous
            // block of code.
            {//Start delete block
              File file = new File(pathFileName);
              boolean isDeleted = file.delete();
              if(!isDeleted)System.out.println(
                     "Unable to delete " + file);
            }//End delete block
*/
          }//end for loop on msgToDelete.size()
          
          //Terminate the session with the
          // pop3Server causing the marked
          // messages to actually be deleted from
          // the pop3Server.
          System.out.println(
                  "Disconnect from POP3 server");
          outputStream.println("QUIT");
          String quitResponse =
                               validateOneLine();
          //Display the response on the
          // command-line screen.
          System.out.println(
                         "QUIT " + quitResponse);
          //Server is now in the UPDATE mode.
          // It will delete all files marked
          // with the DELE command earlier
          // in the execution of the program.
          //Close the socket
          try{
            socket.close();
          }catch(Exception ex){
            ex.printStackTrace();
          }//end catch
          textArea.append("nnMessages deleted "
               + "from pop3Server.n"
               + "Press X button to terminate.");
        }//end actionPerformed
      }//end ActionListener
    );//end addActionListener
    //-----------------------------------------//
    //Configure the GUI by placing the various
    // components on it, setting the size, and
    // making it visible.
    add(forwardButton);
    add(deleteButton);
    deleteButton.setEnabled(false);
    add(textArea);
    textArea.setText("");
    setLayout(new FlowLayout());
    setTitle("Copyright 2005, R.G.Baldwin");
    setBounds(250,0,250,200);
    //Make the GUI visible.
    setVisible(true);
  }//end constructor
  //===========================================//
  //Validate a one-line response.
  //The purpose of this method is to confirm that
  // the pop3Server returned +OK and not -ERR to
  // the previous command.
  //If +OK, the method returns the string
  // returned by the pop3Server.
  //If -ERR, the method displays the string
  // returned by the pop3Server and terminates
  // the session.
  private String validateOneLine(){
    try{
      String response = inputStream.readLine();
      if(response.startsWith("+OK")){
        return response;
      }else{
        System.out.println(response);
        //Terminate the session.
        outputStream.println("QUIT");
        socket.close();
        System.out.println(
                       "Premature QUIT on -ERR");
        System.exit(0);
      }//end else
    }catch(IOException e){
      e.printStackTrace();
    }//end catch
                
    //The following return statement is required
    // to satisfy the compiler, but it can never
    // be reached..
    return "Make compiler happy";
  }//end validateOneLine()
  //===========================================//
  //The purpose of this method is to kick off the
  // processing of a new message.
  void processTheMsg(){
    //Get and save the unique identifier assigned
    // by the pop3Server.
    uidl = pathFileName.substring(
                  pathFileName.lastIndexOf(" "));
    //This code forwards the message to the
    // destination email account and adds the
    // identification of the message to the list
    // of messages scheduled for deletion from
    // the pop3Server later.  The third parameter
    // can be used to tag the message to show
    // that it was forwarded.  This parameter is
    // provided as a command-line parameter to
    // the BigDog04g program.  Just pass an empty
    // string, "", if you don't want to tag it.
    //Don't add the message to the deletion list
    // if forwarding failed.
    okToDelete = forwardEmailMsg(
                              destinationAddress,
                              smtpServer,
                              fwdTag,
                              pathFileName);
    if(okToDelete){
      msgToDelete.add(pathFileName);
    }//end if
  }//end processTheMsg
  //===========================================//
  
  //Method to move a file from its current
  // location specified by pathFileName to a new
  // location specified by archiveDir.
  public void moveFile(String pathFileName,
                       String archiveDir){
    String fileName = pathFileName.substring(
              pathFileName.lastIndexOf('/') + 1);
    String archiveDirFileName =
                           archiveDir + fileName;
    boolean moved =
                 new File(pathFileName).renameTo(
                   new File(archiveDirFileName));
    if(!moved)System.out.println(
      "Unable to move " + new File(pathFileName)
      + "nto " + new File(archiveDirFileName));
  }//end moveFile method
  //===========================================//
  
  //This method reads and saves lines of data
  // from a file starting with the line that
  // startsWith firstLine and ending with the
  // line that startsWith lastLine.
  //If firstLine is null, data is saved beginning
  // with the first line in the file.
  //If lastLine is null, data is saved to the end
  // of the file.
  //The lines of data from the file are saved by
  // concatenating them into a single string with
  // a newline inserted into the string at the
  // end of each line.
  //The name and path to the file is given by
  // pathFileName.
  public String readLines(String pathFileName,
                          String firstLine,
                          String lastLine){
    StringBuffer strBuf = new StringBuffer();
    try{
      BufferedReader inDataMsg
        = new BufferedReader(new FileReader(
                                  pathFileName));
      String data;
      boolean isSave = false;
      while((data = inDataMsg.readLine())
                                        != null){
        if( ((firstLine == null) ||
             (data.startsWith(firstLine))) &&
             (isSave == false)){
          isSave = true;
        }//end if
        if(isSave){
          strBuf.append(data + "n");
        }//end if
        if((lastLine != null) &&
           (data.startsWith(lastLine))){
          break;//no need to read any more
        }//end if
      }//end while loop
      inDataMsg.close();//Close file
    }catch(Exception e){e.printStackTrace();}
    return new String(strBuf);
  }//end readLines
  //===========================================//
  
  //This method is used to construct an email
  // message and send it to the 
  // destinationAddress.
  public boolean forwardEmailMsg(
                       String destinationAddress,
                       String smtpServer,
                       String fwdTag,
                       String pathFileName){
      StringBuffer message = new StringBuffer(
                             "No message found");
      try{
        //Pass a string containing the name of
        // the smtp server as a parameter to the
        // following constructor.
        SmtpClient smtp =
                      new SmtpClient(smtpServer);
        //Pass any valid email address to the
        // from() method.
        smtp.from(destinationAddress);
        //Pass the email address of the
        // destinationAddress to the to() method.
        smtp.to(destinationAddress);
        //Construct the message as a single
        // StringBuffer object by concatenating
        // all of the lines in the message file.
        message = new StringBuffer(readLines(
                        pathFileName,null,null));
        //Insert fwdTag in subject
        message = message.insert(message.indexOf(
                          "Subject: ")+9,fwdTag);
        //Get an output stream for the message
        PrintStream msg = smtp.startMessage();
        //Write the message into the output
        // stream.
        msg.println(new String(message));
        //Close the stream and send the message
        smtp.closeServer();
        return true;
      }catch( Exception e ){
        e.printStackTrace();
        System.out.println(
                       "while forwarding email");
        //Sound an alarm.
        Toolkit.getDefaultToolkit().beep();
        try{
          Thread.currentThread().sleep(500);
        }catch(Exception ex){
          System.out.println(ex);
        }//end catch
        Toolkit.getDefaultToolkit().beep();
        
        //Return false to indicate that the msg
        // was not successfully forwarded.
        return false;
      }//end catch
  }//end forwardEmailMsg
  //===========================================//
}//end class BigDog04i
//=============================================//

Listing 46

 


Copyright 2005, Richard G. Baldwin.  Reproduction in whole or in part in any
form or medium without express written permission from Richard Baldwin is
prohibited.

About the author

Richard Baldwin is a
college professor (at Austin Community College in Austin, TX) and private
consultant whose primary focus is a combination of Java, C#, and XML. In
addition to the many platform and/or language independent benefits of Java and
C# applications, he believes that a combination of Java, C#, and XML will become
the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects and he
frequently provides onsite training at the high-tech companies located in and
around Austin, Texas.  He is the author of Baldwin’s Programming
Tutorials, which have gained a
worldwide following among experienced and aspiring programmers. He has also
published articles in JavaPro magazine.

In addition to his programming expertise, Richard has many years of
practical experience in Digital Signal Processing (DSP).  His first job after he
earned his Bachelor’s degree was doing DSP in the Seismic Research Department of
Texas Instruments.  (TI is still a world leader in DSP.)  In the following
years, he applied his programming and DSP expertise to other interesting areas
including sonar and underwater acoustics.

Richard holds an MSEE degree from Southern Methodist University and has
many years of experience in the application of computer technology to real-world
problems.

Baldwin@DickBaldwin.com

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories