|
What this is
Other links
The source code/* CVS ID: $Id: WebMailSession.java,v 1.1.1.1 2002/10/02 18:42:53 wastl Exp $ */ package net.wastl.webmail.server; import java.net.*; import java.io.*; import java.util.*; import java.text.*; import javax.mail.*; import javax.mail.event.*; import javax.mail.internet.*; import net.wastl.webmail.misc.*; import net.wastl.webmail.xml.*; import net.wastl.webmail.ui.html.Fancyfier; import net.wastl.webmail.server.http.HTTPRequestHeader; import net.wastl.webmail.exceptions.*; // Modified by exce, start import org.bulbul.webmail.util.TranscodeUtil; // Modified by exce, end //import org.w3c.tidy.Tidy; import org.w3c.dom.*; // HTML parser: import org.xml.sax.InputSource; //import org.cyberneko.html.parsers.DOMParser; /* * WebMailSession.java * * Created: Thu Feb 4 12:59:30 1999 * * Copyright (C) 1999-2001 Sebastian Schaffert * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /** * A user session for WebMail. * Contains the state of the actual user (loads it from disk). * Has a unique session-ID. * * * @author Sebastian Schaffert * @version $Revision: 1.1.1.1 $ */ /* 9/24/2000 devink - updated for challenge/response auth */ public class WebMailSession implements HTTPSession { /** When has the session been last accessed? */ private long last_access; /** The session-ID for this session */ private String session_code; /** Parent WebMailServer */ private WebMailServer parent; /** State of the current users configuration */ private XMLUserData user; private XMLUserModel model; /** Connections to Mailboxes */ private Hashtable connections; /** Connections to hosts */ private Hashtable stores; /** javax.mail Mailsession */ private Session mailsession; private InetAddress remote; /* Files attached to messages will be stored here. We will have to take care of possible memory problems! */ private Hashtable mime_parts_decoded; private boolean sent; private String remote_agent; private String remote_accepts; private int attachments_size=0; private String last_login; /** Save the login password. It will be used for the second try password if * opening a folder fails. */ private String login_password; private Object sess=null; private Hashtable folders; protected Vector need_expunge_folders; protected boolean is_logged_out=false; public WebMailSession(WebMailServer parent,Object parm,HTTPRequestHeader h) throws UserDataException, InvalidPasswordException, WebMailException { try { Class srvltreq=Class.forName("javax.servlet.http.HttpServletRequest"); if(srvltreq.isInstance(parm)) { javax.servlet.http.HttpServletRequest req=(javax.servlet.http.HttpServletRequest)parm; this.sess=req.getSession(false); session_code=((javax.servlet.http.HttpSession)sess).getId(); try { remote=InetAddress.getByName(req.getRemoteHost()); } catch(UnknownHostException e) { try { remote=InetAddress.getByName(req.getRemoteAddr()); } catch(Exception ex) { try { remote=InetAddress.getByName("localhost"); } catch(Exception ex2) {} } } } else { throw new Exception("Servlet class found but not running as servlet"); } } catch(Throwable t) { this.remote=(InetAddress)parm; session_code=Helper.calcSessionCode(remote,h); } doInit(parent,h); } /** * This method does the actual initialisation * * devink 7/15/2000 - added TwoPassAuthenticationException * - updated call to getUserData(), to use my new one. * devink 9/24/2000 - reverted back to old getUserData call */ protected void doInit(WebMailServer parent, HTTPRequestHeader h) throws UserDataException, InvalidPasswordException, WebMailException { setLastAccess(); this.parent=parent; remote_agent=h.getHeader("User-Agent").replace('\n',' '); remote_accepts=h.getHeader("Accept").replace('\n',' '); parent.getStorage().log(Storage.LOG_INFO,"WebMail: New Session ("+session_code+")"); user=WebMailServer.getStorage().getUserData(h.getContent("login"),h.getContent("vdom"),h.getContent("password"),true); last_login=user.getLastLogin(); user.login(); login_password=h.getContent("password"); model=parent.getStorage().createXMLUserModel(user); connections=new Hashtable(); stores=new Hashtable(); folders=new Hashtable(); mailsession=Session.getDefaultInstance(System.getProperties(),null); /* If the user logs in for the first time we want all folders subscribed */ if(user.getLoginCount().equals("1")) { Enumeration enum=user.mailHosts(); while(enum.hasMoreElements()) { String id=(String)enum.nextElement(); if(user.getMailHost(id).getName().equals("Default")) { try { setSubscribedAll(id,true); } catch(MessagingException ex) { ex.printStackTrace(); } break; } } } setEnv(); } public XMLUserModel getUserModel() { return model; } public Document getModel() { return model.getRoot(); } /** * Calculate session-ID for a session. * * @param a Adress of the remote host * @param h Requestheader of the remote user agent * @returns Session-ID */ public String calcCode(InetAddress a, HTTPRequestHeader h) { if(sess==null) { return Helper.calcSessionCode(a,h); } else { try { Class srvltreq=Class.forName("javax.servlet.http.HttpSession"); if(srvltreq.isInstance(sess)) { return ((javax.servlet.http.HttpSession)sess).getId(); } else { return "error"; } } catch(Throwable t) { return "error"; } } } /** * Login to this session. * Establishes connections to a userīs Mailhosts * * @param h RequestHeader with content from Login-POST operation. * @deprecated Use login() instead, no need for parameters and exception handling */ public void login(HTTPRequestHeader h) throws InvalidPasswordException { //user.login(h.getContent("password")); login(); } /** * Login this session. * * Updates access time, sets initial environment and connects all configured mailboxes. */ public void login() { setLastAccess(); setEnv(); connectAll(); } /** * Return a locale-specific string resource */ public String getStringResource(String key) { return parent.getStorage().getStringResource(key,user.getPreferredLocale()); } /** * Create a Message List. * Fetches a list of headers in folder foldername for part list_part. * The messagelist will be stored in the "MESSAGES" environment. * * @param foldername folder for which a message list should be built * @param list_part part of list to display (1 = last xx messages, 2 = total-2*xx - total-xx messages) */ public void createMessageList(String folderhash,int list_part) throws NoSuchFolderException { long time_start=System.currentTimeMillis(); TimeZone tz=TimeZone.getDefault(); DateFormat df=DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.SHORT, user.getPreferredLocale()); df.setTimeZone(tz); try { Folder folder=getFolder(folderhash); Element xml_folder=model.getFolder(folderhash); Element xml_current=model.setCurrentFolder(folderhash); Element xml_messagelist=model.getMessageList(xml_folder); if(folder == null) { throw new NoSuchFolderException(folderhash); } long fetch_start=System.currentTimeMillis(); if(!folder.isOpen()) { folder.open(Folder.READ_ONLY); } else { folder.close(false); folder.open(Folder.READ_ONLY); } /* Calculate first and last message to show */ int total_messages=folder.getMessageCount(); int new_messages=folder.getNewMessageCount(); int show_msgs=user.getMaxShowMessages(); xml_messagelist.setAttribute("total",total_messages+""); xml_messagelist.setAttribute("new",new_messages+""); // System.err.println("Total: "+total_messages); /* Handle small messagelists correctly */ if(total_messages < show_msgs) { show_msgs = total_messages; } /* Don't accept list-parts smaller than 1 */ if(list_part < 1) { list_part=1; } for(int k=0;k<list_part;k++) { total_messages-=show_msgs; } /* Handle beginning of message list */ if(total_messages<0) { total_messages=0; } int first=total_messages+1; int last=total_messages+show_msgs; /* Set environment variable */ setEnv(); xml_current.setAttribute("first_msg",first+""); xml_current.setAttribute("last_msg",last+""); xml_current.setAttribute("list_part",list_part+""); /* Fetch headers */ FetchProfile fp=new FetchProfile(); fp.add(FetchProfile.Item.ENVELOPE); fp.add(FetchProfile.Item.FLAGS); fp.add(FetchProfile.Item.CONTENT_INFO); // System.err.println("Last: "+last+", first: "+first); Message[] msgs=folder.getMessages(first,last); //System.err.println(msgs.length + " messages fetching..."); folder.fetch(msgs,fp); long fetch_stop=System.currentTimeMillis(); Hashtable header=new Hashtable(15); Flags.Flag[] sf; String from,to,cc,bcc,replyto,subject; String messageid; for(int i=msgs.length-1; i>=0; i--) { // if(((MimeMessage)msgs[i]).getMessageID() == null) { // folder.close(false); // folder.open(Folder.READ_WRITE); // ((MimeMessage)msgs[i]).setHeader("Message-ID","<"+user.getLogin()+"."+System.currentTimeMillis()+".jwebmail@"+user.getDomain()+">"); // ((MimeMessage)msgs[i]).saveChanges(); // folder.close(false); // folder.open(Folder.READ_ONLY); // } try { StringTokenizer tok=new StringTokenizer(((MimeMessage)msgs[i]).getMessageID(),"<>"); messageid=tok.nextToken(); } catch(NullPointerException ex) { // For mail servers that don't generate a Message-ID (Outlook et al) messageid=user.getLogin()+"."+i+".jwebmail@"+user.getDomain(); } XMLMessage xml_message=model.getMessage(xml_folder,msgs[i].getMessageNumber()+"", messageid); /* Addresses */ from="";replyto="";to="";cc="";bcc=""; try { from=MimeUtility.decodeText(Helper.joinAddress(msgs[i].getFrom())); replyto=MimeUtility.decodeText(Helper.joinAddress(msgs[i].getReplyTo())); to=MimeUtility.decodeText(Helper.joinAddress(msgs[i].getRecipients(Message.RecipientType.TO))); cc=MimeUtility.decodeText(Helper.joinAddress(msgs[i].getRecipients(Message.RecipientType.CC))); bcc=MimeUtility.decodeText(Helper.joinAddress(msgs[i].getRecipients(Message.RecipientType.BCC))); } catch(UnsupportedEncodingException e) { parent.getStorage().log(Storage.LOG_WARN,"Unsupported Encoding: "+e.getMessage()); } if(from=="") from=getStringResource("unknown sender"); if(to == "") to = getStringResource("unknown recipient"); /* Flags */ sf = msgs[i].getFlags().getSystemFlags(); String basepath=parent.getBasePath(); for(int j=0;j<sf.length;j++) { if(sf[j]==Flags.Flag.RECENT) xml_message.setAttribute("recent","true"); if(sf[j]==Flags.Flag.SEEN) xml_message.setAttribute("seen","true"); if(sf[j]==Flags.Flag.DELETED) xml_message.setAttribute("deleted","true"); if(sf[j]==Flags.Flag.ANSWERED) xml_message.setAttribute("answered","true"); if(sf[j]==Flags.Flag.DRAFT) xml_message.setAttribute("draft","true"); if(sf[j]==Flags.Flag.FLAGGED) xml_message.setAttribute("flagged","true"); if(sf[j]==Flags.Flag.USER) xml_message.setAttribute("user","true"); } if(msgs[i] instanceof MimeMessage && ((MimeMessage) msgs[i]).getContentType().toUpperCase().startsWith("MULTIPART/")) { xml_message.setAttribute("attachment","true"); } if(msgs[i] instanceof MimeMessage) { int size=((MimeMessage) msgs[i]).getSize(); size/=1024; xml_message.setAttribute("size",(size>0?size+"":"<1")+" kB"); } /* Subject */ subject=""; if(msgs[i].getSubject() != null) { try { subject=MimeUtility.decodeText(msgs[i].getSubject()); } catch(UnsupportedEncodingException ex) { parent.getStorage().log(Storage.LOG_WARN,"Unsupported Encoding: "+ex.getMessage()); } } if(subject == null || subject.equals("")) { subject=getStringResource("no subject"); } /* Set all of what we found into the DOM */ xml_message.setHeader("FROM",from); try { // Modified by exce, start. // hmm, why decode subject twice? Though it doesn't matter.. // Modified by exce, end. xml_message.setHeader("SUBJECT",MimeUtility.decodeText(subject)); } catch(UnsupportedEncodingException e) { parent.getStorage().log(Storage.LOG_WARN, "Unsupported Encoding: "+e.getMessage()); } xml_message.setHeader("TO",to); xml_message.setHeader("CC",cc); xml_message.setHeader("BCC",bcc); xml_message.setHeader("REPLY-TO",replyto); /* Date */ Date d=msgs[i].getSentDate(); String ds=""; if(d!=null) { ds=df.format(d); } xml_message.setHeader("DATE",ds); } long time_stop=System.currentTimeMillis(); // try { // XMLCommon.writeXML(model.getRoot(),new FileOutputStream("/tmp/wmdebug"),""); // } catch(IOException ex) {} parent.getStorage().log(Storage.LOG_DEBUG,"Construction of message list took "+(time_stop-time_start)+" ms. Time for IMAP transfer was "+(fetch_stop-fetch_start)+" ms."); folder.close(false); } catch(NullPointerException e) { e.printStackTrace(); throw new NoSuchFolderException(folderhash); } catch(MessagingException ex) { ex.printStackTrace(); } } /** * This indicates standard getMessage behaviour: Fetch the message from the IMAP server and store it in the * current UserModel. * @see getMessage(String,int,int) */ public static final int GETMESSAGE_MODE_STANDARD=0; /** * Set this mode in getMessage to indicate that the message is requested to generate a reply message and * should therefore be set as the current "work" message. * @see getMessage(String,int,int) */ public static final int GETMESSAGE_MODE_REPLY=1; /** * Set this mode in getMessage to indicate that the message is to be forwarded to someone else and a "work" * message should be generated. * @see getMessage(String,int,int) */ public static final int GETMESSAGE_MODE_FORWARD=2; /** * This is a wrapper to call getMessage with standard mode. * @see getMessage(String,int,int) */ public void getMessage(String folderhash, int msgnum) throws NoSuchFolderException { getMessage(folderhash,msgnum,GETMESSAGE_MODE_STANDARD); } /** * Fetch a message from a folder. * Will put the messages parameters in the sessions environment * * @param foldername Name of the folder were the message should be fetched from * @param msgnum Number of the message to fetch * @param mode there are three different modes: standard, reply and forward. reply and forward will enter the message * into the current work element of the user and set some additional flags on the message if the user * has enabled this option. * @see net.wastl.webmail.server.WebMailSession.GETMESSAGE_MODE_STANDARD * @see net.wastl.webmail.server.WebMailSession.GETMESSAGE_MODE_REPLY * @see net.wastl.webmail.server.WebMailSession.GETMESSAGE_MODE_FORWARD */ public void getMessage(String folderhash, int msgnum, int mode) throws NoSuchFolderException { // security reasons: // attachments=null; try { TimeZone tz=TimeZone.getDefault(); DateFormat df=DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.SHORT, user.getPreferredLocale()); df.setTimeZone(tz); Folder folder=getFolder(folderhash); Element xml_folder=model.getFolder(folderhash); if(folder==null) { throw new NoSuchFolderException("No such folder: "+folderhash); } if(folder.isOpen() && folder.getMode()==Folder.READ_WRITE) { folder.close(false); folder.open(Folder.READ_ONLY); } else if(!folder.isOpen()) { folder.open(Folder.READ_ONLY); } MimeMessage m=(MimeMessage)folder.getMessage(msgnum); String messageid; try { StringTokenizer tok=new StringTokenizer(m.getMessageID(),"<>"); messageid=tok.nextToken(); } catch(NullPointerException ex) { // For mail servers that don't generate a Message-ID (Outlook et al) messageid=user.getLogin()+"."+msgnum+".jwebmail@"+user.getDomain(); } Element xml_current=model.setCurrentMessage(messageid); XMLMessage xml_message=model.getMessage(xml_folder,m.getMessageNumber()+"", messageid); /* Check whether we already cached this message (not only headers but complete)*/ boolean cached=xml_message.messageCompletelyCached(); /* If we cached the message, we don't need to fetch it again */ if(!cached) { //Element xml_header=model.getHeader(xml_message); try { String from=MimeUtility.decodeText(Helper.joinAddress(m.getFrom())); String replyto=MimeUtility.decodeText(Helper.joinAddress(m.getReplyTo())); String to=MimeUtility.decodeText(Helper.joinAddress(m.getRecipients(Message.RecipientType.TO))); String cc=MimeUtility.decodeText(Helper.joinAddress(m.getRecipients(Message.RecipientType.CC))); String bcc=MimeUtility.decodeText(Helper.joinAddress(m.getRecipients(Message.RecipientType.BCC))); Date date_orig=m.getSentDate(); String date=getStringResource("no date"); if(date_orig!=null) { date=df.format(date_orig); } String subject=""; if(m.getSubject() != null) { subject=MimeUtility.decodeText(m.getSubject()); } if(subject == null || subject.equals("")) { subject=getStringResource("no subject"); } try { Flags.Flag[] sf = m.getFlags().getSystemFlags(); for(int j=0;j<sf.length;j++) { if(sf[j]==Flags.Flag.RECENT) xml_message.setAttribute("recent","true"); if(sf[j]==Flags.Flag.SEEN) xml_message.setAttribute("seen","true"); if(sf[j]==Flags.Flag.DELETED) xml_message.setAttribute("deleted","true"); if(sf[j]==Flags.Flag.ANSWERED) xml_message.setAttribute("answered","true"); if(sf[j]==Flags.Flag.DRAFT) xml_message.setAttribute("draft","true"); if(sf[j]==Flags.Flag.FLAGGED) xml_message.setAttribute("flagged","true"); if(sf[j]==Flags.Flag.USER) xml_message.setAttribute("user","true"); } } catch(NullPointerException ex) {} if(m.getContentType().toUpperCase().startsWith("MULTIPART/")) { xml_message.setAttribute("attachment","true"); } int size=m.getSize(); size/=1024; xml_message.setAttribute("size",(size>0?size+"":"<1")+" kB"); /* Set all of what we found into the DOM */ xml_message.setHeader("FROM",from); xml_message.setHeader("SUBJECT",Fancyfier.apply(subject)); xml_message.setHeader("TO",to); xml_message.setHeader("CC",cc); xml_message.setHeader("BCC",bcc); xml_message.setHeader("REPLY-TO",replyto); xml_message.setHeader("DATE",date); /* Decode MIME contents recursively */ xml_message.removeAllParts(); parseMIMEContent(m,xml_message,messageid); } catch(UnsupportedEncodingException e) { parent.getStorage().log(Storage.LOG_WARN,"Unsupported Encoding in parseMIMEContent: "+e.getMessage()); System.err.println("Unsupported Encoding in parseMIMEContent: "+e.getMessage()); } } /* Set seen flag (Maybe make that threaded to improve performance) */ if(user.wantsSetFlags()) { if(folder.isOpen() && folder.getMode()==Folder.READ_ONLY) { folder.close(false); folder.open(Folder.READ_WRITE); } else if(!folder.isOpen()) { folder.open(Folder.READ_WRITE); } folder.setFlags(msgnum,msgnum,new Flags(Flags.Flag.SEEN),true); folder.setFlags(msgnum,msgnum,new Flags(Flags.Flag.RECENT), false); if((mode & GETMESSAGE_MODE_REPLY) == GETMESSAGE_MODE_REPLY) { folder.setFlags(msgnum,msgnum,new Flags(Flags.Flag.ANSWERED),true); } } folder.close(false); /* In this part we determine whether the message was requested so that it may be used for further editing (replying or forwarding). In this case we set the current "work" message to the message we just fetched and then modifiy it a little (quote, add a "Re" to the subject, etc). */ XMLMessage work=null; if((mode & GETMESSAGE_MODE_REPLY) == GETMESSAGE_MODE_REPLY || (mode & GETMESSAGE_MODE_FORWARD) == GETMESSAGE_MODE_FORWARD) { //System.err.println("Setting work message!"); work=model.setWorkMessage(xml_message); String newmsgid=WebMailServer.generateMessageID(user.getUserName()); if(work != null && (mode & GETMESSAGE_MODE_REPLY) == GETMESSAGE_MODE_REPLY) { String from=work.getHeader("FROM"); work.setHeader("FROM",user.getEmail()); work.setHeader("TO",from); work.prepareReply(getStringResource("reply subject prefix"), getStringResource("reply subject postfix"), getStringResource("reply message prefix"), getStringResource("reply message postfix")); } else if(work != null && (mode & GETMESSAGE_MODE_FORWARD) == GETMESSAGE_MODE_FORWARD) { String from=work.getHeader("FROM"); work.setHeader("FROM",user.getEmail()); work.setHeader("TO",""); work.setHeader("CC",""); work.prepareForward(getStringResource("forward subject prefix"), getStringResource("forward subject postfix"), getStringResource("forward message prefix"), getStringResource("forward message postfix")); /* Copy all references to MIME parts to the new message id */ Enumeration attids=getMimeParts(work.getAttribute("msgid")); while(attids.hasMoreElements()) { String key=(String)attids.nextElement(); StringTokenizer tok2=new StringTokenizer(key,"/"); tok2.nextToken(); String newkey=tok2.nextToken(); mime_parts_decoded.put(newmsgid+"/"+newkey,mime_parts_decoded.get(key)); } } /* Clear the msgnr and msgid fields at last */ work.setAttribute("msgnr","0"); work.setAttribute("msgid",newmsgid); prepareCompose(); } } catch(MessagingException ex) { ex.printStackTrace(); } } /** Use depth-first search to go through MIME-Parts recursively. @param p Part to begin with */ protected void parseMIMEContent(Part p, XMLMessagePart parent_part, String msgid) throws MessagingException { StringBuffer content=new StringBuffer(1000); XMLMessagePart xml_part; try { if(p.getContentType().toUpperCase().startsWith("TEXT/HTML")) { /* The part is a text in HTML format. We will try to use "Tidy" to create a well-formated XHTML DOM from it and then remove JavaScript and other "evil" stuff. For replying to such a message, it will be useful to just remove all of the tags and display only the text. */ xml_part=parent_part.createPart("html"); /* Here we create a DOM tree. */ //Tidy tidy=new Tidy(); //tidy.setUpperCaseTags(true); //Document htmldoc=tidy.parseDOM(p.getInputStream(),null); org.cyberneko.html.parsers.DOMParser parser = new org.cyberneko.html.parsers.DOMParser(); parser.parse(new InputSource(p.getInputStream())); Document htmldoc = parser.getDocument(); //XMLCommon.debugXML(htmldoc); /* Now let's look for all the malicious JavaScript and other <SCRIPT> tags, URLS containing the "javascript:" and tags containing "onMouseOver" and such stuff. */ // if(user.getBoolVar("filter javascript")) new JavaScriptCleaner(htmldoc); new JavaScriptCleaner(htmldoc); //XMLCommon.debugXML(htmldoc); /* HTML doesn't allow us to do such fancy stuff like different quote colors, perhaps this will be implemented in the future */ /* So we just add this HTML document to the message part, which will deal with removing headers and tags that we don't need */ xml_part.addContent(htmldoc); } else if(p.getContentType().toUpperCase().startsWith("TEXT") || p.getContentType().toUpperCase().startsWith("MESSAGE")) { /* The part is a standard message part in some incarnation of text (html or plain). We should decode it and take care of some extra issues like recognize quoted parts, filter JavaScript parts and replace smileys with smiley-icons if the user has set wantsFancy() */ xml_part=parent_part.createPart("text"); // TODO: System.err.println("text hit"); BufferedReader in; if(p instanceof MimeBodyPart) { int size=p.getSize(); MimeBodyPart mpb=(MimeBodyPart)p; InputStream is=mpb.getInputStream(); /* Workaround for Java or Javamail Bug */ is=new BufferedInputStream(is); ByteStore ba=ByteStore.getBinaryFromIS(is,size); in=new BufferedReader(new InputStreamReader(new ByteArrayInputStream(ba.getBytes()))); /* End of workaround */ size=is.available(); } else { in=new BufferedReader(new InputStreamReader(p.getInputStream())); } //System.err.println("Content-Type: "+p.getContentType()); String token=""; int quote_level=0, old_quotelevel=0; boolean javascript_mode=false; /* Read in the message part line by line */ while((token=in.readLine()) != null) { /* First decode all language and MIME dependant stuff */ // Default to ISO-8859-1 (Western Latin 1) String charset="ISO-8859-1"; // Check whether the part contained a charset in the content-type header StringTokenizer tok2=new StringTokenizer(p.getContentType(),";="); String blah=tok2.nextToken(); if(tok2.hasMoreTokens()) { blah=tok2.nextToken().trim(); if(blah.toLowerCase().equals("charset") && tok2.hasMoreTokens()) { charset=tok2.nextToken().trim(); } } try { token=new String(token.getBytes(),charset); } catch(UnsupportedEncodingException ex1) { parent.getStorage().log(Storage.LOG_INFO,"Java Engine does not support charset "+charset+". Trying to convert from MIME ..."); try { charset=MimeUtility.javaCharset(charset); token=new String(token.getBytes(),charset); } catch(UnsupportedEncodingException ex) { parent.getStorage().log(Storage.LOG_WARN,"Converted charset ("+charset+") does not work. Using default charset (ouput may contain errors)"); token=new String(token.getBytes()); } } /* Here we figure out which quote level this line has, simply by counting how many ">" are in front of the line, ignoring all whitespaces. */ int current_quotelevel=Helper.getQuoteLevel(token); /* When we are in a different quote level than the last line, we append all we got so far to the part with the old quotelevel and begin with a clean String buffer */ if(current_quotelevel != old_quotelevel) { xml_part.addContent(content.toString(),old_quotelevel); old_quotelevel = current_quotelevel; content=new StringBuffer(1000); } if(user.wantsBreakLines()) { Enumeration enum=Helper.breakLine(token,user.getMaxLineLength(),current_quotelevel); while(enum.hasMoreElements()) { String s=(String)enum.nextElement(); if(user.wantsShowFancy()) { content.append(Fancyfier.apply(s)).append("\n"); } else { content.append(s).append("\n"); } } } else { if(user.wantsShowFancy()) { content.append(Fancyfier.apply(token)).append("\n"); } else { content.append(token).append("\n"); } } } xml_part.addContent(content.toString(),old_quotelevel); // Modified by exce, start // Why the following code??? content=new StringBuffer(1000); // Modified by exce, end. } else if(p.getContentType().toUpperCase().startsWith("MULTIPART/ALTERNATIVE")) { /* This is a multipart/alternative part. That means that we should pick one of the formats and display it for this part. Our current precedence list is to choose HTML first and then to choose plain text. */ MimeMultipart m=(MimeMultipart)p.getContent(); String[] preferred={"TEXT/HTML","TEXT"}; boolean found=false; int alt=0; // Walk though our preferred list of encodings. If we have found a fitting part, // decode it and replace it for the parent (this is what we really want with an // alternative!) // Modified by exce, start /** findalt: while(!found && alt < preferred.length) { for(int i=0;i<m.getCount();i++) { Part p2=m.getBodyPart(i); if(p2.getContentType().toUpperCase().startsWith(preferred[alt])) { parseMIMEContent(p2,parent_part,msgid); found=true; break findalt; } } alt++; } **/ /** * When user try to reply a mail, there may be 3 conditions: * 1. only TEXT exists. * 2. both HTML and TEXT exist. * 3. only HTML exists. * * We have to choose which part should we quote, that is, we must * decide the prority of parts to quote. Since quoting HTML is not * easy and precise (consider a html: <body> |
. Even we try to get text node under
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.