WebMailSession.java
上传用户:huihesys
上传日期:2007-01-04
资源大小:3877k
文件大小:52k
源码类别:

WEB邮件程序

开发平台:

C/C++

  1. /* CVS ID: $Id: WebMailSession.java,v 1.5 2000/04/18 13:12:47 wastl Exp $ */
  2. package net.wastl.webmail.server;
  3. import java.net.*;
  4. import java.io.*;
  5. import java.util.*;
  6. import java.text.*;
  7. import javax.mail.*;
  8. import javax.mail.event.*;
  9. import javax.mail.internet.*;
  10. import net.wastl.webmail.misc.*;
  11. import net.wastl.webmail.xml.*;
  12. import net.wastl.webmail.ui.html.JavaScriptFilter;
  13. import net.wastl.webmail.ui.html.HTMLifier;
  14. import net.wastl.webmail.ui.html.Fancyfier;
  15. import net.wastl.webmail.server.http.HTTPRequestHeader;
  16. import org.w3c.dom.*;
  17. /*
  18.  * WebMailSession.java
  19.  *
  20.  * Created: Thu Feb  4 12:59:30 1999
  21.  *
  22.  * Copyright (C) 1999-2000 Sebastian Schaffert
  23.  * 
  24.  * This program is free software; you can redistribute it and/or
  25.  * modify it under the terms of the GNU General Public License
  26.  * as published by the Free Software Foundation; either version 2
  27.  * of the License, or (at your option) any later version.
  28.  * 
  29.  * This program is distributed in the hope that it will be useful,
  30.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  31.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  32.  * GNU General Public License for more details.
  33.  * 
  34.  * You should have received a copy of the GNU General Public License
  35.  * along with this program; if not, write to the Free Software
  36.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  37.  */
  38. /**
  39.  * A user session for WebMail.
  40.  * Contains the state of the actual user (loads it from disk).
  41.  * Has a unique session-ID.
  42.  *
  43.  *
  44.  * @author Sebastian Schaffert
  45.  * @version $Revision: 1.5 $
  46.  */
  47. public class WebMailSession implements HTTPSession {
  48.     /** When has the session been last accessed? */
  49.     private long last_access;
  50.     /** The session-ID for this session */
  51.     private String session_code;
  52.     /** Parent WebMailServer */
  53.     private WebMailServer parent;
  54.     /** State of the current users configuration */
  55.     private XMLUserData user;
  56.     private XMLUserModel model;
  57.     /** Connections to Mailboxes */
  58.     private Hashtable connections;
  59.     
  60.     /** Connections to hosts */
  61.     private Hashtable stores;
  62.     /** javax.mail Mailsession */
  63.     private Session mailsession;
  64.     private InetAddress remote;
  65.     /* Files attached to messages will be stored here. We will have to take care of
  66.        possible memory problems! */
  67.     private Hashtable mime_parts_decoded;
  68.     private boolean sent;
  69.     private String remote_agent;
  70.     private String remote_accepts;
  71.     private int attachments_size=0;
  72.     private String last_login;
  73.     /** Save the login password. It will be used for the second try password if
  74.      * opening a folder fails.
  75.      */
  76.     private String login_password;
  77.     private Object sess=null;
  78.     private Hashtable folders;
  79.     protected Vector need_expunge_folders;
  80.     protected boolean is_logged_out=false;
  81.     
  82.     public WebMailSession(WebMailServer parent, Object parm, HTTPRequestHeader h) throws InvalidPasswordException {
  83. try {
  84.     Class srvltreq=Class.forName("javax.servlet.http.HttpServletRequest");
  85.     if(srvltreq.isInstance(parm)) {
  86. javax.servlet.http.HttpServletRequest req=(javax.servlet.http.HttpServletRequest)parm;
  87. this.sess=req.getSession(false);
  88. session_code=((javax.servlet.http.HttpSession)sess).getId();
  89. try {
  90.     remote=InetAddress.getByName(req.getRemoteHost());
  91. } catch(UnknownHostException e) {
  92.     try {
  93. remote=InetAddress.getByName(req.getRemoteAddr());
  94.     } catch(Exception ex) {
  95. try {
  96.     remote=InetAddress.getByName("localhost");
  97. } catch(Exception ex2) {}
  98.     }
  99. }
  100.     } else {
  101. throw new Exception("Servlet class found but not running as servlet");
  102.     }
  103. } catch(Throwable t) {
  104.     this.remote=(InetAddress)parm;
  105.     session_code=Helper.calcSessionCode(remote,h);
  106. }
  107. doInit(parent,h);
  108.     }
  109.     protected void doInit(WebMailServer parent, HTTPRequestHeader h) throws InvalidPasswordException {
  110. last_access=System.currentTimeMillis();
  111. this.parent=parent;
  112. remote_agent=h.getHeader("User-Agent").replace('n',' ');
  113. remote_accepts=h.getHeader("Accept").replace('n',' ');
  114. parent.getStorage().log(Storage.LOG_INFO,"WebMail: New Session ("+session_code+")");
  115. user=WebMailServer.getStorage().getUserData(h.getContent("login"),h.getContent("vdom"),h.getContent("password"));       
  116. last_login=user.getLastLogin();
  117. user.login();
  118. login_password=h.getContent("password");
  119. model=parent.getStorage().createXMLUserModel(user);
  120. connections=new Hashtable();
  121. stores=new Hashtable();
  122. folders=new Hashtable();
  123. mailsession=Session.getDefaultInstance(System.getProperties(),null);
  124. setEnv();
  125.     }
  126.     public XMLUserModel getUserModel() {
  127. return model;
  128.     }
  129.     public Document getModel() {
  130. return model.getRoot();
  131.     }
  132.     /**
  133.      * Calculate session-ID for a session.
  134.      *
  135.      * @param a Adress of the remote host
  136.      * @param h Requestheader of the remote user agent
  137.      * @returns Session-ID
  138.      */
  139.     public String calcCode(InetAddress a, HTTPRequestHeader h) {
  140. if(sess==null) {
  141.     return Helper.calcSessionCode(a,h);
  142. } else {
  143.     try {
  144. Class srvltreq=Class.forName("javax.servlet.http.HttpSession");
  145. if(srvltreq.isInstance(sess)) {
  146.     return ((javax.servlet.http.HttpSession)sess).getId();
  147. } else {
  148.     return "error";
  149. }
  150.     } catch(Throwable t) {
  151. return "error";
  152.     }
  153. }
  154.     }
  155.     /**
  156.      * Login to this session.
  157.      * Establishes connections to a user磗 Mailhosts
  158.      *
  159.      * @param h RequestHeader with content from Login-POST operation.
  160.      */
  161.     public void login(HTTPRequestHeader h) throws InvalidPasswordException {
  162. //user.login(h.getContent("password"));
  163. setLastAccess();
  164. setEnv();
  165. connectAll();
  166.     }
  167.     /**
  168.      * Return a locale-specific string resource
  169.      */
  170.     public String getStringResource(String key) {
  171. return parent.getStorage().getStringResource(key,user.getPreferredLocale());
  172.     }
  173.     
  174.     /**
  175.      * Create a Message List.
  176.      * Fetches a list of headers in folder foldername for part list_part.
  177.      * The messagelist will be stored in the "MESSAGES" environment.
  178.      *
  179.      * @param foldername folder for which a message list should be built
  180.      * @param list_part part of list to display (1 = last xx messages, 2 = total-2*xx - total-xx messages)
  181.      */
  182.     public void createMessageList(String folderhash,int list_part) 
  183. throws NoSuchFolderException {
  184. long time_start=System.currentTimeMillis();
  185. TimeZone tz=TimeZone.getDefault();
  186. DateFormat df=DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.SHORT, user.getPreferredLocale());
  187. df.setTimeZone(tz);
  188. try {
  189.     Folder folder=getFolder(folderhash);
  190.     Element xml_folder=model.getFolder(folderhash);
  191.     Element xml_current=model.setCurrentFolder(folderhash);
  192.     Element xml_messagelist=model.getMessageList(xml_folder);
  193.     if(folder == null) {
  194. throw new NoSuchFolderException(folderhash);
  195.     }
  196.     long fetch_start=System.currentTimeMillis();
  197.     if(!folder.isOpen()) {
  198. folder.open(Folder.READ_ONLY);
  199.     } else {
  200. folder.close(false);
  201. folder.open(Folder.READ_ONLY);
  202.     }
  203.     /* Calculate first and last message to show */
  204.     int total_messages=folder.getMessageCount();
  205.     int new_messages=folder.getNewMessageCount();
  206.     int show_msgs=user.getMaxShowMessages();
  207.     
  208.     xml_messagelist.setAttribute("total",total_messages+"");
  209.     xml_messagelist.setAttribute("new",new_messages+"");
  210. //      System.err.println("Total: "+total_messages);
  211.     /* Handle small messagelists correctly */
  212.     if(total_messages < show_msgs) {
  213. show_msgs = total_messages;
  214.     }
  215.     /* Don't accept list-parts smaller than 1 */
  216.     if(list_part < 1) {
  217. list_part=1;
  218.     }
  219.     for(int k=0;k<list_part;k++) {
  220. total_messages-=show_msgs;
  221.     }
  222.     /* Handle beginning of message list */
  223.     if(total_messages<0) {
  224. total_messages=0;
  225.     }
  226.     int first=total_messages+1;
  227.     int last=total_messages+show_msgs;
  228.     /* Set environment variable */
  229.     setEnv();
  230.     xml_current.setAttribute("first_msg",first+"");
  231.     xml_current.setAttribute("last_msg",last+"");
  232.     xml_current.setAttribute("list_part",list_part+"");
  233.     /* Fetch headers */
  234.     FetchProfile fp=new FetchProfile();
  235.     fp.add(FetchProfile.Item.ENVELOPE);
  236.     fp.add(FetchProfile.Item.FLAGS);
  237.     fp.add(FetchProfile.Item.CONTENT_INFO);
  238. //      System.err.println("Last: "+last+", first: "+first);
  239.     Message[] msgs=folder.getMessages(first,last);
  240.     //System.err.println(msgs.length + " messages fetching...");
  241.     folder.fetch(msgs,fp);
  242.     long fetch_stop=System.currentTimeMillis();
  243.     Hashtable header=new Hashtable(15);
  244.     Flags.Flag[] sf;
  245.     String from,to,cc,bcc,replyto,subject;
  246.     String messageid;
  247.     for(int i=msgs.length-1; i>=0; i--) {
  248. StringTokenizer tok=new StringTokenizer(((MimeMessage)msgs[i]).getMessageID(),"<>");
  249. messageid=tok.nextToken();
  250.         XMLMessage xml_message=model.getMessage(xml_folder,msgs[i].getMessageNumber()+"",
  251.      messageid);
  252.     
  253. /* Addresses */
  254. from="";replyto="";to="";cc="";bcc="";
  255. try {    
  256.     from=MimeUtility.decodeText(Helper.joinAddress(msgs[i].getFrom()));
  257.     replyto=MimeUtility.decodeText(Helper.joinAddress(msgs[i].getReplyTo()));
  258.     to=MimeUtility.decodeText(Helper.joinAddress(msgs[i].getRecipients(Message.RecipientType.TO)));
  259.     cc=MimeUtility.decodeText(Helper.joinAddress(msgs[i].getRecipients(Message.RecipientType.CC)));
  260.     bcc=MimeUtility.decodeText(Helper.joinAddress(msgs[i].getRecipients(Message.RecipientType.BCC)));
  261. } catch(UnsupportedEncodingException e) {
  262.     parent.getStorage().log(Storage.LOG_WARN,"Unsupported Encoding: "+e.getMessage());
  263. }
  264. if(from=="") from=getStringResource("unknown sender");
  265. if(to == "") to = getStringResource("unknown recipient");    
  266.     
  267. /* Flags */
  268. sf = msgs[i].getFlags().getSystemFlags();
  269. String basepath=parent.getBasePath();
  270.     
  271. for(int j=0;j<sf.length;j++) {
  272.     if(sf[j]== Flags.Flag.RECENT) xml_message.setAttribute("recent","true");
  273.     if(sf[j]== Flags.Flag.SEEN) xml_message.setAttribute("seen","true");
  274.     if(sf[j]== Flags.Flag.DELETED) xml_message.setAttribute("deleted","true");
  275.     if(sf[j]== Flags.Flag.ANSWERED) xml_message.setAttribute("answered","true");
  276.     if(sf[j]== Flags.Flag.DRAFT) xml_message.setAttribute("draft","true");
  277.     if(sf[j]== Flags.Flag.FLAGGED) xml_message.setAttribute("flagged","true");
  278.     if(sf[j]== Flags.Flag.USER) xml_message.setAttribute("user","true");
  279. }
  280. if(msgs[i] instanceof MimeMessage && 
  281.    ((MimeMessage) msgs[i]).getContentType().toUpperCase().startsWith("MULTIPART/")) {
  282.     xml_message.setAttribute("attachment","true");
  283. }
  284.     
  285. if(msgs[i] instanceof MimeMessage) {
  286.     int size=((MimeMessage) msgs[i]).getSize();
  287.     size/=1024;
  288.     xml_message.setAttribute("size",size+" kB");
  289. }
  290.     
  291. /* Subject */
  292. subject="";
  293. if(msgs[i].getSubject() != null) {
  294.     try {
  295. subject=MimeUtility.decodeText(msgs[i].getSubject());
  296.     } catch(UnsupportedEncodingException ex) {
  297. parent.getStorage().log(Storage.LOG_WARN,"Unsupported Encoding: "+ex.getMessage());
  298.     }
  299. }
  300. if(subject == null || subject.equals("")) {
  301.     subject=getStringResource("no subject");
  302. }
  303.     
  304. /* Set all of what we found into the DOM */
  305. xml_message.setHeader("FROM",from);
  306. try {
  307.     xml_message.setHeader("SUBJECT",MimeUtility.decodeText(subject));
  308. } catch(UnsupportedEncodingException e) {
  309.     parent.getStorage().log(Storage.LOG_WARN,
  310.     "Unsupported Encoding: "+e.getMessage());
  311. }
  312. xml_message.setHeader("TO",to);
  313. xml_message.setHeader("CC",cc);
  314. xml_message.setHeader("BCC",bcc);
  315. xml_message.setHeader("REPLY-TO",replyto);
  316.     
  317. /* Date */
  318. Date d=msgs[i].getSentDate();
  319. String ds="";
  320. if(d!=null) {
  321.     ds=df.format(d);
  322. }
  323. xml_message.setHeader("DATE",ds);
  324.     }
  325.     long time_stop=System.currentTimeMillis();
  326.     
  327. //      try {
  328. //  XMLCommon.writeXML(model.getRoot(),new FileOutputStream("/tmp/wmdebug"),"");
  329. //      } catch(IOException ex) {}
  330.     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.");
  331.     folder.close(false);
  332. } catch(NullPointerException e) {
  333.     e.printStackTrace();
  334.     throw new NoSuchFolderException(folderhash);
  335. } catch(MessagingException ex) {
  336.     ex.printStackTrace();
  337. }
  338.     }
  339.     public static final int GETMESSAGE_MODE_STANDARD=0;
  340.     public static final int GETMESSAGE_MODE_REPLY=1;
  341.     public static final int GETMESSAGE_MODE_FORWARD=2;    
  342.     public void getMessage(String folderhash, int msgnum) throws NoSuchFolderException {
  343. getMessage(folderhash,msgnum,GETMESSAGE_MODE_STANDARD);
  344.     }
  345.     /**
  346.      * Fetch a message from a folder.
  347.      * Will put the messages parameters in the sessions environment
  348.      *
  349.      * @param foldername Name of the folder were the message should be fetched from
  350.      * @param msgnum Number of the message to fetch
  351.      * @param mode there are three different modes: standard, reply and forward. reply and forward will enter the message
  352.      *             into the current work element of the user and set some additional flags on the message if the user
  353.      *             has enabled this option.
  354.      * @see GETMESSAGE_MODE_STANDARD
  355.      * @see GETMESSAGE_MODE_REPLY
  356.      * @see GETMESSAGE_MODE_FORWARD
  357.      */
  358.     public void getMessage(String folderhash, int msgnum, int mode) throws NoSuchFolderException {
  359. // security reasons:
  360. // attachments=null;
  361. try {
  362.     TimeZone tz=TimeZone.getDefault();
  363.     DateFormat df=DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.SHORT, user.getPreferredLocale());
  364.     df.setTimeZone(tz);
  365.     Folder folder=getFolder(folderhash);
  366.     Element xml_folder=model.getFolder(folderhash);
  367.     if(folder==null) {
  368. throw new NoSuchFolderException("No such folder: "+folderhash);
  369.     }
  370.     if(folder.isOpen() && folder.getMode()==Folder.READ_WRITE) {
  371. folder.close(false);
  372. folder.open(Folder.READ_ONLY);
  373.     } else if(!folder.isOpen()) {
  374. folder.open(Folder.READ_ONLY);
  375.     }
  376.     
  377.     MimeMessage m=(MimeMessage)folder.getMessage(msgnum);
  378.     StringTokenizer tok=new StringTokenizer(m.getMessageID(),"<>");
  379.     String messageid=tok.nextToken();
  380.     Element xml_current=model.setCurrentMessage(messageid);
  381.     XMLMessage xml_message=model.getMessage(xml_folder,m.getMessageNumber()+"",
  382.     messageid);
  383.     /* Check whether we already cached this message (not only headers but complete)*/
  384.     boolean cached=xml_message.messageCompletelyCached();
  385.     /* If we cached the message, we don't need to fetch it again */
  386.     if(!cached) {
  387. //Element xml_header=model.getHeader(xml_message);
  388. try {
  389.     String from=MimeUtility.decodeText(Helper.joinAddress(m.getFrom()));
  390.     String replyto=MimeUtility.decodeText(Helper.joinAddress(m.getReplyTo()));
  391.     String to=MimeUtility.decodeText(Helper.joinAddress(m.getRecipients(Message.RecipientType.TO)));
  392.     String cc=MimeUtility.decodeText(Helper.joinAddress(m.getRecipients(Message.RecipientType.CC)));
  393.     String bcc=MimeUtility.decodeText(Helper.joinAddress(m.getRecipients(Message.RecipientType.BCC)));
  394.     Date date_orig=m.getSentDate();
  395.     String date=getStringResource("no date");
  396.     if(date_orig!=null) {
  397. date=df.format(date_orig);
  398.     }
  399.     String subject="";     
  400.     if(m.getSubject() != null) {
  401. subject=MimeUtility.decodeText(m.getSubject());
  402.     }
  403.     if(subject == null || subject.equals("")) {
  404. subject=getStringResource("no subject");
  405.     }
  406.     
  407.     try {
  408. Flags.Flag[] sf = m.getFlags().getSystemFlags();
  409. for(int j=0;j<sf.length;j++) {
  410.     if(sf[j]== Flags.Flag.RECENT) xml_message.setAttribute("recent","true");
  411.     if(sf[j]== Flags.Flag.SEEN) xml_message.setAttribute("seen","true");
  412.     if(sf[j]== Flags.Flag.DELETED) xml_message.setAttribute("deleted","true");
  413.     if(sf[j]== Flags.Flag.ANSWERED) xml_message.setAttribute("answered","true");
  414.     if(sf[j]== Flags.Flag.DRAFT) xml_message.setAttribute("draft","true");
  415.     if(sf[j]== Flags.Flag.FLAGGED) xml_message.setAttribute("flagged","true");
  416.     if(sf[j]== Flags.Flag.USER) xml_message.setAttribute("user","true");
  417. }
  418.     } catch(NullPointerException ex) {}
  419.     if(m.getContentType().toUpperCase().startsWith("MULTIPART/")) {
  420. xml_message.setAttribute("attachment","true");
  421.     }
  422.     
  423.     int size=m.getSize();
  424.     size/=1024;
  425.     xml_message.setAttribute("size",size+" kB");
  426.     
  427.     /* Set all of what we found into the DOM */
  428.     xml_message.setHeader("FROM",from);
  429.     xml_message.setHeader("SUBJECT",Fancyfier.apply(subject));
  430.     xml_message.setHeader("TO",to);
  431.     xml_message.setHeader("CC",cc);
  432.     xml_message.setHeader("BCC",bcc);
  433.     xml_message.setHeader("REPLY-TO",replyto);
  434.     xml_message.setHeader("DATE",date);
  435.     
  436.     /* Decode MIME contents recursively */
  437.     xml_message.removeAllParts();
  438.     parseMIMEContent(m,xml_message,messageid);
  439.     
  440. } catch(UnsupportedEncodingException e) {
  441.     parent.getStorage().log(Storage.LOG_WARN,"Unsupported Encoding in parseMIMEContent: "+e.getMessage());
  442.     System.err.println("Unsupported Encoding in parseMIMEContent: "+e.getMessage());
  443. }
  444.     }
  445.     /* Set seen flag (Maybe make that threaded to improve performance) */
  446.     if(user.wantsSetFlags()) {
  447. if(folder.isOpen() && folder.getMode()==Folder.READ_ONLY) {
  448.     folder.close(false);
  449.     folder.open(Folder.READ_WRITE);
  450. } else if(!folder.isOpen()) {
  451.     folder.open(Folder.READ_WRITE);
  452. }
  453. folder.setFlags(msgnum,msgnum,new Flags(Flags.Flag.SEEN),true);
  454. folder.setFlags(msgnum,msgnum,new Flags(Flags.Flag.RECENT), false);
  455. if((mode & GETMESSAGE_MODE_REPLY) == GETMESSAGE_MODE_REPLY) {
  456.     folder.setFlags(msgnum,msgnum,new Flags(Flags.Flag.ANSWERED),true);
  457. }
  458.     }
  459.     folder.close(false);
  460.     XMLMessage work=null;
  461.     if((mode & GETMESSAGE_MODE_REPLY) == GETMESSAGE_MODE_REPLY || 
  462.        (mode & GETMESSAGE_MODE_FORWARD) == GETMESSAGE_MODE_FORWARD) {
  463. System.err.println("Setting work message!");
  464. work=model.setWorkMessage(xml_message);
  465. String newmsgid=WebMailServer.generateMessageID(user.getUserName());
  466. if(work != null && (mode & GETMESSAGE_MODE_REPLY) == GETMESSAGE_MODE_REPLY) {
  467.     String from=work.getHeader("FROM");
  468.     work.setHeader("FROM",user.getEmail());
  469.     work.setHeader("TO",from);
  470.     work.prepareReply(getStringResource("reply subject prefix"), 
  471.       getStringResource("reply subject postfix"),
  472.       getStringResource("reply message prefix"),
  473.       getStringResource("reply message postfix"));
  474. } else if(work != null && (mode & GETMESSAGE_MODE_FORWARD) == GETMESSAGE_MODE_FORWARD) {
  475.     String from=work.getHeader("FROM");
  476.     work.setHeader("FROM",user.getEmail());
  477.     work.setHeader("TO","");
  478.     work.setHeader("CC","");
  479.     work.prepareForward(getStringResource("forward subject prefix"), 
  480. getStringResource("forward subject postfix"),
  481. getStringResource("forward message prefix"),
  482. getStringResource("forward message postfix"));
  483.     /* Copy all references to MIME parts to the new message id */
  484.     Enumeration attids=getMimeParts(work.getAttribute("msgid"));
  485.     while(attids.hasMoreElements()) {
  486. String key=(String)attids.nextElement();
  487. StringTokenizer tok2=new StringTokenizer(key,"/");
  488. tok2.nextToken();
  489. String newkey=tok2.nextToken();
  490. mime_parts_decoded.put(newmsgid+"/"+newkey,mime_parts_decoded.get(key));
  491.     }
  492. }
  493. /* Clear the msgnr and msgid fields at last */
  494. work.setAttribute("msgnr","0");
  495. work.setAttribute("msgid",newmsgid);
  496. prepareCompose();
  497.     }
  498. } catch(MessagingException ex) {
  499.     ex.printStackTrace();
  500. }
  501.     }
  502.     /**
  503.        Use depth-first search to go through MIME-Parts recursively.
  504.  
  505.        @param p Part to begin with
  506.     */
  507.     protected void parseMIMEContent(Part p, XMLMessagePart parent_part, String msgid) throws MessagingException {
  508. StringBuffer content=new StringBuffer(1000);
  509.         XMLMessagePart xml_part;
  510. try {
  511.     if(p.getContentType().toUpperCase().startsWith("TEXT") ||
  512.        p.getContentType().toUpperCase().startsWith("MESSAGE")) {
  513. /* The part is a standard message part in some incarnation of text (html or plain).
  514.    We should decode it and take care of some extra issues like recognize quoted parts,
  515.    filter JavaScript parts and replace smileys with smiley-icons if the user has
  516.    set wantsFancy() */
  517. if(p.getContentType().toUpperCase().startsWith("TEXT/HTML")) {
  518.     xml_part=parent_part.createPart("html");
  519. } else {
  520.     xml_part=parent_part.createPart("text");
  521. }
  522. BufferedReader in;
  523. if(p instanceof MimeBodyPart) {
  524.     int size=p.getSize();
  525.     MimeBodyPart mpb=(MimeBodyPart)p;
  526.     InputStream is=mpb.getInputStream();
  527.     /* Workaround for Java or Javamail Bug */
  528.     is=new BufferedInputStream(is);
  529.     ByteStore ba=ByteStore.getBinaryFromIS(is,size);
  530.     in=new BufferedReader(new InputStreamReader(new ByteArrayInputStream(ba.getBytes())));
  531.     /* End of workaround */
  532.     size=is.available();
  533. } else {
  534.     in=new BufferedReader(new InputStreamReader(p.getInputStream()));
  535. }
  536. String token="";
  537. int quote_level=0, old_quotelevel=0;
  538. boolean javascript_mode=false;
  539. /* Read in the message part line by line */
  540. while((token=in.readLine()) != null) {
  541.     /* First decode all language and MIME dependant stuff */
  542.     try {
  543. token=MimeUtility.decodeText(token);
  544.     } catch(UnsupportedEncodingException e) {
  545. parent.getStorage().log(Storage.LOG_WARN,"Unsupported Encoding in parseMIMEContent: "+e.getMessage());
  546.     }
  547.     
  548.     /* Here we figure out which quote level this line has, simply by counting how many
  549.        ">" are in front of the line, ignoring all whitespaces. */
  550.     int current_quotelevel=Helper.getQuoteLevel(token);
  551.     /* When we are in a different quote level than the last line, we append all we got
  552.        so far to the part with the old quotelevel and begin with a clean String buffer */
  553.     if(current_quotelevel != old_quotelevel) {
  554. xml_part.addContent(content.toString(),old_quotelevel);
  555. old_quotelevel = current_quotelevel;
  556. content=new StringBuffer(1000);
  557.     }
  558.     if(user.wantsBreakLines()) {
  559. Enumeration enum=Helper.breakLine(token,user.getMaxLineLength(),current_quotelevel);
  560. while(enum.hasMoreElements()) {
  561.     String s=(String)enum.nextElement();
  562.     if(user.wantsShowFancy()) {
  563. content.append(Fancyfier.apply(s)).append("n");
  564.     } else {
  565. content.append(s).append("n");
  566.     }
  567. }
  568.     } else {
  569. if(user.wantsShowFancy()) {
  570.     content.append(Fancyfier.apply(token)).append("n");
  571. } else {
  572.     content.append(token).append("n");
  573. }
  574.     }
  575. }
  576. xml_part.addContent(content.toString(),old_quotelevel);
  577. content=new StringBuffer(1000);
  578.     } else if(p.getContentType().toUpperCase().startsWith("MULTIPART/ALTERNATIVE")) {
  579. /* This is a multipart/alternative part. That means that we should pick one of
  580.    the formats and display it for this part. Our current precedence list is
  581.    to choose HTML first and then to choose plain text. */
  582. MimeMultipart m=(MimeMultipart)p.getContent();
  583. String[] preferred={"TEXT/PLAIN","TEXT"};
  584. boolean found=false;
  585. int alt=0;
  586. // Walk though our preferred list of encodings. If we have found a fitting part,
  587. // decode it and replace it for the parent (this is what we really want with an 
  588. // alternative!)
  589.     findalt: while(!found && alt < preferred.length) {
  590. for(int i=0;i<m.getCount();i++) {
  591.     Part p2=m.getBodyPart(i);
  592.     if(p2.getContentType().toUpperCase().startsWith(preferred[alt])) {
  593. parseMIMEContent(p2,parent_part,msgid);
  594. found=true;
  595. break findalt;
  596.     }
  597. }
  598. alt++;
  599.     }
  600. if(!found) {
  601.     // If we didn't find one of our preferred encodings, choose the first one
  602.     // simply pass the parent part because replacement is what we really want with
  603.     // an alternative.
  604.     parseMIMEContent(m.getBodyPart(0),parent_part,msgid);
  605. }
  606.     } else if(p.getContentType().toUpperCase().startsWith("MULTIPART/")) {
  607. /* This is a standard multipart message. We should recursively walk thorugh all of 
  608.    the parts and decode them, appending as children to the current part */
  609. xml_part=parent_part.createPart("multi");
  610. MimeMultipart m=(MimeMultipart)p.getContent();
  611. for(int i=0;i<m.getCount();i++) {
  612.     parseMIMEContent(m.getBodyPart(i),xml_part,msgid);
  613. }
  614.     } else {
  615. /* Else treat the part as a binary part that the user should either download or
  616.    get displayed immediately in case of an image */
  617. InputStream in=null;
  618. String type="";
  619. if(p.getContentType().toUpperCase().startsWith("IMAGE/JPG") ||
  620.    p.getContentType().toUpperCase().startsWith("IMAGE/JPEG")) {
  621.     type="jpg";
  622.     xml_part=parent_part.createPart("image");
  623. } else if(p.getContentType().toUpperCase().startsWith("IMAGE/GIF")) {
  624.     type="gif";
  625.     xml_part=parent_part.createPart("image");
  626. } else if(p.getContentType().toUpperCase().startsWith("IMAGE/PNG")) {
  627.     type="png";
  628.     xml_part=parent_part.createPart("image");
  629. } else {
  630.     xml_part=parent_part.createPart("binary");
  631. }
  632. int size=p.getSize();
  633. if(p instanceof MimeBodyPart) {
  634.     MimeBodyPart mpb=(MimeBodyPart)p;
  635. //      System.err.println("MIME Body part (image), Encoding: "+mpb.getEncoding());
  636.     InputStream is=mpb.getInputStream();
  637.     /* Workaround for Java or Javamail Bug */
  638.     in=new BufferedInputStream(is);
  639.     ByteStore ba=ByteStore.getBinaryFromIS(in,size);
  640.     in=new ByteArrayInputStream(ba.getBytes());
  641.     /* End of workaround */
  642.     size=in.available();
  643. } else {
  644.     System.err.println("*** No MIME Body part!! ***");
  645.     in=p.getInputStream();
  646. }
  647. ByteStore data=ByteStore.getBinaryFromIS(in,size);
  648. if(mime_parts_decoded==null) {
  649.     mime_parts_decoded=new Hashtable();
  650. }
  651. String name=p.getFileName();
  652. if(name == null || name.equals("")) {
  653.     name="unknown."+type;
  654. }
  655. // Eliminate space characters. Should do some more things in the future
  656. name=name.replace(' ','_');
  657. data.setContentType(p.getContentType());
  658. data.setContentEncoding("BINARY");
  659. mime_parts_decoded.put(msgid+"/"+name,data);
  660. xml_part.setAttribute("filename",name);
  661. xml_part.setAttribute("size",size+"");
  662. String description=p.getDescription()==null?"":p.getDescription();
  663. xml_part.setAttribute("description",description);
  664. StringTokenizer tok=new StringTokenizer(p.getContentType(),";");
  665. xml_part.setAttribute("content-type",tok.nextToken().toLowerCase());
  666.     }
  667. } catch(java.io.IOException ex) {
  668.     ex.printStackTrace();
  669. }
  670.     }
  671.     
  672.     public ByteStore getMIMEPart(String msgid,String name) {
  673. if(mime_parts_decoded != null) {
  674.     return (ByteStore)mime_parts_decoded.get(msgid+"/"+name);
  675. } else {
  676.     return null;
  677. }
  678.     }
  679.     public Enumeration getMimeParts(String msgid) {
  680. if(mime_parts_decoded == null) {
  681.     mime_parts_decoded=new Hashtable();
  682. }
  683. Enumeration enum=mime_parts_decoded.keys();
  684.         Vector v=new Vector();
  685. while(enum.hasMoreElements()) {
  686.     String key=(String)enum.nextElement();
  687.     if(key.startsWith(msgid)) {
  688. v.addElement(key);
  689.     }
  690. }
  691. return v.elements();
  692.     }
  693.     public void clearWork() {
  694. clearAttachments();
  695. model.clearWork();
  696.     }
  697.     public void prepareCompose() {
  698. model.getWorkMessage().getFirstMessageTextPart().addContent("n--n",0);
  699. model.getWorkMessage().getFirstMessageTextPart().addContent(user.getSignature(),0);
  700.     }
  701.     public void clearAttachments() {
  702. attachments_size=0;
  703. XMLMessage xml_message=model.getWorkMessage();
  704. String msgid=xml_message.getAttribute("msgid");
  705. Enumeration enum=getMimeParts(msgid);
  706. attachments_size=0;
  707. while(enum.hasMoreElements()) {
  708.     mime_parts_decoded.remove((String)enum.nextElement());
  709. }
  710.     }
  711.     public Hashtable getAttachments() {
  712. Hashtable hash=new Hashtable();
  713. XMLMessage xml_message=model.getWorkMessage();
  714. String msgid=xml_message.getAttribute("msgid");
  715. Enumeration enum=getMimeParts(msgid);
  716. while(enum.hasMoreElements()) {
  717.     String key=(String)enum.nextElement();
  718.     String filename=key.substring(msgid.length()+1);
  719.     hash.put(filename,mime_parts_decoded.get(key));
  720. }
  721. return hash;
  722.     }
  723.     public ByteStore getAttachment(String key) {
  724. XMLMessage xml_message=model.getWorkMessage();
  725. String msgid=xml_message.getAttribute("msgid");
  726. return getMIMEPart(msgid,key);
  727.     }
  728.     public void addWorkAttachment(String name, ByteStore bs, String description) throws WebMailException {
  729. XMLMessage xml_message=model.getWorkMessage();
  730. XMLMessagePart xml_multipart=xml_message.getFirstMessageMultiPart();
  731. String msgid=xml_message.getAttribute("msgid");
  732. bs.setDescription(description);
  733. Enumeration enum=getMimeParts(msgid);
  734. attachments_size=0;
  735. while(enum.hasMoreElements()) {
  736.     ByteStore b=(ByteStore)mime_parts_decoded.get((String)enum.nextElement());
  737.     attachments_size+=b.getSize();
  738. }
  739. int max_size=0;
  740. try {
  741.     max_size=Integer.parseInt( parent.getStorage().getConfig("MAX ATTACH SIZE"));
  742. } catch(NumberFormatException e) {
  743.     parent.getStorage().log(Storage.LOG_WARN,"Invalid setting for parameter "MAX ATTACH SIZE". Must be a number!");
  744. }
  745. if(attachments_size+bs.getSize() > max_size) {
  746.     throw new WebMailException("Attachments are too big. The sum of the sizes may not exceed "+max_size+" bytes.");
  747. } else {
  748.     mime_parts_decoded.put(msgid+"/"+name,bs);
  749.     attachments_size+=bs.getSize();
  750.     XMLMessagePart xml_part=xml_multipart.createPart("binary");
  751.     
  752.     xml_part.setAttribute("filename",name);
  753.     xml_part.setAttribute("size",bs.getSize()+"");
  754.     xml_part.setAttribute("description",description);
  755.     xml_part.setAttribute("content-type",bs.getContentType().toLowerCase());
  756. }
  757. setEnv();
  758. //XMLCommon.debugXML(model.getRoot());
  759.     }
  760.     public void removeWorkAttachment(String name) {
  761. XMLMessage xml_message=model.getWorkMessage();
  762. XMLMessagePart xml_multipart=xml_message.getFirstMessageMultiPart();
  763. String msgid=xml_message.getAttribute("msgid");
  764. mime_parts_decoded.remove(msgid+"/"+name);
  765. Enumeration enum=getMimeParts(msgid);
  766. attachments_size=0;
  767. while(enum.hasMoreElements()) {
  768.     ByteStore b=(ByteStore)mime_parts_decoded.get((String)enum.nextElement());
  769.     attachments_size+=b.getSize();
  770. }
  771. enum=xml_multipart.getParts();
  772. XMLMessagePart oldpart=null;
  773. while(enum.hasMoreElements()) {
  774.     XMLMessagePart tmp=(XMLMessagePart)enum.nextElement();
  775.     if(tmp.getAttribute("filename") != null &&
  776.        tmp.getAttribute("filename").equals(name)) {
  777. oldpart=tmp;
  778. break;
  779.     }
  780. }
  781. if(oldpart != null) {
  782.     xml_multipart.removePart(oldpart);
  783. }
  784. setEnv();
  785. //XMLCommon.debugXML(model.getRoot());
  786.     }
  787.     /**
  788.      * Store a message in the environment for further processing.
  789.      */
  790.     public void storeMessage(HTTPRequestHeader head) {
  791. XMLMessage xml_message=model.getWorkMessage();
  792. XMLMessagePart xml_textpart=xml_message.getFirstMessageTextPart();
  793. /* Store the already typed message if necessary/possible */
  794. if(head.isContentSet("BODY")) {
  795.     StringBuffer content=new StringBuffer();
  796.     // If the user enabled "break line", then do it!
  797.     if(user.wantsBreakLines()) {
  798. StringTokenizer tok=new StringTokenizer(head.getContent("BODY"),"n");
  799. while(tok.hasMoreTokens()) {
  800.     String line=tok.nextToken();
  801.     Enumeration enum=Helper.breakLine(line,user.getMaxLineLength(),
  802.       Helper.getQuoteLevel(line));
  803.     while(enum.hasMoreElements()) {        
  804. content.append((String)enum.nextElement()).append('n');
  805.     }
  806. }
  807.     } else {
  808. content.append(head.getContent("BODY"));
  809.     }
  810.     xml_textpart.removeAllContent();
  811.     xml_textpart.addContent(content.toString(),0);
  812. }
  813. if(head.isContentSet("TO")) {
  814.     xml_message.setHeader("TO",head.getContent("TO"));
  815. }
  816. if(head.isContentSet("CC")) {
  817.     xml_message.setHeader("CC",head.getContent("CC"));
  818. }
  819. if(head.isContentSet("BCC")) {
  820.     xml_message.setHeader("BCC",head.getContent("BCC"));
  821. }
  822. if(head.isContentSet("REPLY-TO")) {
  823.     xml_message.setHeader("REPLY-TO",head.getContent("REPLY-TO"));
  824. }
  825. if(head.isContentSet("SUBJECT")) {
  826.     xml_message.setHeader("SUBJECT",head.getContent("SUBJECT"));
  827. }
  828. setEnv();
  829.     }        
  830.     
  831.     /**
  832.      * Connect to all Mailhosts
  833.      * @deprecated Should use refreshFolderInformation now.
  834.      */
  835.     public void connectAll() {
  836. refreshFolderInformation();
  837.     }
  838.     /**
  839.        Get a childfolder of a rootfolder for a specified hash value
  840.     */
  841.     public Folder getChildFolder(Folder root, String folderhash) {
  842. return getFolder(folderhash);
  843.     }
  844.     public Folder getFolder(String folderhash) {
  845. return (Folder)folders.get(folderhash);
  846.     }
  847.     /**
  848.      * Construct the folder subtree for the given folder and append it to xml_parent.
  849.      */
  850.     protected int getFolderTree(Folder folder, Element xml_parent) {
  851. int depth=1;
  852. String id=Integer.toHexString(folder.hashCode());
  853. // If possible, use the MD5-Sum for the folder ID because it is persistant over sessions
  854. try {
  855.     MD5 md5=new MD5(folder.getURLName());    
  856.     id=md5.asHex();
  857. } catch(MessagingException ex) {
  858. }
  859. boolean holds_folders=false,holds_messages=false;
  860. Element xml_folder;
  861. try {
  862.     holds_folders=(folder.getType() & Folder.HOLDS_FOLDERS) == Folder.HOLDS_FOLDERS;
  863.     holds_messages=(folder.getType() & Folder.HOLDS_MESSAGES) == Folder.HOLDS_MESSAGES;
  864.     xml_folder=model.createFolder(id,folder.getName(),holds_folders,holds_messages);
  865. } catch(MessagingException ex) {
  866.     xml_folder=model.createFolder(id,folder.getName(),holds_folders,holds_messages);
  867.     xml_folder.setAttribute("error",ex.getMessage());
  868. }
  869. folders.put(id,folder);
  870. try {
  871.     /* This folder can contain messages */
  872.     if(holds_messages) {
  873. Element messagelist=model.createMessageList();
  874. int total_messages=folder.getMessageCount();
  875. int new_messages=folder.getNewMessageCount();
  876. if((total_messages == -1 || new_messages == -1) && !folder.isOpen()) {
  877.     folder.open(Folder.READ_ONLY);
  878.     total_messages=folder.getMessageCount();
  879.     new_messages=folder.getNewMessageCount();
  880. }
  881. folder.close(false);
  882. messagelist.setAttribute("total",total_messages+"");
  883. messagelist.setAttribute("new",new_messages+"");
  884. xml_folder.appendChild(messagelist);
  885.     }
  886. } catch(MessagingException ex) {
  887.     xml_folder.setAttribute("error",ex.getMessage());
  888. }
  889. try {
  890.     /* There are subfolders, get them! */
  891.     if(holds_folders) {
  892. Folder[] subfolders=folder.list();
  893. int max_tree_depth=0;
  894. for(int i=0;i<subfolders.length;i++) {
  895.     int tree_depth=getFolderTree(subfolders[i],xml_folder);
  896.     if(tree_depth>max_tree_depth) {
  897. max_tree_depth=tree_depth;
  898.     }
  899. }
  900. depth+=max_tree_depth;
  901.     }
  902. } catch(MessagingException ex) {
  903.     xml_folder.setAttribute("error",ex.getMessage());
  904. }
  905. xml_parent.appendChild(xml_folder);
  906. return depth;
  907.     }
  908.     /**
  909.      * Refresh Information about folders.
  910.      * Tries to connect folders that are not yet connected.
  911.      */
  912.     public void refreshFolderInformation() {
  913. setEnv();
  914. folders=new Hashtable();
  915. Folder cur_folder=null;
  916. String cur_mh_id="";
  917. Enumeration mailhosts=user.mailHosts();
  918. int max_depth=0;
  919. while(mailhosts.hasMoreElements()) {
  920.     cur_mh_id=(String)mailhosts.nextElement();
  921.     try {
  922. MailHostData mhd=user.getMailHost(cur_mh_id);
  923. URLName url=new URLName(mhd.getHostURL());
  924. Element mailhost=model.createMailhost(mhd.getName(),mhd.getID(),url.toString());
  925. cur_folder=getRootFolder(cur_mh_id);
  926. int depth=0;
  927. // Here we try to determine the remote IMAP or POP host.
  928. // Washington University
  929. if(cur_folder.getFolder("~"+mhd.getLogin()+"/mail").exists()) {
  930.     // Washington University stores user mailboxes as
  931.     // ~user/mail/...    
  932.     depth=getFolderTree(cur_folder.getFolder("INBOX"),mailhost);
  933.     if(depth>max_depth) {
  934. max_depth=depth;
  935.     }
  936.     depth=getFolderTree(cur_folder.getFolder("~"+mhd.getLogin()+"/mail"),mailhost);
  937. // Cyrus & Co have their folders beneath the INBOX
  938. else if(cur_folder.getFolder("INBOX").exists()) {
  939.     depth=getFolderTree(cur_folder.getFolder("INBOX"),mailhost);
  940. }     
  941. if(depth>max_depth) {
  942.     max_depth=depth;
  943. }
  944. model.addMailhost(mailhost);
  945.     } catch(MessagingException ex) {
  946. URLName url=new URLName(((MailHostData)user.getMailHost(cur_mh_id)).getHostURL());
  947. parent.getStorage().log(Storage.LOG_WARN,"Error connecting to mailhost ("+url.toString()+"): "+ex.getMessage());
  948.     }
  949. }
  950. model.setStateVar("max folder depth",(1+max_depth)+"");
  951.     }
  952.     public void refreshFolderInformation(String folderhash) {
  953. Folder folder=getFolder(folderhash);
  954. Element xml_folder=model.getFolder(folderhash);
  955. if(xml_folder.getAttribute("holds_messages").toLowerCase().equals("true")) {
  956.     try {
  957. Element messagelist=model.createMessageList();
  958. int total_messages=folder.getMessageCount();
  959. int new_messages=folder.getNewMessageCount();
  960. if((total_messages == -1 || new_messages == -1) && !folder.isOpen()) {
  961.     folder.open(Folder.READ_ONLY);
  962.     total_messages=folder.getMessageCount();
  963.     new_messages=folder.getNewMessageCount();
  964. }
  965. folder.close(false);
  966. messagelist.setAttribute("total",total_messages+"");
  967. messagelist.setAttribute("new",new_messages+"");
  968. model.removeMessageList(xml_folder);
  969. xml_folder.appendChild(messagelist);
  970.     
  971.     } catch(MessagingException ex) {
  972. xml_folder.setAttribute("error",ex.getMessage());
  973.     }
  974. }
  975.     }
  976.    
  977.     /**
  978.        Disconnect from all Mailhosts
  979.     */
  980.     public void disconnectAll() {
  981. Enumeration e=user.mailHosts();
  982. while(e.hasMoreElements()) {
  983.     String name=(String)e.nextElement();
  984.     disconnect(name);
  985. }
  986. e=stores.keys();
  987. while(e.hasMoreElements()) {
  988.     String name=(String)e.nextElement();
  989.     Store st=(Store)stores.get(name);
  990.     try {
  991. st.close();
  992. parent.getStorage().log(Storage.LOG_INFO,"Mail: Connection to "+st.toString()+" closed.");
  993.     } catch(MessagingException ex) {
  994. parent.getStorage().log(Storage.LOG_WARN,"Mail: Failed to close connection to "+st.toString()+". Reason: "+ex.getMessage());
  995.     }
  996.     stores.remove(name);
  997. }
  998.     }
  999.     public Folder getRootFolder(String name) throws MessagingException {
  1000. if(connections != null && connections.containsKey(name)) {
  1001.     return (Folder)connections.get(name);
  1002. } else {
  1003.     return connect(name);
  1004. }
  1005.     }
  1006.     protected Store connectStore(String host,String protocol,String login, String password) throws MessagingException {
  1007. /* Check whether the domain of this user allows to connect to the host */
  1008. WebMailVirtualDomain vdom=parent.getStorage().getVirtualDomain(user.getDomain());
  1009. if(!vdom.isAllowedHost(host)) {
  1010.     throw new MessagingException("You are not allowed to connect to this host");
  1011. }
  1012. /* Check if this host is already connected. Use connection if true, create a new one if false. */
  1013. Store st=(Store)stores.get(host+"-"+protocol);
  1014. if(st==null) {
  1015.     st=mailsession.getStore(protocol);
  1016.     stores.put(host+"-"+protocol,st);
  1017. }
  1018. /* Maybe this is a new store or this store has been disconnected. Reconnect if this is the case. */
  1019. if(!st.isConnected()) {
  1020.     try {
  1021. st.connect(host,login,password);
  1022. parent.getStorage().log(Storage.LOG_INFO,"Mail: Connection to "+st.toString()+".");
  1023.     } catch(AuthenticationFailedException ex) {
  1024. /* If login fails, try the login_password */
  1025. if(!login_password.equals(password) &&
  1026.    parent.getStorage().getConfig("FOLDER TRY LOGIN PASSWORD").toUpperCase().equals("YES")) {
  1027.     st.connect(host,login,login_password);
  1028.     parent.getStorage().log(Storage.LOG_INFO,"Mail: Connection to "+st.toString()+", second attempt with login password succeeded.");
  1029. } else {
  1030.     throw ex;
  1031. }
  1032.     }
  1033. }
  1034. return st;
  1035.     }    
  1036.     /**
  1037.        Connect to mailhost "name"
  1038.     */
  1039.     public Folder connect(String name) throws MessagingException {
  1040. MailHostData m=user.getMailHost(name);
  1041. URLName url=new URLName(m.getHostURL());
  1042. Store st=connectStore(url.getHost(),url.getProtocol(),m.getLogin(),m.getPassword());
  1043. //System.err.println("Default folder: "+st.getDefaultFolder().toString());
  1044. Folder f=st.getDefaultFolder();
  1045. connections.put(name,f);
  1046. parent.getStorage().log(Storage.LOG_INFO,"Mail: Folder "+f.toString()+" opened at store "+st.toString()+".");
  1047. return f;
  1048.     }
  1049.     
  1050.     /**
  1051.        Disconnect from mailhost "name"
  1052.     */
  1053.     public void disconnect(String name) {
  1054. try {
  1055.     Folder f=(Folder)connections.get(name);
  1056.     if(f != null && f.isOpen()) {
  1057. f.close(true);
  1058. Store st=((Folder)connections.get(name)).getStore();
  1059. //st.close();
  1060. parent.getStorage().log(Storage.LOG_INFO,"Mail: Disconnected from folder "+f.toString()+" at store "+st.toString()+".");
  1061.     } else {
  1062. parent.getStorage().log(Storage.LOG_WARN,"Mail: Folder "+name+" was null???.");
  1063.     }
  1064. } catch(MessagingException ex) {
  1065.     // Should not happen
  1066.     ex.printStackTrace();
  1067. } catch(NullPointerException ex) {
  1068.     // This happens when deleting a folder with an error
  1069.     ex.printStackTrace();
  1070. } finally {
  1071.     connections.remove(name);
  1072. }
  1073.     }
  1074.     /**
  1075.        Terminate this session.
  1076.     */
  1077.     public void logout() {
  1078. if(!is_logged_out) {
  1079.     is_logged_out=true;
  1080.     expungeFolders();
  1081.     disconnectAll();
  1082.     saveData();
  1083.     parent.getStorage().log(Storage.LOG_INFO,"WebMail: Session "+getSessionCode()+" logout.");
  1084.     // Make sure the session is invalidated
  1085.     if(sess != null) {
  1086. try {
  1087.     Class srvltreq=Class.forName("javax.servlet.http.HttpSession");
  1088.     if(srvltreq.isInstance(sess)) {
  1089. ((javax.servlet.http.HttpSession)sess).invalidate();
  1090.     }
  1091. } catch(Throwable t) {
  1092. }
  1093.     }
  1094.     if(parent.getSession(getSessionCode()) != null) {
  1095. parent.removeSession(this);
  1096.     }
  1097. } else {
  1098.     System.err.println("WARNING: Session was already logged out. Ignoring logout request.");
  1099. }
  1100.     }
  1101.     
  1102.     public boolean isLoggedOut() {
  1103. return is_logged_out;
  1104.     }
  1105.     public String getSessionCode() {
  1106. return session_code;
  1107.     }
  1108.     public long getLastAccess() {
  1109. return last_access;
  1110.     }
  1111.     public void setLastAccess() {
  1112. last_access=System.currentTimeMillis();
  1113.     }
  1114.     public void timeoutOccured() {
  1115. parent.getStorage().log(Storage.LOG_WARN,"WebMail: Session "+getSessionCode()+" timeout.");
  1116. logout();
  1117.     }
  1118.     public long getTimeout() {
  1119. long i=600000;
  1120. try {
  1121.     i=Long.parseLong(parent.getStorage().getConfig("session timeout"));
  1122. } catch(NumberFormatException ex) {
  1123.     ex.printStackTrace();
  1124. }
  1125. return i;
  1126.     }
  1127.     public Locale getLocale() {
  1128. return user.getPreferredLocale();
  1129.     }
  1130.     
  1131.     public void saveData() {
  1132. parent.getStorage().saveUserData(user.getUserName(),user.getDomain());
  1133.     }
  1134.     protected static int[] getSelectedMessages(HTTPRequestHeader head, int max) {
  1135. // System.err.print(" - select messages...");
  1136. Enumeration e=head.getContent().keys();
  1137. int _msgs[]=new int[max];
  1138. int j=0;
  1139. while(e.hasMoreElements()) {
  1140.     String s=(String)e.nextElement();
  1141.     if(s.startsWith("CH") && head.getContent(s).equals("on")) {
  1142. try {
  1143.     _msgs[j]=Integer.parseInt(s.substring(3));
  1144.     //    System.err.print(_msgs[j]+" ");
  1145.     j++;
  1146. } catch(NumberFormatException ex) {
  1147.     ex.printStackTrace();
  1148. }
  1149.     }
  1150. }
  1151. //System.err.println();
  1152. int msgs[]=new int[j];
  1153. for(int i=0;i<j;i++) {
  1154.     msgs[i]=_msgs[i];
  1155. }
  1156. return msgs;
  1157.     }
  1158.     public void expungeFolders() {
  1159. if(need_expunge_folders != null) {
  1160.     Enumeration enum=need_expunge_folders.elements();
  1161.     while(enum.hasMoreElements()) {
  1162. String hash=(String)enum.nextElement();
  1163. //System.err.println("Expunging folder: "+hash);
  1164. if(user.wantsSetFlags()) {
  1165.     Folder f=getFolder(hash);
  1166.     try {
  1167. //System.err.println("URL: "+f.getURLName());
  1168. if(f.isOpen()) {
  1169.     //System.err.println("Folder is already open ... ");
  1170.     f.close(false);
  1171. }
  1172. f.open(Folder.READ_WRITE);
  1173. f.expunge();
  1174. f.close(true);
  1175.     } catch(MessagingException ex) {
  1176. // XXXX
  1177. ex.printStackTrace();
  1178.     }
  1179. }
  1180.     }
  1181. }
  1182.     }
  1183.     /**
  1184.        Change the Flags of the messages the user selected.
  1185.  
  1186.     */
  1187.     public void setFlags(String folderhash, HTTPRequestHeader head) throws MessagingException {
  1188. if(head.isContentSet("copymovemsgs") && head.getContent("COPYMOVE").equals("COPY")) {
  1189.     copyMoveMessage(folderhash,head.getContent("TO"),head,false);
  1190. } else if(head.isContentSet("copymovemsgs") && head.getContent("COPYMOVE").equals("MOVE")) {
  1191.     copyMoveMessage(folderhash,head.getContent("TO"),head,true);
  1192. } else if(head.isContentSet("flagmsgs")) {
  1193.     System.err.println("setting message flags");
  1194.     Folder folder=getFolder(folderhash);
  1195.     //System.err.println("Processing Request Header...");
  1196.     /* Get selected messages */
  1197.     int msgs[]=getSelectedMessages(head,folder.getMessageCount());
  1198.     //System.err.println(" - get flags...");
  1199.     /* Get selected flags */
  1200.     Flags fl=new Flags(Flags.Flag.USER);
  1201.     if(head.getContent("MESSAGE FLAG").equals("DELETED")) {
  1202. fl=new Flags(Flags.Flag.DELETED);
  1203. if(need_expunge_folders == null) {
  1204.     need_expunge_folders=new Vector();
  1205. }
  1206. need_expunge_folders.addElement(folderhash);
  1207.     } else if(head.getContent("MESSAGE FLAG").equals("SEEN")) {
  1208. fl=new Flags(Flags.Flag.SEEN);
  1209.     } else if(head.getContent("MESSAGE FLAG").equals("RECENT")) {
  1210. fl=new Flags(Flags.Flag.RECENT);
  1211.     } else if(head.getContent("MESSAGE FLAG").equals("ANSWERED")) {
  1212. fl=new Flags(Flags.Flag.ANSWERED);
  1213.     } else if(head.getContent("MESSAGE FLAG").equals("DRAFT")) {
  1214. fl=new Flags(Flags.Flag.DRAFT);
  1215.     }
  1216.     boolean value=true;
  1217.     if(head.getContent("MARK").equals("UNMARK")) {
  1218. value=false;
  1219.     }
  1220.     //System.err.println("Done!");
  1221.     //System.err.println("Setting flags...");
  1222.     if(user.wantsSetFlags()) {
  1223. if(folder.isOpen() && folder.getMode()==Folder.READ_ONLY) {
  1224.     folder.close(false);
  1225.     folder.open(Folder.READ_WRITE);
  1226. } else if(!folder.isOpen()) {
  1227.     folder.open(Folder.READ_WRITE);
  1228. }
  1229. folder.setFlags(msgs,fl,value);
  1230. if(user.getBoolVar("autoexpunge")) {
  1231.     folder.close(true);
  1232.     if(need_expunge_folders != null) {
  1233. need_expunge_folders.removeElement(folderhash);
  1234.     }
  1235. } else {
  1236.     folder.close(false);
  1237. }
  1238.     }
  1239.     refreshFolderInformation(folderhash);
  1240.     
  1241. }
  1242.     }
  1243.     /**
  1244.      * Copy or move the selected messages from folder fromfolder to folder tofolder.
  1245.      */
  1246.     public void copyMoveMessage(String fromfolder, String tofolder, HTTPRequestHeader head, boolean move) throws MessagingException {
  1247. Folder from=getFolder(fromfolder);
  1248. Folder to=getFolder(tofolder);
  1249. if(user.wantsSetFlags()) {
  1250.     if(from.isOpen() && from.getMode() == Folder.READ_ONLY) {
  1251. from.close(false);
  1252. from.open(Folder.READ_WRITE);
  1253.     } else if(!from.isOpen()) {
  1254. from.open(Folder.READ_WRITE);
  1255.     }
  1256.     if(to.isOpen() && to.getMode() == Folder.READ_ONLY) {
  1257. to.close(false);
  1258. to.open(Folder.READ_WRITE);
  1259.     } else if(!to.isOpen()) {
  1260. to.open(Folder.READ_WRITE);
  1261.     }
  1262. } else {
  1263.     if(!from.isOpen()) {
  1264. from.open(Folder.READ_ONLY);
  1265.     }
  1266.     if(to.isOpen() && to.getMode() == Folder.READ_ONLY) {
  1267. to.close(false);
  1268. to.open(Folder.READ_WRITE);
  1269.     } else if(!to.isOpen()) {
  1270. to.open(Folder.READ_WRITE);
  1271.     }
  1272. }
  1273. int m[]=getSelectedMessages(head,from.getMessageCount());
  1274. Message msgs[]=from.getMessages(m);
  1275. from.copyMessages(msgs,to);
  1276. if(move && user.wantsSetFlags()) {
  1277.     from.setFlags(m,new Flags(Flags.Flag.DELETED),true);
  1278.     if(user.getBoolVar("autoexpunge")) {
  1279. from.close(true);     
  1280. to.close(true);
  1281.     } else {
  1282. if(need_expunge_folders == null) {
  1283.     need_expunge_folders=new Vector();
  1284. }
  1285. need_expunge_folders.addElement(fromfolder);
  1286. from.close(false);
  1287. to.close(false);
  1288.     }
  1289. } else {
  1290.     from.close(false);
  1291.     if(user.getBoolVar("autoexpunge")) {
  1292. to.close(true);
  1293.     } else {
  1294. to.close(false);
  1295.     }
  1296. }
  1297. refreshFolderInformation(fromfolder);
  1298. refreshFolderInformation(tofolder);
  1299.     }
  1300.     public void changeSetup(HTTPRequestHeader head) throws WebMailException {
  1301. Enumeration contentkeys=head.getContentKeys();
  1302. user.resetBoolVars();
  1303. while(contentkeys.hasMoreElements()) {
  1304.     String key=((String)contentkeys.nextElement()).toLowerCase();
  1305.     if(key.startsWith("intvar")) {
  1306. try {
  1307.     long value=Long.parseLong(head.getContent(key));
  1308.     user.setIntVar(key.substring(7),value);
  1309. } catch(NumberFormatException ex) {
  1310.     System.err.println("Warning: Remote provided illegal intvar in request header: n("+key+","+head.getContent(key)+")");
  1311. }
  1312.     } else if(key.startsWith("boolvar")) {
  1313. boolean value=head.getContent(key).toUpperCase().equals("ON");
  1314. user.setBoolVar(key.substring(8),value);
  1315.     }
  1316. }
  1317. user.setSignature(head.getContent("SIGNATURE"));
  1318. user.setFullName(head.getContent("FULLNAME"));
  1319. user.setEmail(head.getContent("EMAIL"));
  1320. if(!head.getContent("PASSWORD").equals("")) {
  1321.     net.wastl.webmail.server.Authenticator auth=parent.getStorage().getAuthenticator();
  1322.     if(auth.canChangePassword()) {
  1323. auth.changePassword(user,head.getContent("PASSWORD"),head.getContent("VERIFY"));
  1324.     } else {
  1325. throw new InvalidDataException(getStringResource("EX NO CHANGE PASSWORD"));
  1326.     }
  1327. }
  1328. user.setPreferredLocale(head.getContent("LANGUAGE"));
  1329. if(head.isContentSet("SENTFOLDER")) {
  1330.     System.err.println("SENTFOLDER="+head.getContent("SENTFOLDER"));
  1331.     user.setSentFolder(head.getContent("SENTFOLDER"));
  1332. }
  1333. refreshFolderInformation();
  1334. setEnv();
  1335. model.update();
  1336.     }
  1337.     
  1338.     public void addMailbox(String name, String protocol, String host, String login, String password) {
  1339. disconnectAll();
  1340. String host_url=protocol+"://"+host;
  1341. user.addMailHost(name,
  1342.  host_url,
  1343.  login,
  1344.  password);
  1345. model.update();
  1346. connectAll();
  1347. refreshFolderInformation();
  1348.     }
  1349.     public void removeMailbox(String id) {
  1350. disconnectAll();
  1351. user.removeMailHost(id);
  1352. model.update();
  1353. connectAll();
  1354.     }
  1355.     public void setAddToFolder(String id) {
  1356. model.setStateVar("add to folder",id);
  1357.     }
  1358.     public void addFolder(String toid, String name, boolean holds_messages, boolean holds_folders) 
  1359. throws MessagingException {
  1360. Folder parent=getFolder(toid);
  1361. Folder folder=parent.getFolder(name);
  1362. if(!folder.exists()) {
  1363.     int type=0;
  1364.     if(holds_messages) {
  1365. type+=Folder.HOLDS_MESSAGES;
  1366.     }
  1367.     if(holds_folders) {
  1368. type+=Folder.HOLDS_FOLDERS;
  1369.     }
  1370.     folder.create(type);
  1371. }
  1372. refreshFolderInformation();
  1373.     }
  1374.     
  1375.     public void removeFolder(String id, boolean recurse) throws MessagingException {
  1376. Folder folder=getFolder(id);
  1377. folder.close(false);
  1378. folder.delete(recurse);
  1379. refreshFolderInformation();
  1380.     }
  1381.     public String getEnv(String key) {
  1382. return "";
  1383.     }
  1384.     public void setEnv(String key, String value) {
  1385.     }
  1386.     public void setEnv() {
  1387. // This will soon replace "ENV":
  1388. model.setStateVar("base uri",parent.getBasePath());
  1389. model.setStateVar("img base uri",parent.getImageBasePath());
  1390. model.setStateVar("webmail version",parent.getVersion());
  1391. model.setStateVar("operating system",System.getProperty("os.name")+" "+
  1392.   System.getProperty("os.version")+"/"+System.getProperty("os.arch"));
  1393. model.setStateVar("java virtual machine",System.getProperty("java.vendor")+" "+
  1394.   System.getProperty("java.vm.name")+" "+System.getProperty("java.version"));
  1395. model.setStateVar("last login",user.getLastLogin());
  1396. model.setStateVar("first login",user.getFirstLogin());
  1397. model.setStateVar("session id",session_code);
  1398. model.setStateVar("date",formatDate(System.currentTimeMillis()));
  1399. model.setStateVar("max attach size",parent.getStorage().getConfig("MAX ATTACH SIZE"));
  1400. model.setStateVar("current attach size",""+attachments_size);
  1401. // Add all languages to the state
  1402. model.removeAllStateVars("language");
  1403. String lang=parent.getConfig("languages");
  1404. StringTokenizer tok=new StringTokenizer(lang," ");
  1405. while(tok.hasMoreTokens()) {
  1406.     String t=tok.nextToken();
  1407.     model.addStateVar("language",t);
  1408. }
  1409. model.removeAllStateVars("protocol");
  1410. Provider[] stores=parent.getStoreProviders();
  1411. for(int i=0; i<stores.length; i++) {
  1412.     model.addStateVar("protocol",stores[i].getProtocol());
  1413. }
  1414.     }
  1415.     public UserData getUser() {
  1416. return user;
  1417.     }
  1418.     public String getUserName() {
  1419. return user.getLogin();
  1420.     }
  1421.     public InetAddress getRemoteAddress() {
  1422. return remote;
  1423.     }
  1424.     public Hashtable getActiveConnections() {
  1425. return connections;
  1426.     }
  1427.     public void setSent(boolean b) {
  1428. sent=b;
  1429.     }
  1430.     public boolean isSent() {
  1431. return sent;
  1432.     }
  1433.     private String formatDate(long date) {
  1434. TimeZone tz=TimeZone.getDefault();
  1435. DateFormat df=DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.DEFAULT, getLocale());
  1436. df.setTimeZone(tz);
  1437. String now=df.format(new Date(date));
  1438. return now;
  1439.     }
  1440.     public void handleTransportException(SendFailedException e) {
  1441. model.setStateVar("send status",e.getNextException().getMessage());
  1442. model.setStateVar("valid sent addresses",Helper.joinAddress(e.getValidSentAddresses()));
  1443. model.setStateVar("valid unsent addresses",Helper.joinAddress(e.getValidUnsentAddresses()));
  1444. model.setStateVar("invalid addresses",Helper.joinAddress(e.getInvalidAddresses()));
  1445. sent=true;
  1446.     }
  1447. } // WebMailSession