/************************************************************************* * * * 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"; clientout.println(output); clientout.flush(); // get the message headers (from, subject, date) String [] headers = getFromDateSubject(1,total); for(int i=0;i\n"; clientout.println(output); clientout.flush(); } output = "
\n" + "  \n" + " \n" + " \n" + "  From\n" + " \n" + " \n" + "  Date\n" + " \n" + " \n" + "  Subject\n" + "
\n" + " \n" + " " + showviewbutton(thismsg) + "\n" + "\n" + " \n" + "  " + headers[i] + "\n" + "\n" + " \n" + "  " + headers[i+1] + "\n" + "\n" + " \n" + "  "+ headers[i+2] + "\n" + " \n" + "
\n" + "
\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" + " \n" + " \n" + " \n" + " \n" + " \n" + "
\n" + " \n" + "  Message " + msgnum + " of " + total + "\n" + " \n" + "  "+ "show " + (showallheadersstr.equals("less") ? "all":"less") + " headers \n" + " \n" + "
\n" + "
\n"; clientout.println(output); // display the specified message output = "" + "
" + "
\n" +
			 doRETR(msgnum,showallheaders,false,false) +
			 "
" + "
"; clientout.println(output); output = "\n" + "\n" + "
\n" + " \n" + " \n" + "
\n" + " \n" + "  " + "\n" + " \n" + "
\n" + "
\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" + "\n" + "
\n" + " \n" + " \n" + "
\n" + " \n" + "  PopWeb [message from " + username + "@" + popserver + "]\n" + " \n" + "
\n" + "
\n"; clientout.println(output); // display the message composition area output = "" + "
" + " " + "
" + "
\n" + " From:

\n" + " To:

\n" + "
\n" + " Cc:

\n" + "
\n" + " Subject:
\n" + "
\n" + " Message:

\n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
\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" + "\n" + "\n" + "" + "yes
\n
\n" + "\n" + "\n" + "\n" + "\n" + "" + "no
\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" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "next msg |
\n"; } private String showprevbutton(int current, int total) { if(current == 1) return "
prev msg |
\n"; else return "
\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "prev msg |
\n"; } private String showindexbutton() { return "
\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "index |
\n"; } private String showwritebutton() { return "
\n" + "\n" + "\n" + "\n" + "\n" + "write msg |
\n"; } private String showreplybutton(int current, boolean inmsg) { if(!inmsg) return "
reply |
\n"; else return "
\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "reply |
\n"; } private String showforwardbutton(int current, boolean inmsg) { if(!inmsg) return "
forward |
\n"; else return "
\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "forward |
\n"; } private String showdeletebutton(int current, boolean inmsg) { if(!inmsg) return "
delete |
\n"; else return "
\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "delete |
\n"; } private String showdeleteallbutton() { return "
\n" + "\n" + "\n" + "\n" + "\n" + "delete all |
\n"; } private String showlogoutbutton() { return "
\n" + "\n" + "\n" + "\n" + "\n" + "logout
\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); } }