001: /* CVS ID: $Id: WebMailServlet.java,v 1.2 2002/10/04 21:23:37 wastl Exp $ */
002: package net.wastl.webmail.server;
003:
004: import java.net.*;
005: import java.io.*;
006: import java.util.*;
007: import java.lang.reflect.*;
008:
009: import javax.mail.Session;
010: import javax.mail.Provider;
011: import net.wastl.webmail.server.http.*;
012: import net.wastl.webmail.ui.*;
013: import net.wastl.webmail.ui.html.*;
014: import net.wastl.webmail.exceptions.*;
015: import net.wastl.webmail.misc.ByteStore;
016:
017: import javax.servlet.*;
018: import javax.servlet.http.*;
019:
020: import com.oreilly.servlet.multipart.*;
021:
022: /*
023: * WebMailServer.java
024: *
025: * Created: Oct 1999
026: *
027: * Copyright (C) 1999-2000 Sebastian Schaffert
028: *
029: * This program is free software; you can redistribute it and/or
030: * modify it under the terms of the GNU General Public License
031: * as published by the Free Software Foundation; either version 2
032: * of the License, or (at your option) any later version.
033: *
034: * This program is distributed in the hope that it will be useful,
035: * but WITHOUT ANY WARRANTY; without even the implied warranty of
036: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
037: * GNU General Public License for more details.
038: *
039: * You should have received a copy of the GNU General Public License
040: * along with this program; if not, write to the Free Software
041: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
042: */
043:
044: /**
045: * This is WebMails main server. From here most parts will be administered.
046: * This is the servlet implementation of WebMail (introduced in 0.6.1)
047: *
048: * Created: Tue Feb 2 12:07:25 1999
049: *
050: * @author Sebastian Schaffert
051: * @version $Revision: 1.2 $
052: */
053:
054: /* devink 7/15/2000 - service() now handles a TwoPassAuthenticationException,
055: * as does newSession()
056: * devink 9/24/2000 - remove TwoPassAuthenticationException stuff
057: */
058: public class WebMailServlet extends WebMailServer implements Servlet {
059:
060: ServletConfig srvlt_config;
061:
062: /** Size of the chunks that are sent. Must not be greater than 65536 */
063: private static final int chunk_size = 8192;
064:
065: protected String basepath;
066: protected String imgbase;
067:
068: public WebMailServlet() {
069: }
070:
071: public void init(ServletConfig config) throws ServletException {
072: System.err.println("Init");
073: srvlt_config=config;
074: this .config=new Properties();
075: Enumeration enum=config.getInitParameterNames();
076: while(enum.hasMoreElements()) {
077: String s=(String)enum.nextElement();
078: this .config.put(s,config.getInitParameter(s));
079: System.err.println(s+": "+config.getInitParameter(s));
080: }
081:
082: /*
083: * Issue a warning if webmail.basepath and/or webmail.imagebase are
084: * not set.
085: */
086: if(config.getInitParameter("webmail.basepath")==null) {
087: config.getServletContext().log("Warning: webmail.basepath initArg should be set to the WebMail Servlet's base path");
088: basepath="";
089: } else {
090: basepath = config.getInitParameter("webmail.basepath");
091: }
092: if(config.getInitParameter("webmail.imagebase") == null) {
093: config.getServletContext().log("Error: webmail.basepath initArg should be set to the WebMail Servlet's base path");
094: imgbase="";
095: } else {
096: imgbase = config.getInitParameter("webmail.imagebase");
097: }
098:
099: /*
100: * Try to get the pathnames from the URL's if no path was given
101: * in the initargs.
102: */
103: if(config.getInitParameter("webmail.data.path") == null) {
104: this .config.put("webmail.data.path",getServletContext().getRealPath("/data"));
105: }
106: if(config.getInitParameter("webmail.lib.path") == null) {
107: this .config.put("webmail.lib.path",getServletContext().getRealPath("/lib"));
108: }
109: if(config.getInitParameter("webmail.template.path") == null) {
110: this .config.put("webmail.template.path",getServletContext().getRealPath("/lib/templates"));
111: }
112: if(config.getInitParameter("webmail.xml.path") == null) {
113: this .config.put("webmail.xml.path",getServletContext().getRealPath("/lib/xml"));
114: }
115: if(config.getInitParameter("webmail.log.facility") == null) {
116: this .config.put("webmail.xml.path","net.wastl.webmail.logger.ServletLogger");
117: }
118:
119: /*
120: * Call the WebMailServer's initialization method
121: * and forward all Exceptions to the ServletServer
122: */
123: try {
124: doInit();
125: } catch(Exception e) {
126: e.printStackTrace();
127: throw new ServletException("Could not intialize: "+e.getMessage(),e);
128: }
129:
130: }
131:
132: public void debugOut(String msg, Exception ex) {
133: if (getDebug()) {
134: srvlt_config.getServletContext().log(msg, ex);
135: }
136: }
137:
138: public ServletConfig getServletConfig() {
139: return srvlt_config;
140: }
141:
142: public ServletContext getServletContext() {
143: return srvlt_config.getServletContext();
144: }
145:
146: public String getServletInfo() {
147: return getVersion()
148: + "\n(c)2000 by Sebastian Schaffert\nThis software is distributed under the GNU General Public License (GPL)";
149: }
150:
151: public void destroy() {
152: shutdown();
153: }
154:
155: /**
156: * Handle a request to the WebMail servlet.
157: *
158: * This is the central method of the WebMailServlet. It first gathers all of the necessary information
159: * from the client, then either creates or gets a Session and executes the URL handler for the given
160: * path.
161: */
162: public void service(ServletRequest req1, ServletResponse res1) throws ServletException {
163: HttpServletRequest req=(HttpServletRequest)req1;
164: HttpServletResponse res=(HttpServletResponse)res1;
165:
166: HTTPRequestHeader http_header=new HTTPRequestHeader();
167:
168: Enumeration en=req.getHeaderNames();
169: while(en.hasMoreElements()) {
170: String s=(String)en.nextElement();
171: http_header.setHeader(s,req.getHeader(s));
172: }
173:
174:
175: if(req.getPathInfo()!=null) {
176: http_header.setPath(req.getPathInfo());
177: } else {
178: http_header.setPath("/");
179: }
180:
181:
182: InetAddress addr;
183: try {
184: addr=InetAddress.getByName(req.getRemoteHost());
185: } catch(UnknownHostException e) {
186: try {
187: addr=InetAddress.getByName(req.getRemoteAddr());
188: } catch(Exception ex) {
189: throw new ServletException("Remote host must identify!");
190: }
191: }
192:
193:
194: HTMLDocument content=null;
195: int err_code=400;
196: HTTPSession sess=null;
197:
198: /* Here we try to parse the MIME content that the Client sent in his POST
199: since the JServ doesn't do that for us:-(
200: At least we can use the functionality provided by the standalone server where we need to parse
201: the content ourself anyway.
202: */
203: try {
204:
205: BufferedOutputStream out=new BufferedOutputStream(res.getOutputStream());
206:
207:
208: /* First we try to use the Servlet API's methods to parse the parameters.
209: Unfortunately, it doesn't know how to handle MIME multipart POSTs, so
210: we will have to handle that ourselves */
211:
212:
213: /* First get all the parameters and set their values into http_header */
214: Enumeration enum2=req.getParameterNames();
215: while(enum2.hasMoreElements()) {
216: String s=(String)enum2.nextElement();
217: http_header.setContent(s,req.getParameter(s));
218: getStorage().log(Storage.LOG_INFO,"Parameter "+s);
219: }
220:
221: /* Then we set all the headers in http_header */
222: enum2=req.getHeaderNames();
223: while(enum2.hasMoreElements()) {
224: String s=(String)enum2.nextElement();
225: http_header.setHeader(s,req.getHeader(s));
226: }
227:
228: /* In Servlet API 2.2 we might want to fetch the attributes also, but this doesn't work
229: in API 2.0, so we leave it commented out */
230:// enum2=req.getAttributeNames();
231:// while(enum2.hasMoreElements()) {
232:// String s=(String)enum2.nextElement();
233:// getStorage().log(Storage.LOG_INFO,"Attribute "+s);
234:// }
235:
236:
237: /* Now let's try to handle multipart/form-data posts */
238:
239: if(req.getContentType() != null &&
240: req.getContentType().toUpperCase().startsWith("MULTIPART/FORM-DATA")) {
241:
242:
243: int size=Integer.parseInt(WebMailServer.getStorage().getConfig("max attach size"));
244: MultipartParser mparser = new MultipartParser(req,size);
245: Part p;
246: while((p = mparser.readNextPart()) != null) {
247: if(p.isFile()) {
248: ByteStore bs = ByteStore.getBinaryFromIS(((FilePart)p).getInputStream(),
249: size);
250: bs.setName(((FilePart)p).getFileName());
251: bs.setContentType(getStorage().getMimeType(((FilePart)p).getFileName()));
252: http_header.setContent(p.getName(),bs);
253: getStorage().log(Storage.LOG_INFO,"File name "+bs.getName());
254: getStorage().log(Storage.LOG_INFO,"Type "+bs.getContentType());
255:
256: } else if(p.isParam()) {
257: http_header.setContent(p.getName(),((ParamPart)p).getStringValue());
258: }
259:
260: getStorage().log(Storage.LOG_INFO,"Parameter "+p.getName());
261: }
262:
263: }
264:
265:
266: try {
267: String url=http_header.getPath();
268:
269:
270: try {
271: /* Find out about the session id */
272: sess=req.getSession(false)==null?null:(HTTPSession)req.getSession(false).getAttribute("webmail.session");
273: /* If the user was logging on, he doesn't have a session id, so generate one.
274: If he already had one, all the better, we will take the old one */
275: if(sess == null && url.startsWith("/login")) {
276: sess=newSession(req,http_header);
277: } else if(sess == null && url.startsWith("/admin/login")) {
278: http_header.setHeader("LOGIN","Administrator");
279: sess=newAdminSession(req,http_header);
280: }
281:
282: /* Ensure that the session state is up-to-date */
283: if(sess != null) sess.setEnv();
284:
285: /* Let the URLHandler determine the result of the query */
286: content=getURLHandler().handleURL(url,sess,http_header);
287:
288: } catch(InvalidPasswordException e) {
289: getStorage().log(Storage.LOG_ERR,"Connection to "+addr.toString()+
290: ": Authentication failed!");
291: if(url.startsWith("/admin/login")) {
292: content=getURLHandler().handleURL("/admin",null,http_header);
293: } else if(url.startsWith("/login")) {
294: content=getURLHandler().handleURL("/",null,http_header);
295: } else {
296: //content=new HTMLErrorMessage(getStorage(),e.getMessage());
297: throw new ServletException("Invalid URL called!");
298: }
299: } catch(Exception ex) {
300: ex.printStackTrace();
301: content=getURLHandler().handleException(ex,sess,http_header);
302: debugOut("Some strange error while handling request",ex);
303: }
304:
305: /* Set some HTTP headers: Date is now, the document should expire in 5 minutes,
306: proxies and clients shouldn't cache it and all WebMail documents must be revalidated
307: when they think they don't have to follow the "no-cache". */
308: res.setDateHeader("Date:",System.currentTimeMillis());
309: res.setDateHeader("Expires",System.currentTimeMillis()+300000);
310: res.setHeader("Pragma","no-cache");
311: res.setHeader("Cache-Control","must-revalidate");
312:
313:
314: synchronized(out) {
315: res.setStatus(content.getReturnCode());
316:
317: if(content.hasHTTPHeader()) {
318: Enumeration enum=content.getHTTPHeaderKeys();
319: while(enum.hasMoreElements()) {
320: String s=(String)enum.nextElement();
321: res.setHeader(s,content.getHTTPHeader(s));
322: }
323: }
324:
325: /* What we will send is an image or some other sort of binary */
326: if(content instanceof HTMLImage) {
327: HTMLImage img=(HTMLImage)content;
328: /* the HTMLImage class provides us with most of the necessary information
329: that we want to send */
330: res.setHeader("Content-Type",img.getContentType());
331: res.setHeader("Content-Transfer-Encoding",img.getContentEncoding());
332: res.setHeader("Content-Length",""+img.size());
333: res.setHeader("Connection","Keep-Alive");
334:
335: /* Send 8k junks */
336: int offset=0;
337: while(offset + chunk_size < img.size()) {
338: out.write(img.toBinary(),offset,chunk_size);
339: offset+=chunk_size;
340: }
341: out.write(img.toBinary(),offset,img.size()-offset);
342: out.flush();
343:
344: out.close();
345: } else {
346: byte[] encoded_content=content.toString().getBytes("UTF-8");
347:
348: /* We are sending HTML text. Set the encoding to UTF-8 for Unicode messages */
349: res.setHeader("Content-Length",""+(encoded_content.length+2));
350: res.setHeader("Connection","Keep-Alive");
351: res.setHeader("Content-Type","text/html; charset=\"UTF-8\"");
352:
353: out.write(encoded_content);
354: out.write("\r\n".getBytes());
355:
356: out.flush();
357:
358: out.close();
359: }
360: }
361: } catch(DocumentNotFoundException e) {
362: getStorage().log(Storage.LOG_INFO,"Connection to "+addr.toString()+
363: ": Could not handle request ("+err_code+") (Reason: "+e.getMessage()+")");
364: throw new ServletException("Error: "+e.getMessage(),e);
365:// res.setStatus(err_code);
366:// res.setHeader("Content-type","text/html");
367:// res.setHeader("Connection","close");
368:
369:// content=new HTMLErrorMessage(getStorage(),e.getMessage());
370:// out.write((content+"\r\n").getBytes("UTF-8"));
371:// out.write("</HTML>\r\n".getBytes());
372:// out.flush();
373:// out.close();
374: }
375: } catch(Exception e) {
376: e.printStackTrace();
377: getStorage().log(Storage.LOG_INFO,"Connection to "+addr.toString()+" closed");
378: throw new ServletException("Error: "+e.getMessage(),e);
379: }
380: }
381:
382: /**
383: * Init possible servers of this main class
384: */
385: protected void initServers() {
386: }
387:
388: protected void shutdownServers() {
389: }
390:
391: public String getBasePath() {
392: return basepath;
393: }
394:
395: public String getImageBasePath() {
396: return imgbase;
397: }
398:
399: public WebMailSession newSession(HttpServletRequest req,
400: HTTPRequestHeader h) throws UserDataException,
401: InvalidPasswordException, WebMailException {
402: HttpSession sess = req.getSession(true);
403:
404: if (sess.getAttribute("webmail.session") == null) {
405: WebMailSession n = new WebMailSession(this , req, h);
406: timer.addTimeableConnection(n);
407: n.login();
408: sess.setAttribute("webmail.session", n);
409: sessions.put(sess.getId(), n);
410: debugOut("Created new Session: " + sess.getId());
411: return n;
412: } else {
413: Object tmp = sess.getAttribute("webmail.session");
414: if (tmp instanceof WebMailSession) {
415: WebMailSession n = (WebMailSession) tmp;
416: n.login();
417: debugOut("Using old Session: " + sess.getId());
418: return n;
419: } else {
420: /* If we have an admin session, get rid of it and create a new session */
421: sess.setAttribute("webmail.session", null);
422: debugOut("Reusing old AdminSession: " + sess.getId());
423: return newSession(req, h);
424: }
425: }
426: }
427:
428: public AdminSession newAdminSession(HttpServletRequest req,
429: HTTPRequestHeader h) throws InvalidPasswordException,
430: WebMailException {
431: HttpSession sess = req.getSession(true);
432:
433: if (sess.getAttribute("webmail.session") == null) {
434: AdminSession n = new AdminSession(this , req, h);
435: timer.addTimeableConnection(n);
436: n.login(h);
437: sess.setAttribute("webmail.session", n);
438: sessions.put(sess.getId(), n);
439: debugOut("Created new Session: " + sess.getId());
440: return n;
441: } else {
442: Object tmp = sess.getAttribute("webmail.session");
443: if (tmp instanceof AdminSession) {
444: AdminSession n = (AdminSession) tmp;
445: n.login(h);
446: debugOut("Using old Session: " + sess.getId());
447: return n;
448: } else {
449: sess.setAttribute("webmail.session", null);
450: debugOut("Reusing old UserSession: " + sess.getId());
451: return newAdminSession(req, h);
452: }
453: }
454: }
455:
456: /** Overwrite the old session handling methods */
457:
458: public WebMailSession newSession(InetAddress a, HTTPRequestHeader h)
459: throws InvalidPasswordException {
460: System.err.println("newSession invalid call");
461: return null;
462: }
463:
464: public AdminSession newAdminSession(InetAddress a,
465: HTTPRequestHeader h) throws InvalidPasswordException {
466: System.err.println("newAdminSession invalid call");
467: return null;
468: }
469:
470: public HTTPSession getSession(String sessionid, InetAddress a,
471: HTTPRequestHeader h) throws InvalidPasswordException {
472: System.err.println("getSession invalid call");
473: return null;
474: }
475:
476: public Enumeration getServers() {
477: return new Enumeration() {
478: public boolean hasMoreElements() {
479: return false;
480: }
481:
482: public Object nextElement() {
483: return null;
484: }
485: };
486: }
487:
488: public String toString() {
489: String s = "";
490: s += "Server: "
491: + srvlt_config.getServletContext().getServerInfo()
492: + "\n";
493: s += "Mount Point: " + getBasePath() + "\n";
494: s += getServletInfo();
495: return s;
496: }
497:
498: public Object getServer(String ID) {
499: return null;
500: }
501:
502: public void reinitServer(String ID) {
503: }
504:
505: public static String getVersion() {
506: return "WebMail/Java Servlet v" + VERSION;
507: }
508:
509: } // WebMailServer
|