/*************************************************************************
* *
* File: PopWeb.java *
* Date: April 4 2000 *
* Author: Donn Morrison donn_morrison@hotmail.com *
* Purpose: Seng462 Final Project *
* *
* Description: PopWeb is a Java Servlet which allows a user to *
* send/recieve email from a standard POP3 account. *
* *
* License: PopWeb can be copied, modified, pirated, traded, deleted, *
* truncated, burnt to cd and driven over, etc. Just as long *
* as this header is left in the source code and I am given *
* credit for writing the original version as you pass it on. *
* *
*************************************************************************/
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
import java.net.*;
/**
* class PopWeb
*
* - provides methods and objects that enable use as a Java Servlet
*
*/
public class PopWeb extends HttpServlet {
PrintWriter clientout = null; // writes data to clients' browser
private Socket socket; // used to connect to POP3/SMTP servers
private PrintWriter popout; // writes data to POP3/SMTP servers
private BufferedReader popin; // reads data from POP3/SMTP servers
private Thread cm; // Thread that monitors our connection
private Object lock = new Object(); // a lock for our critical section
private boolean sortbydatedec = true; // sort email index by date descending
private boolean showallheaders = false; // show all email headers when viewing
// ------------------------------------------------------
// These variables are http GET/POST request parameters
// ------------------------------------------------------
private String command; // http param: main command
private String popserver; // http param: pop3 server
private String username; // http param: username
private String passwordstr; // http param: password (shifted)
private String password; // http param: plain text password
private String msgnumstr; // http param: message number
private String from; // http param: 'from' address
private String replyto; // http param: 'reply-to' address
private String to; // http param: 'to' address
private String cc; // http param: 'cc' address
private String message; // http param: email message
private String subject; // http param: email subject
private String showallheadersstr; // http param: show all headers
private String file; // http param: attachment filename on server
private String attachfilename; // http param: attachment real filename
private String attachcontenttype; // http param: attachment content-type
// ------------------------------------------------------
// Constants
// ------------------------------------------------------
public static final int WRITE_NORMAL = 0;
public static final int WRITE_REPLY = 1;
public static final int WRITE_FORWARD = 2;
public static final String SMTP_SERVER = "mail";
public static final String HOST_NAME = "popweb.dyndns.org";
public static final String XMAILER_INFO = "X-Mailer: PopWeb v.05 [en] (Java Servlet; Apache; Linux)\n";
public static final String HTML_DIR = "/usr/local/apache/servlets/";
public void service(HttpServletRequest req, HttpServletResponse res) throws IOException {
res.setContentType("text/html");
try {
// ------------------------------------------------------
// We attempt to retrieve the http parameters
// ------------------------------------------------------
command = req.getParameter("command");
popserver = req.getParameter("popserver");
username = req.getParameter("username");
passwordstr = req.getParameter("password");
/* if(passwordstr != null && username != null) {
passwordstr = URLDecoder.decode(passwordstr);
if(passwordstr.startsWith("==~%%$")) {
password = Shift.unShift(passwordstr.substring(6),username);
passwordstr = URLEncoder.encode(passwordstr);
} else {
password = passwordstr;
passwordstr = Shift.Shift(passwordstr,username);
passwordstr = URLEncoder.encode("==~%%$" + passwordstr);
}
}
*/ msgnumstr = req.getParameter("msgnum");
message = req.getParameter("message");
from = req.getParameter("from");
replyto = req.getParameter("replyto");
to = req.getParameter("to");
cc = req.getParameter("cc");
subject = req.getParameter("subject");
showallheadersstr = req.getParameter("hdrs");
attachfilename = req.getParameter("attachfilename");
attachcontenttype = req.getParameter("attachcontenttype");
file = req.getParameter("file");
if(showallheadersstr != null) {
if(showallheadersstr.equals("less")) showallheaders=false;
else showallheaders=true;
} else {
showallheadersstr = "less";
showallheaders=false;
}
// log the current session for security reasons
logSession(req);
// ------------------------------------------------------
// We determine which "command" was issued and take
// appropriate action
// ------------------------------------------------------
if(command == null) {
// display login screen
clientout = res.getWriter();
cm = new ConnectionMonitor(clientout);
showLogin();
} else if(command.equals("index")) {
// show message index
clientout = res.getWriter();
cm = new ConnectionMonitor(clientout);
checkParams();
showIndex();
} else if(command.equals("viewmsg")) {
// show an email message
clientout = res.getWriter();
cm = new ConnectionMonitor(clientout);
checkParams();
showMessage();
} else if(command.equals("logout")) {
// display logout screen
clientout = res.getWriter();
cm = new ConnectionMonitor(clientout);
showLogout();
} else if(command.equals("write")) {
// show write message screen (normal write)
clientout = res.getWriter();
cm = new ConnectionMonitor(clientout);
checkParams();
showWrite(WRITE_NORMAL);
} else if(command.equals("reply")) {
// show write message screen (reply)
clientout = res.getWriter();
cm = new ConnectionMonitor(clientout);
checkParams();
showWrite(WRITE_REPLY);
} else if(command.equals("forward")) {
// show write message screen (forward)
clientout = res.getWriter();
cm = new ConnectionMonitor(clientout);
checkParams();
showWrite(WRITE_FORWARD);
} else if(command.equals("send")) {
// send the written message and display confirmation
clientout = res.getWriter();
cm = new ConnectionMonitor(clientout);
checkParams();
showSend();
} else if(command.equals("delete")) {
// delete the current message
clientout = res.getWriter();
cm = new ConnectionMonitor(clientout);
checkParams();
showDelete();
} else if(command.equals("deleteall")) {
// confirm delete all
clientout = res.getWriter();
cm = new ConnectionMonitor(clientout);
checkParams();
showDeleteAllConfirm();
} else if(command.equals("deleteall2")) {
// confirm delete all
clientout = res.getWriter();
cm = new ConnectionMonitor(clientout);
checkParams();
showDeleteAll();
} else if(command.equals("disclaimer")) {
// show the disclaimer
clientout = res.getWriter();
cm = new ConnectionMonitor(clientout);
showDisclaimer();
} else if(command.equals("viewsource")) {
// view the PopWeb source
clientout = res.getWriter();
cm = new ConnectionMonitor(clientout);
showSource();
} else if(command.equals("getattach")) {
// send the requested attachment
showGetAttach(res);
} else if(command.equals("nojs")) {
// client browser does not have javascript enabled
clientout = res.getWriter();
printError("error","You do not have a JavaScript enabled browser.");
} else if(command.equals("yesjs")) {
// client browser does have javascript enabled
clientout = res.getWriter();
printError("success","You have a JavaScript enabled browser.");
} else {
// show an error message
clientout = res.getWriter();
cm = new ConnectionMonitor(clientout);
printError("error","Invalid command");
}
} catch (Exception e) {
//e.printStackTrace(clientout);
clientout = res.getWriter();
String msg = e.getMessage();
if(msg == null) msg = "An error has occured. Unfortunately, the error itself is classified.";
printError("error",msg);
try {
disconnect();
} catch (Exception ex) {}
} /* try catch */
} /* service */
/**
* class ConnectionMonitor
*
* - Allows us to determine whether the client has disconnected
* - If a disconnection occurs, we try to disconnect from the
* Pop3/SMTP server
*
*/
private class ConnectionMonitor extends Thread {
PrintWriter pw;
ConnectionMonitor(PrintWriter pw) {
this.pw = pw;
this.start();
}
public void run() {
while(true) {
// test if we can still write
// to the client
if(pw.checkError()) {
try {
disconnect();
} catch (Exception e) {}
break;
}
try {
Thread.sleep(100);
} catch (Exception e) {}
}
}
}
/**
* checkParams()
*
* - Makes sure client has sent all information
* required to complete a mail transaction
*
*/
private void checkParams() throws Exception {
if((popserver == null || username == null || passwordstr == null) ||
(popserver.equals("") || username.equals("") || passwordstr.equals(""))) {
throw new Exception("POP3 Server, Username, and Password are required.");
}
}
/**
* showLogin()
*
* - Displays the login screen
*
*/
private void showLogin() {
showFile("PopWebLogin.html",false);
}
/**
* showDisclaimer()
*
* - Displays the disclaimer screen
*
*/
private void showDisclaimer() {
showFile("PopWebDisclaimer.html",false);
}
/**
* showLogout()
*
* - Displays the logout screen
*
*/
private void showLogout() {
showFile("PopWebLogout.html",false);
}
/**
* showGetAttach()
*
* - Sends the attachment to the clients' web browser
* - Decodes the attachment using a base64 decoder
*
* - problem: does not support content encoding other than base64
*
*/
private void showGetAttach(HttpServletResponse res) throws Exception {
// make sure we stay in the attachment directory
if(file.indexOf("..") >= 0) throw new Exception("Path error: Nice try, but I've thought of that");
// set the content-type
if(!attachcontenttype.equals("")) {
res.setContentType(attachcontenttype);
} else {
res.setContentType("text/plain");
}
File f = new File(HTML_DIR + "attach/" + file);
// ensure that the file exists
if(f.exists()) {
if(attachcontenttype.equalsIgnoreCase("text/html")) {
// attachment is embedded html
clientout = res.getWriter();
showFile("attach/" + file,false);
} else {
// decode the attachment and send it to
// the clients' web browser
InputStream fin = new FileInputStream(f);
sun.misc.BASE64Decoder b64dec = new sun.misc.BASE64Decoder();
b64dec.decodeBuffer(fin,res.getOutputStream());
}
// delete the attachment
//f.delete(); // (should be left up to a seperate process on the server: eg cron)
} else throw new Exception("Path error: Your attachment has been deleted; reload the message again to retrieve it");
}
/**
* showSource()
*
* - Displays the PopWeb source code to the client
*
*/
private void showSource() {
// html source 1
showFile("pwbody1.thtml",false);
clientout.println("PopWeb view source");
// html source 2
showFile("pwbody2.thtml",false);
clientout.println("
");
// show PopWeb source
showFile("PopWeb.java",true);
clientout.println("
");
// html source 3
showFile("pwbody3.thtml",false);
}
/**
* showIndex()
*
* - Displays the message index to the client
*
* - problem: If the client has a large volume
* of email messages, it will take
* a while to display the headers and
* the client may give up (press stop
* or cancel)
*
*/
private void showIndex() throws Exception {
// attempt to connect to the server
connect();
// attempt to login to the server
login();
// get STAT (message count)
int total = doSTAT();
// retreive the current message number
int msgnum = 1;
if(msgnumstr != null) {
try {
msgnum = Integer.parseInt(msgnumstr);
} catch (Exception e) {}
}
clientout.println(showtoolbar(1,1,false));
String waittime = total > 20?" Please be patient as downloading headers may take some time.":"";
String output = "PopWeb [" + username + "@" + popserver + "]\n" +
"\n" +
" Welcome, " + username + ", you have " + total +
(total == 1 ? " message ":" messages ") + "on " + popserver + "."+waittime+"
\n";
clientout.println(output);
// display the message index
output = "
\n" +
"
\n" +
"
\n" +
"
\n" +
" \n" +
"
\n" +
" \n" +
" From\n" +
"
\n" +
" \n" +
" Date\n" +
"
\n" +
" \n" +
" Subject\n" +
"
\n";
clientout.println(output);
clientout.flush();
// get the message headers (from, subject, date)
String [] headers = getFromDateSubject(1,total);
for(int i=0;i
\n" +
" " + shownextindexpagebutton();
clientout.println(output);
clientout.flush();
// disconnect from server
disconnect();
}
/**
* showMessage()
*
* - Displays an email message to the client
* - Saves any attachments to the attachment directory
* on the server
*
* - problem: If message size is large, client will
* be waiting for a long time, and may
* give up (press stop or cancel)
*
*/
private void showMessage() throws Exception {
// attempt to connect to the server
connect();
// attempt to login to the server
login();
// get STAT (message count)
int total = doSTAT();
// get the current message number
int msgnum = 0;
if(msgnumstr != null) {
try {
msgnum = Integer.parseInt(msgnumstr);
} catch (Exception e) {}
}
// message number is invalid, show the index instead
if(msgnum < 1 || msgnum > total) {
disconnect();
showIndex();
return;
}
String output = "PopWeb [" + username + "@" + popserver + "]\n" +
"";
clientout.println(output);
output = showtoolbar(msgnum,total,true);
clientout.println(output);
output = "
\n" +
"
\n" +
"
\n" +
" \n" +
"
\n" +
"
\n" +
"
\n";
clientout.println(output);
// display the specified message
output = "
\n";
clientout.println(output);
clientout.flush();
// disconnect from server
disconnect();
}
/**
* showWrite()
*
* - Displays the write message screen to the client
* - If client pressed "reply" or "forward", we want to
* copy the message body into the textarea of the new
* message.
*
* - problem: If the message being replied to is large, the
* client will have to wait for a long time
* and may give up (press stop or cancel)
*
*/
private void showWrite(int type) {
// get the current message number
int msgnum = 0;
if(msgnumstr != null) {
try {
msgnum = Integer.parseInt(msgnumstr);
} catch (Exception e) {}
}
// initialize message variables
message = "";
subject = "";
if(to == null) to = "";
cc = "";
// ------------------------------------------------------
// Determine if we are writing, replying or forwarding
// (to) a message
// ------------------------------------------------------
switch(type) {
case WRITE_NORMAL:
if(to.startsWith("mailto:")) {
to = to.substring(to.indexOf(":")+1);
}
break;
case WRITE_REPLY:
try {
// we need to download the message
// and place it in the textarea so
// the client can include the original
// message in his reply
connect();
login();
// get the message
message = doRETR(msgnum,false,false,true);
// get the subject
subject = getHeaderValue("Subject: ",message,"\n");
if(subject == null) subject = "";
subject = stripQuotes(subject);
if(subject.toLowerCase().startsWith("re:"))
subject = "\"" + subject + "\"";
else
subject = "\"re: " + subject + "\"";
// get the 'to' address
to = getHeaderValue("Reply-To: ",message,"\n");
if(to == null)
to = getHeaderValue("From: ",message,"\n");
if(to == null) to = "";
to = stripQuotes(to);
to = "\"" + to + "\"";
disconnect();
} catch (Exception e) {try {disconnect();} catch (Exception ex) {}}
message = "\n\n\n---original message---\n" + message;
break;
case WRITE_FORWARD:
String newmsg = "";
try {
// we need to download the message
// and place it in the textarea so
// the client can include the original
// message in his forward
connect();
login();
// get the message
message = doRETR(msgnum,false,false,true);
// get the subject
subject = getHeaderValue("Subject: ",message,"\n");
if(subject == null) subject = "";
subject = stripQuotes(subject);
subject = "\"" + subject + " (fwd)\"";
// 'to' address left blank
to = "";
disconnect();
// add '> ' to the beginning of each line
// so we know its a forwarded message
String fwchar = "> ";
BufferedReader strin = new BufferedReader(new StringReader(message));
String line;
while((line = strin.readLine()) != null) {
newmsg = newmsg + fwchar + line + "\n";
}
} catch (Exception e) { try {disconnect();} catch (Exception ex) {}}
message = "\n\n\n---forwarded message---\n" + newmsg;
break;
default:
}
// display the page title
String output = "PopWeb [send message]\n" +
"";
clientout.println(output);
// display the toolbar
clientout.println(showtoolbar(1,1,false));
// display the page heading
output = "
\n";
clientout.println(output);
// display the message composition area
output = "
" +
"
" +
" " +
" \n" +
" \n" +
"
";
clientout.println(output);
output = "
\n" +
"
\n" +
"
\n" +
"
\n" +
" \n" +
" " + "\n" +
" \n" +
"
\n" +
"
\n" +
"
\n" +
"
\n";
clientout.println(output);
clientout.flush();
}
/**
* showDelete()
*
* - Deletes a message from the POP3 server
* - Displays the message index
*
* - problem: Client has to actually view the
* message in order to delete it
*
*/
private void showDelete() throws Exception {
// get the current message number
int msgnum = 0;
if(msgnumstr != null) {
try {
msgnum = Integer.parseInt(msgnumstr);
} catch (Exception e) {}
}
connect();
login();
// delete the message
doDELE(msgnum);
disconnect();
msgnum--;
msgnumstr = msgnum + "";
showMessage();
}
/**
* showDeleteAllConfirm()
*
* - Asks user for confirmation before deleting all messages
*
*/
private void showDeleteAllConfirm() throws Exception {
String message = "Are you sure you want to delete all messages?
" +
"
\n" +
"\n
" +
"
\n" +
"
\n";
printError("Confirm delete", message);
}
private void showDeleteAll() throws Exception {
connect();
login();
// get total message count
int total = doSTAT();
for(int i=1; i <= total; i++) {
doDELE(i);
}
disconnect();
showIndex();
}
/**
* showSend()
*
* - Sends the written, replied, or forwared message
* - Displays a confirmation box if send was successful,
* or an error if unsuccessful
*
* - problem: If client sends a large message, it may
* take a while to deliver to the SMTP server
* and the client may grow impatient and
* give up (press stop or cancel)
* - problem: Client cannot attach files to the message (yet)
*
*/
private void showSend() throws Exception {
clientout.println("PopWeb [send result]");
// display the toolbar
clientout.println(showtoolbar(1,1,false));
// check the destination email addresses
parseAddresses(to);
parseAddresses(cc);
// connect to the SMTP server
connectSMTP();
// send the message
dosend();
// display success message
showFile("pwbody1.thtml",false);
clientout.println("PopWeb message delivered");
showFile("pwbody2.thtml",false);
clientout.println("Your message was successfully sent.");
showFile("pwbody3.thtml",false);
disconnect();
}
/**
* logSession()
*
* - Logs the current PopWeb transaction to a logfile
* - Records username, pop3 server, and the command
*
* - note: Does not record sensitive information
* such as the client's password or
* email messages
* - problem: May cause deadlocks when two
* clients simultaneously connect
* and file is written to by both
*
*/
private void logSession(ServletRequest req) {
// get the clients' ip address
String ipaddr = req.getRemoteAddr();
try {
// write the information to the logfile
synchronized(lock) {
FileWriter fw = new FileWriter("/usr/local/apache/servlets/PopWebLog.txt",true);
fw.write(ipaddr + ":" + username + "@" + popserver + ":" + command + "\n");
fw.close();
}
} catch (Exception e) {}
}
/**
* showFile()
*
* - Displays the contents of a plain text file to the client
* - Used to display static html pages and other server files
*
*/
private void showFile(String file, boolean stripHTML) {
try {
// read the file and print it to the client
BufferedReader filein = new BufferedReader(new FileReader(HTML_DIR + file));
String line;
while((line = filein.readLine()) != null) {
if(stripHTML) {
line = stripHTML(line);
}
clientout.println(line);
}
} catch (Exception e) {}
}
/**
* connect()
*
* - Attempts to connect to the clients' POP3 server
*
*/
private void connect() throws IOException {
try {
socket = new Socket(popserver,110);
popout = new PrintWriter(socket.getOutputStream());
popin = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
} catch (UnknownHostException e) {
throw new IOException("Unknown host: " + e.getMessage());
} catch (IOException e) {
throw new IOException("Connection error: " + e.getMessage());
}
String resp;
try {
resp = popin.readLine();
} catch (Exception e) {
throw new IOException("Connection error: connection closed by remote host");
}
if(resp == null || !resp.startsWith("+OK")) throw new IOException("Connection error: " + resp);
}
/**
* connectSMTP()
*
* - Attempts to connect to the SMTP server
*
*/
private void connectSMTP() throws IOException {
try {
socket = new Socket(SMTP_SERVER,25);
popout = new PrintWriter(socket.getOutputStream());
popin = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
} catch (UnknownHostException e) {
throw new IOException("Unknown Host: " + e.getMessage());
} catch (IOException e) {
throw new IOException("Connection error: " + e.getMessage());
}
String resp;
try {
resp = popin.readLine();
} catch (Exception e) {
throw new IOException("Connection error: connection closed by remote host");
}
if(resp == null || !resp.startsWith("2")) throw new IOException("Connection error: " + resp);
}
/**
* disconnect()
*
* - Disconnects from the clients' POP3/SMTP server
*
*/
private void disconnect() throws IOException {
if(socket == null) return;
// disconnect gracefully
popout.println("QUIT\r");
popout.flush();
String resp = popin.readLine();
// close i/o objects
popout.close();
popin.close();
socket.close();
socket = null;
}
/**
* login()
*
* - Attempts to login with clients' username and password
*
*/
private void login() throws Exception {
// send username
popout.println("USER " + username + "\r");
popout.flush();
String resp = popin.readLine();
if(!resp.startsWith("+OK")) throw new Exception("Login error (username): " + resp);
// send password
popout.println("PASS " + password + "\r");
popout.flush();
resp = popin.readLine();
if(!resp.startsWith("+OK")) throw new Exception("Login error (password): " + resp);
}
/**
* dosend()
*
* - Attempts to send clients' written, replied, or forwarded
* message to the SMTP server
*
*/
private void dosend() throws Exception {
if(to == null || to.equals("")) {
throw new IOException("\"To:\" address field cannot be left blank");
}
// store outgoing message - to be used for OUTBOX in future
// not fully implemented yet
FileWriter fw = new FileWriter("/usr/local/apache/servlets/outmail/" + username + "@" + popserver,true);
// parse 'to' and 'cc' addresses
String tolist [] = parseAddresses(to);
String cclist [] = parseAddresses(cc);
String ccstr = "";
if(cclist.length != 0) ccstr = "CC: " + cc + "\n";
String resp;
if(from == null || from.equals("")) from = username + "@" + popserver;
// form the message header
String header = "From: " + from + "\n" +
XMAILER_INFO +
"To: " + to + "\n" +
ccstr +
"Subject: " + subject + "\n" +
"\n";
// greet SMTP server
popout.println("HELO " + HOST_NAME);
popout.flush();
resp = popin.readLine();
if(!resp.startsWith("2")) throw new Exception("Error sending message: " + resp);
// mail from
popout.println("MAIL From: " + from + "");
popout.flush();
resp = popin.readLine();
if(!resp.startsWith("2")) throw new Exception("Error sending message: " + resp);
// mail to
for(int i=0;i 0) {
message = message.substring(0,idx) + "\n..\n" + message.substring(idx+3,message.length());
}
// data
popout.println("DATA");
popout.flush();
resp = popin.readLine();
if(!resp.startsWith("354")) throw new Exception("Error sending message: " + resp);
// send message header
popout.println(header);
// send message
message = message + "\n\n___________________________________\n";
message = message + "This message was sent using PopWeb.\nhttp://popweb.dyndns.org";
popout.println(message);
fw.write(header);
fw.write("\n\n");
fw.write(message + "\n\n");
fw.close();
popout.flush();
// end message with CRLF.CRLF
popout.println("\n.");
popout.flush();
resp = popin.readLine();
if(!resp.startsWith("2")) throw new Exception("Error sending message: " + resp);
}
/**
* doSTAT()
*
* - Retrieves the number of messages on the clients' POP3 server
* and returns an integer parsed from the response
*
*/
private int doSTAT() throws Exception {
// stat
popout.println("STAT\r");
popout.flush();
// get response
String statinfo = popin.readLine().substring(4);
int intend = statinfo.indexOf(" ");
statinfo = statinfo.substring(0,intend);
// return parsed integer
return Integer.parseInt(statinfo);
}
/**
* doUIDL()
*
* - Retrieves the unique identifiers for all messages
* on the clients' POP3 server
*
* - note: This method is not currently used
*
*/
private String [] doUIDL() throws Exception {
Vector v = new Vector();
popout.println("UIDL\r");
popout.flush();
String resp = popin.readLine();
if(!resp.startsWith("+OK")) throw new Exception("Error getting UIDL information: " + resp);
String line = null;
while(!(line = popin.readLine()).equals(".")) {
v.addElement(line);
}
String uidlinfo [] = new String[v.size()];
for(int i=0;i" to end of line for html formatting
* - Optionally returns all headers
* - Optionally cleans message for use in reply or forward
*
*/
private String doRETR(int msgnum, boolean allHeaders, boolean addBR, boolean isReply) throws Exception {
// retr (get the message)
popout.println("RETR " + msgnum + "\r");
popout.flush();
String resp = popin.readLine();
if(!resp.startsWith("+OK")) throw new Exception("Error retrieving message " + msgnum + ": " + resp);
// constants define sections of the email we can be in
final int MAIN_HDR = 0; // message header
final int MIME_HDR = 1; // mime info header
final int MIME_BDY = 2; // message/mime body
int inSection = MAIN_HDR; // what section of msg we are in
boolean attachExists = false; // does an attachment exist? (also embedded html)
boolean writeAttach = false; // tells us whether to write body to file
String message = ""; // message to be returned
String line = null; // current line
String boundaryCode = null; // attachment delim
String attachFName = null; // attach file name
String attachCT = null; // attach content-type
String attachList = ""; // html-ized attachment listing
String tmp; // used for string operations
String brstr = addBR ? " \n":"\n"; // string to add to end of line
FileWriter fw = null; // used to write attachment to disk
// ------------------------------------------------------
// Loop until we reach a CRLF.CRLF
// ------------------------------------------------------
while(!(line = popin.readLine()).equals(".")) {
// ------------------------------------------------------
// Determine which part of the message we are reading
//
// An email message is in the following BNF format:
//
// message := HEADER + body
// body := TEXT | mimes
// mimes := mime | mime + mimes
// mime := MIMEHDR + MIMEBDY | NOTHING
//
// Each mime is seperated by a boundary code.
//
// ------------------------------------------------------
switch(inSection) {
case MAIN_HDR:
// remove any html tag delims
line = stripHTML(line);
// get boundary code from headers (if it exists)
tmp = getHeaderValue("boundary=",line,"\n");
if(tmp != null) {
boundaryCode = stripQuotes(tmp);
// remove the prepending -'s
boundaryCode = boundaryCode.substring(boundaryCode.lastIndexOf("-")+1);
attachExists = true;
}
// get the message headers
if(allHeaders) {
message = message + line + brstr;
} else {
if(line.startsWith("To:") || line.startsWith("From:") ||
line.startsWith("Subject:") || line.startsWith("Date:") ||
line.toLowerCase().startsWith("cc:")) {
message = message + line + brstr;
}
}
// if we come to an empty line,
// change section state
if(line.equals("")) {
if(attachExists) inSection = MIME_HDR;
else inSection = MIME_BDY;
if(!allHeaders) message = message + brstr;
}
break;
case MIME_HDR:
// attempt to get the content-type for this
// section
tmp = getHeaderValue("Content-Type: ",line,";");
if(tmp != null) {
attachCT = tmp;
}
// attempt to get the name of the
// attachment
tmp = getHeaderValue("name=",line,"\n");
if(tmp != null) {
attachFName = stripQuotes(tmp);
}
// if we come to an empty line,
// change section state
if(line.equals("")) {
inSection = MIME_BDY;
if(attachCT != null && !attachCT.equalsIgnoreCase("text/plain")) {
if(attachFName == null) attachFName = "embedded html";
// add this attachment to the attachment list
attachList = attachList +
"PopWeb Attachment: " + attachFName + "" + brstr;
writeAttach = true;
}
}
break;
case MIME_BDY:
// look for boundary code
if(attachExists && line.indexOf(boundaryCode) != -1) {
inSection = MIME_HDR;
if(fw != null) {
// close the filewriter and re-init attach vars
// in case we have another attachment
fw.close();
fw = null;
writeAttach = false;
attachCT = null;
attachFName = null;
}
break;
}
// either write the attachment to a file (if it is
// an attachment) or add it to the message variable
// for display to client
if(writeAttach && !isReply) {
// we write the attachment in raw format to file
if(fw == null) {
// init filewriter
fw = new FileWriter(HTML_DIR + "attach/" + boundaryCode + attachFName);
}
fw.write(line + "\n");
} else if(!writeAttach) {
// add the line to message for viewing
line = stripHTML(line);
if(!isReply) line = highlightURLS(line);
message = message + line + brstr;
}
break;
default:
}
}
// if we are replying, don't include the attachment list
if(isReply) return message;
else return message + brstr + attachList;
}
/**
* getFromDateSubject()
*
* - Iterates through messages on clients' POP3 server and
* retrieves From, Date, and Subject headers for display
* in the message index
*
*/
private String [] getFromDateSubject(int start, int end) throws Exception {
Vector datev = new Vector();
Vector fromv = new Vector();
Vector subjv = new Vector();
for(int i=start; i<=end; i++) {
// show the TOP of the message
popout.println("TOP " + i + " 0\r");
popout.flush();
String resp = popin.readLine();
if(!resp.startsWith("+OK")) continue;
String line;
int found = 0;
// loop through and pick out headers
while(!(line = popin.readLine()).equals(".")) {
line = stripHTML(line);
if(line.startsWith("From:") && found < 3) {
line = stripQuotes(line);
int ltidx = line.indexOf("<");
ltidx = ltidx < 0 ? line.length():ltidx;
line = line.substring(6,ltidx);
fromv.addElement(line);
found++;
continue;
} else if(line.startsWith("Subject:") && found < 3) {
if(line.length() == 9) subjv.addElement("no subject");
else subjv.addElement(line.substring(9));
found++;
continue;
} else if(line.startsWith("Date:") && found < 3) {
int dashidx = line.indexOf("-");
dashidx = dashidx < 0 ? line.length():dashidx;
int plusidx = line.indexOf("+");
plusidx = plusidx < 0 ? line.length():plusidx;
String datestr = line.substring(6, plusidx > dashidx ? dashidx: plusidx);
datev.addElement(datestr);
found++;
continue;
}
}
// subject line has no value, default "no subject" so
// client has something to click on to get the message
if(found == 2) subjv.addElement("no subject");
}
// make an array of strings to return
String fromdatesubject [] = new String[fromv.size()*3];
// convert the Vector to array of strings
for(int i=0; inext msg | \n";
else return "\n";
}
private String showprevbutton(int current, int total) {
if(current == 1) return "\n";
else return "\n";
}
private String showindexbutton() {
return "\n";
}
private String showwritebutton() {
return "\n";
}
private String showreplybutton(int current, boolean inmsg) {
if(!inmsg) return "\n";
else return "\n";
}
private String showforwardbutton(int current, boolean inmsg) {
if(!inmsg) return "\n";
else return "\n";
}
private String showdeletebutton(int current, boolean inmsg) {
if(!inmsg) return "\n";
else return "\n";
}
private String showdeleteallbutton() {
return "\n";
}
private String showlogoutbutton() {
return "\n";
}
private String showviewbutton(int current) {
return "\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n";
}
private String shownextindexpagebutton() {
// not implemented (yet)
return "";
}
/**
* showtoolbar()
*
* - Creates a toolbar out of the above methods return values
* - User can navigate PopWeb easily using this toolbar
* that always looks the same
*
*/
private String showtoolbar(int current, int total, boolean inmsg) {
return "
\n" +
"
" + showindexbutton() +
"\n
" + showprevbutton(current,total) +
"\n
" + shownextbutton(current,total) +
"\n
" + showwritebutton() +
"\n
" + showreplybutton(current, inmsg) +
"\n
" + showforwardbutton(current, inmsg) +
"\n
" + showdeletebutton(current, inmsg) +
"\n
" + showdeleteallbutton() +
"\n
" + showlogoutbutton() +
"\n
\n";
}
/**
* stripQuotes()
*
* - Removes quotation marks from a String
*
*/
private String stripQuotes(String str) {
String result = "";
StringTokenizer st = new StringTokenizer(str, "\"");
while(st.hasMoreElements()) {
result = result + (String)st.nextElement();
}
return result;
}
/**
* stripHTML()
*
* - Converts ">" to ">" and "<" to "<" in a String
*
*/
private String stripHTML(String str) {
String result = "";
char buff [] = new char[str.length()];
str.getChars(0,str.length(),buff,0);
for(int i=0; i': result = result + ">"; break;
case '<': result = result + "<"; break;
case '&': result = result + "&"; break;
default: result = result + new String(new char[] {buff[i]});
}
}
return result;
}
/**
* highlightURLS()
*
* - Searches a String for the http:// prefix and
* creates a hyperlink out of it
*
* - problem: Space (" ") is the only delimiter
*
*/
private String highlightURLS(String str) {
int urlidx = str.indexOf("http://");
if(urlidx < 0) urlidx = str.indexOf("ftp://");
if(urlidx < 0) return str;
String url = str.substring(urlidx);
int endurlidx = url.indexOf(" ");
if(endurlidx == -1) endurlidx = url.length();
url = url.substring(0, endurlidx);
return str.substring(0,urlidx) + "" + url + "" +
highlightURLS(str.substring(url.length() + urlidx));
}
/**
* parseAddresses()
*
* - Parses single email addresses from a String
* of email addresses seperated by commas (",")
*
*/
private String [] parseAddresses(String str) throws Exception {
Vector v = new Vector();
StringTokenizer st = new StringTokenizer(str, ",");
while(st.hasMoreElements()) {
String anaddr = (String)st.nextElement();
if(anaddr.length() < 3 ||
anaddr.indexOf("@") < 1 ||
anaddr.indexOf("@") == anaddr.length()-1)
throw new Exception("Invalid email address: " + anaddr);
v.addElement(anaddr);
}
String [] result = new String[v.size()];
for(int i=0;i linelength) {
} else {
result += line + "\n";
}
}
return result;
}
*/
/**
* printError()
*
* - Displays an error message to the client
*
*/
private void printError(String title, String str) {
clientout.println("PopWeb [" + title + "]");
showFile("pwbody1.thtml",false);
clientout.println("PopWeb " + title);
showFile("pwbody2.thtml",false);
clientout.println(str);
showFile("pwbody3.thtml",false);
}
}