/* * Copyright (c) 2001 Matthew Feldt. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided the copyright notice above is * retained. * * THIS SOFTWARE IS PROVIDED ''AS IS'' AND WITHOUT ANY EXPRESSED OR * IMPLIED WARRANTIES. */ /** * Checkmail.java * * Java Examples In A Nutshell Copyright (c) 2000 David Flanagan * Exercise 5-4: * In the discussion of Example 5-8, GenericClient, there is an example of * using that program to communicate with a POP (Post Office Protocol) server * to retrieve email. The POP protocol is a simple one; a little experimentation * with GenericClient should allow you to figure out how it works. (Be careful * not to delete any important email!) * * For this excercise write a client program named Checkmail that uses the POP * protocol to check a user's mail. It should output the number of messages that * are waiting to be retrieved and display the From line of each message. This * client should not use the POP dele command to delete mail messages from * the server; it should simply display a summary of the messages waiting to be * retrieved. In order to read mail messages, this client has to know the host- * name of the POP server and has to send a username and password to the * server. Your program may obtain the hostname, username, and password * from the command line or by prompting the user, but should ideally get this * information by reading a configuration file. Consider a java.util.Properties * object to implement such a configuration file. * * @author Matthew Feldt * @version 1.1, 04/20/2001 22:17 */ package com.feldt.examples.net; import java.io.*; import java.net.*; import java.util.*; import java.text.*; import javax.mail.internet.MailDateFormat; public class Checkmail { private String mailhost, username, password; private int mailport; private Socket sock; private BufferedReader in; private PrintWriter out; protected boolean isConnected; public static final String MAIL_PORT = "110"; // default mail port /** construct a POP connection with the specified properties */ Checkmail(String host, int port, String user, String pass) { mailhost = host; mailport = port; username = user; password = pass; isConnected = false; } /** * construct a POP connection with the specified properties * and default mail port */ Checkmail(String host, String user, String pass) { this(host, Integer.parseInt(MAIL_PORT), user, pass); } /** connect and log in to the specified POP server */ public void connect() throws IOException { String response; if (isConnected) return; // connect to the specified host and port sock = new Socket(mailhost, mailport); // configure socket io streams in = new BufferedReader(new InputStreamReader(sock.getInputStream())); out = new PrintWriter(sock.getOutputStream(), true); // log in response = in.readLine(); // consume welcome banner out.println("user " + username); // send username response = in.readLine(); // consume "+OK please send PASS command" out.println("pass " + password); // send password response = in.readLine(); // confirm password accepted if (response.substring(0, 3).compareTo("+OK") != 0) throw new IOException(response); isConnected = true; // set connected flag } /** close POP connection and update isConnected flag */ public void close() throws IOException { sock.close(); isConnected = false; } /** format a decimal with leading spaces */ private String decimalFormat(long number, int length) { DecimalFormat df = new DecimalFormat("###0"); StringBuffer result = new StringBuffer(); FieldPosition pos = new FieldPosition(NumberFormat.INTEGER_FIELD); df.format(number, result, pos); if (pos.getEndIndex() < length) { char[] padding = new char[length-pos.getEndIndex()]; for (int i = 0; i < padding.length; i++) padding[i] = ' '; result.insert(0, padding); } // result.length() can be greater than spaces return new String(result); } /** format email address */ private String emailFormat(String email, int length) { int open, close; String result = email; if ((open = email.indexOf('<')) != -1) // address bound by <> if ((close = email.indexOf('>')) != -1) result = email.substring(open+1, close); return formatStringLength(result, length, true); } /** format the date */ private String dateFormat(String in, int length) { MailDateFormat mf = new MailDateFormat(); SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd yyyy HH:mm"); Date date = null; // trap parse error and continue try { date = mf.parse(in); } catch(ParseException e) {} // format what we got or return a properly formatted date if (date == null) return formatStringLength(in, length, true); else return sdf.format(date); } /** return a string of 'length' length */ private String formatStringLength(String in, int length, boolean pad) { String out = in; if (pad) { if (out.length() < length) { // pad with spaces if length() < length char[] padding = new char[length-out.length()]; for (int i = 0; i < padding.length; i++) padding[i] = ' '; out += new String(padding); } } if (out.length() > length) { // truncate if length() > length out = out.substring(0, length); } return out; } /** get mail headers from specified pop server (Checkmail object) */ public void printHeaderSummary() throws IOException { final int DATE_LENGTH = 21; final int FROM_LENGTH = 20; final int MESSAGECOUNT_LENGTH = 4; final int SUBJECT_LENGTH = 27; String response, from = null, subject = null, date = null; int messageCount; boolean header; if (! isConnected) throw new IOException("not connected."); // set up a flushed system output stream PrintWriter sysOutFlushed = new PrintWriter(System.out, true); out.println("stat"); // get messages count StringTokenizer token = new StringTokenizer(response = in.readLine()); if ((token.countTokens() != 3) || (token.nextToken().compareTo("-ERR") == 0)) throw new IOException("stat error: " + response); messageCount = Integer.parseInt(token.nextToken()); // request messages in order and print header summary for (int i = 1; i <= messageCount; i++) { header = true; out.println("retr " + i); while (true) { response = in.readLine(); if (response.compareTo(".") == 0) break; // end of message // head and body separater by blank line if ((header) && (response.compareTo("") == 0)) header = false; if (header) { // only review headers if (response.startsWith("From: ")) { from = response.substring(6); // remove From: } if (response.startsWith("Subject: ")) { subject = response.substring(9); // remove Subject: } if (response.startsWith("Date: ")) { date = response.substring(6); // remove Date: } } } sysOutFlushed.println(decimalFormat(i, MESSAGECOUNT_LENGTH) + " " + emailFormat(from, FROM_LENGTH) + " " + dateFormat(date, DATE_LENGTH) + " \"" + formatStringLength(subject, SUBJECT_LENGTH, false) + "\""); } } /** * get configuration from a Properties file, or prompt the user * for missing values. optionally will save queried values to a * Properties file. */ protected static Checkmail getCheckmailConfig(String filename) throws FileNotFoundException, IllegalArgumentException, IOException { final String HOST_KEY_STRING = "mailhost"; // property file key strings final String USER_KEY_STRING = "username"; final String PASS_KEY_STRING = "password"; final String PORT_KEY_STRING = "mailport"; String host = null, user = null , pass = null, port = null, response; BufferedReader userInput = new BufferedReader( new InputStreamReader(System.in)); boolean prompted = false; Properties p = new Properties(); if (filename != null) { // load Properties is filename was supplied File f = new File(filename); if (! f.exists()) fail(filename + ": no such file."); if (! f.canRead()) fail(filename + ": read protected."); if (! f.isFile()) fail(filename + ": is not a regular file."); p.load(new FileInputStream(f)); host = p.getProperty(HOST_KEY_STRING); port = p.getProperty(PORT_KEY_STRING); user = p.getProperty(USER_KEY_STRING); pass = p.getProperty(PASS_KEY_STRING); } if (host == null) { System.out.print("Enter mailhost > "); host = userInput.readLine(); p.setProperty(HOST_KEY_STRING, host); prompted = true; } if (port == null) { System.out.print("Enter mailport > "); port = userInput.readLine(); p.setProperty(PORT_KEY_STRING, port); prompted = true; } if (user == null) { System.out.print("Enter username > "); user = userInput.readLine(); p.setProperty(USER_KEY_STRING, user); prompted = true; } if (pass == null) { System.out.print("Enter password > "); pass = userInput.readLine(); p.setProperty(PASS_KEY_STRING, pass); prompted = true; } // if user prompted for input see if they want to save if (prompted) { System.out.print("Save configuration [y/n] > "); response = userInput.readLine(); if (response.compareToIgnoreCase("y") == 0) { System.out.print("Configuration filename > "); response = userInput.readLine(); // currently will overwrite existing file p.store(new FileOutputStream(response), null); System.out.println("Configuration saved."); } } return new Checkmail(host, Integer.parseInt(port), user, pass); } /** shorthand method to throw an exception with the appropriate message */ protected static void fail(String msg) throws IllegalArgumentException { throw new IllegalArgumentException(msg); } /** test class */ static class Test { public static void main(String[] args) throws IOException { final String USAGE = "Usage: java Checkmail$Test []"; String configFile = null; Checkmail cm; // parse the command line if (args.length != 0 && args.length != 1) fail(USAGE); // config file supplied on command line if (args.length == 1) configFile = args[0]; try { cm = Checkmail.getCheckmailConfig(configFile); cm.connect(); // open Checkmail connection cm.printHeaderSummary(); // print header summary cm.close(); // close Checkmail connection } catch(FileNotFoundException e){ System.err.println(e.getMessage()); System.exit(-1); } catch(IllegalArgumentException e) { System.err.println(e.getMessage()); System.exit(-1); } catch(IOException e) { System.err.println(e.getMessage()); System.exit(-1); } } } }