001: /***
002: * jwma Java WebMail
003: * Copyright (c) 2000-2003 jwma team
004: *
005: * jwma is free software; you can distribute and use this source
006: * under the terms of the BSD-style license received along with
007: * the distribution.
008: ***/package dtw.webmail;
009:
010: import java.io.*;
011: import java.util.*;
012: import javax.servlet.*;
013: import javax.servlet.http.*;
014: import javax.mail.*;
015: import javax.mail.internet.*;
016:
017: import org.apache.log4j.Logger;
018: import org.apache.log4j.NDC;
019:
020: import dtw.webmail.model.*;
021: import dtw.webmail.util.*;
022: import dtw.webmail.plugin.RandomAppendPlugin;
023:
024: /**
025: * Class extending the <tt>HttpServlet</tt> to implement
026: * the SendMail controller of jwma.
027: * <p>
028: * Please see the related documentation for more detailed
029: * information on process flow and functionality.
030: *
031: * @author Dieter Wimberger
032: * @version 0.9.7 07/02/2003
033: */
034: public class JwmaSendMail extends HttpServlet {
035:
036: //logging
037: private static Logger log = Logger.getLogger(JwmaSendMail.class);
038:
039: /**
040: * Initializes the servlet when it is loaded by the
041: * servlet engine.
042: * <p>This implementation just calls its superclass
043: * implementation.
044: *
045: * @throws ServletException if initialization fails.
046: */
047:
048: public void init(ServletConfig config) throws ServletException {
049:
050: super .init(config);
051: }//init
052:
053: /**
054: * Handles the incoming requests.
055: * <p>
056: * This implementation ensures authenticated session
057: * existence and just calls the only supported action
058: * (sendmessage).
059: * <p>
060: * <i><b>Note:</b>incoming data should be <tt>multipart/form-data</tt>
061: * encoded.</i>
062: * <p>
063: * Application related errors/exceptions are handled internally
064: * by forwarding the request to an error page (or maybe inlining
065: * error information somewhen).
066: *
067: * @param req a reference to the actual <tt>HttpServletRequest</tt>
068: * instance.
069: * @param res a reference to the actual <tt>HttpServletResponse</tt>
070: * instance.
071: *
072: * @throws ServletException if servlet related operations fail.
073: * @throws IOException if i/o operations fail.
074: */
075: public void service(HttpServletRequest req, HttpServletResponse res)
076: throws ServletException, IOException {
077: try {
078: //prepare NDC with client host name
079: NDC.push(req.getRemoteHost());
080: //1. Handle websession
081: HttpSession websession = req.getSession(true);
082: Object o = websession.getValue("jwma.session");
083:
084: //2. Ensure valid jwma session
085: JwmaSession session = ((o == null) ? new JwmaSession(
086: websession) : ((JwmaSession) o));
087: session.setRequest(req);
088: session.setResponse(res);
089: session.setWebSession(websession);
090:
091: //3. Redirect invalid web sessions
092: if (!session.isValidWebSession()) {
093: session.redirect(JwmaKernel.LOGIN_VIEW);
094: } else {
095: //dispatch send message
096: try {
097: //ensure authenticated jwma session
098: session.ensureAuthenticated();
099: doSendMessage(session);
100: } catch (JwmaException ex) {
101: //1. if not authenticated forward to login page
102: if (!session.isAuthenticated()) {
103: session.redirect(JwmaKernel.LOGIN_VIEW);
104: } else {
105: String message = ex.getMessage();
106: if (message.equals("stacked")) {
107: message = JwmaKernel.getReference()
108: .getLogMessage("jwma.usererror");
109: for (Iterator iter = ex.iterator(); iter
110: .hasNext();) {
111: message += "::"
112: + JwmaKernel
113: .getReference()
114: .getErrorMessage(
115: (String) iter
116: .next());
117: }
118: } else {
119: //oneliner resolving of key to message in
120: message = JwmaKernel.getReference()
121: .getLogMessage("jwma.usererror")
122: + JwmaKernel.getReference()
123: .getErrorMessage(message);
124: //log exception with description and trace if inlined exception
125: //available, else with stacktrace.
126: }
127: if (ex.hasException()) {
128: log.error(message, ex.getException());
129: } else {
130: log.error(message, ex);
131: }
132: //store error
133: session.storeBean("jwma.error", ex);
134: //redirect last view with inlined error or
135: //to error page.
136: if (ex.isInlineError()) {
137: session.redirectToActual();
138: } else {
139: session.redirect(JwmaKernel.ERROR_VIEW);
140: }
141: }
142: }
143: }
144: } finally {
145: NDC.pop();
146: }
147: }//service
148:
149: /**
150: * Handles the multipart/form-data request and sends the message.
151: */
152: private void doSendMessage(JwmaSession session)
153: throws JwmaException {
154:
155: boolean to_missing = false;
156: boolean receivers_invalid = false;
157: boolean subject_invalid = false;
158: boolean body_invalid = false;
159: boolean content_missing = false;
160: boolean signature_selection_invalid = false;
161: StringBuffer tobuf = new StringBuffer(50);
162:
163: MultipartRequest multi = null;
164:
165: try {
166: //JwmaKernel.getReference().debugLog().write("Going to parse Multipart request..");
167:
168: //retrieve contacts database
169: JwmaContactsImpl ctdb = (JwmaContactsImpl) session
170: .getWebSession().getValue("jwma.contacts");
171:
172: //Handle the incoming request
173: multi = new MultipartRequest(session.getRequest(), session
174: .getMTA().getTransportLimit());
175:
176: boolean savedraft = multi.getParameter("savedraft").equals(
177: "true");
178: //retrieve all necessary parameters into local strings
179: //recipients
180: String to = multi.getParameter("to");
181: String cc = multi.getParameter("ccto");
182: String bcc = multi.getParameter("bccto");
183: //subject
184: String subject = multi.getParameter("subject");
185: //sign
186: boolean toggleautosign = new Boolean(multi
187: .getParameter("toggleautosign")).booleanValue();
188: boolean togglerndappend = new Boolean(multi
189: .getParameter("togglerndappend")).booleanValue();
190:
191: //body
192: String body = multi.getParameter("body");
193: //mail identity changed?
194: String mailid = multi.getParameter("mailidentity");
195:
196: //Get the preferences from the userdata
197: JwmaPreferencesImpl prefs = session.getPreferences();
198:
199: RandomAppendPlugin rap = JwmaKernel.getReference()
200: .getRandomAppendPlugin();
201:
202: //check out the message itself
203: JwmaComposeMessage message = (JwmaComposeMessage) session
204: .retrieveBean("jwma.composemessage");
205:
206: //open draft message
207: if (savedraft) {
208: message.openDraft(session.getJwmaStore());
209: }
210:
211: //get the mail identity
212: JwmaMailIdentity mailidentity = prefs
213: .getMailIdentity(mailid);
214:
215: //set sender identity
216: String from = mailidentity.getFrom();
217: if (from == null || from.length() == 0) {
218: //set default
219: tobuf.delete(0, tobuf.length());
220: from = tobuf.append(prefs.getFirstname()).append(" ")
221: .append(prefs.getLastname()).append(" <")
222: .append(prefs.getUserIdentity()).append(">")
223: .toString();
224:
225: //from = prefs.getFirstname() + " " + prefs.getLastname() +
226: // " <" + prefs.getUserIdentity() + ">";
227: } else {
228: //format it nicely
229: tobuf.delete(0, tobuf.length());
230: from = tobuf.append(prefs.getFirstname()).append(" ")
231: .append(prefs.getLastname()).append(" <")
232: .append(from).append(">").toString();
233:
234: // from = prefs.getFirstname() + " " + prefs.getLastname() +
235: // " <" + from + ">";
236: }
237: message.setFrom(from);
238:
239: //Set a Reply-To Address
240: String repto = mailidentity.getReplyTo();
241: if (repto != null && !repto.equals("")) {
242: //format it nicely
243: tobuf.delete(0, tobuf.length());
244: repto = tobuf.append(prefs.getFirstname()).append(" ")
245: .append(prefs.getLastname()).append(" <")
246: .append(repto).append(">").toString();
247:
248: //repto = prefs.getFirstname() + " " + prefs.getLastname() +
249: // " <" + repto + ">";
250: message.setReplyTo(repto);
251: }
252:
253: //Set all recipients
254: try {
255: if (to != null && !to.trim().equals("")) {
256: if (isNickname(to)) {
257: to = to.substring(1, to.length());
258: String[] nicks = StringUtil.split(to, ",");
259: if (nicks.length > 1) {
260: message.setInvisibleToList(true);
261: }
262: tobuf.delete(0, tobuf.length());
263: for (int i = 0; i < nicks.length; i++) {
264: JwmaContact ct = ctdb
265: .getContactByNickname(nicks[i]);
266: tobuf.append(ct.getFirstname()).append(" ")
267: .append(ct.getLastname()).append(
268: " <").append(ct.getEmail())
269: .append(">");
270: if (i < (nicks.length - 1)) {
271: tobuf.append(",");
272: }
273: }
274: to = tobuf.toString();
275: } else if (isGroup(to)) {
276: if (isInvisibleGroup(to)) {
277: message.setInvisibleToList(true);
278: to = to.substring(0, to.length() - 2);
279: } else {
280: to = to.substring(0, to.length() - 3);
281: }
282: if (ctdb.containsContactGroupName(to)) {
283: JwmaContact[] cts = ctdb
284: .getContactGroupByName(to)
285: .listContacts();
286: tobuf.delete(0, tobuf.length());
287: for (int i = 0; i < cts.length; i++) {
288: tobuf.append(cts[i].getFirstname())
289: .append(" ").append(
290: cts[i].getLastname())
291: .append(" <").append(
292: cts[i].getEmail())
293: .append(">");
294: if (i < (cts.length - 1)) {
295: tobuf.append(",");
296: }
297: }
298: to = tobuf.toString();
299: }
300: }
301: message.setTo(to);
302: } else {
303: //flag error
304: to_missing = true;
305: }
306: if (cc != null) {
307: message.setCCTo(cc);
308: }
309: if (bcc != null) {
310: message.setBCCTo(bcc);
311: }
312:
313: } catch (MessagingException mex) {
314: log.debug("Setting receivers:", mex);
315: receivers_invalid = true;
316: }
317:
318: //Set subject
319: if (subject != null) {
320: message.setSubject(subject);
321:
322: } else {
323: subject_invalid = true;
324: }
325: if (body != null) {
326: message.setBody(body);
327: } else {
328: body_invalid = true;
329: }
330: if ((body != null && body.length() == 0)
331: && (subject != null && subject.length() == 0)) {
332: content_missing = true;
333: }
334:
335: //Handle attachments
336: if (multi.hasAttachments()) {
337: message.addAttachments(multi.getAttachments());
338: }
339:
340: if (to_missing || receivers_invalid || subject_invalid
341: || body_invalid || content_missing) {
342:
343: JwmaException jex = new JwmaException("stacked");
344:
345: if (to_missing) {
346: jex.addDescription("sm.receivers.tomissing");
347: }
348: if (receivers_invalid) {
349: jex.addDescription("sm.receivers.invalid");
350: }
351: if (subject_invalid) {
352: jex.addDescription("sm.subject.missing");
353: }
354: if (body_invalid) {
355: jex.addDescription("sm.body.missing");
356: }
357: if (content_missing) {
358: jex.addDescription("sm.content.missing");
359: }
360: throw jex;
361: }
362:
363: if (mailidentity.isAutoSigning() != toggleautosign) {
364: String signature = mailidentity.getSignature();
365: if (signature != null && signature.length() > 0) {
366: signature = "\n" + signature;
367: message.appendBody(signature);
368: }
369: }
370:
371: //Allow saving of message draft
372: if (savedraft) {
373: message.closeDraft(session.getJwmaStore());
374: } else {
375:
376: //Handle random append
377: if (rap != null
378: && mailidentity.isRandomAppendAllowed() != togglerndappend) {
379: message.appendBody(rap.getRandomAppend(mailidentity
380: .getRandomAppendType(), prefs.getLocale()));
381: }
382:
383: //Now send message
384: message.send(session);
385:
386: //Archive message if necessary
387: session.getJwmaStore().archiveSentMail(
388: message.getMessage());
389: }
390:
391: //a little bit of magic :)
392: session.redirectToLast();
393:
394: } catch (IOException ioex) {
395: throw new JwmaException("sm.attachments.gatheringfailed")
396: .setException(ioex);
397: } finally {
398: //schedule a gc, if there were attachments, could free
399: //up to max allowed transport size of mem!
400: if (multi != null && multi.hasAttachments()) {
401: System.gc();
402: }
403: }
404:
405: }//doSendMessage
406:
407: /**
408: * Converts a <tt>String</tt> into an <tt>int</tt>.
409: */
410: private int toInt(String number) throws JwmaException {
411: try {
412: return Integer.parseInt(number.trim());
413: } catch (Exception ex) {
414: throw new JwmaException("jwma.number.format");
415: }
416: }//toInt
417:
418: /**
419: * Tests if an incoming to address is
420: * actually nickname.
421: *
422: * @param true if nickname, false otherwise;
423: */
424: private boolean isNickname(String to) {
425: return (to.startsWith("@"));
426: }//isNickname
427:
428: /**
429: * Tests if an incoming to address is
430: * actually a group.
431: *
432: * @param true if group, false otherwise.
433: */
434: private boolean isGroup(String to) {
435: return (to.endsWith(":;") || to.endsWith(":@;"));
436: }//isGroup
437:
438: /**
439: * Tests if an incoming to address is
440: * an invisible group.
441: *
442: * @param true if invisible group, false otherwise.
443: */
444: private boolean isInvisibleGroup(String to) {
445: return (to.endsWith(":;") && !to.endsWith(":@;"));
446: }//isInvisibleGroup
447:
448: /**
449: * Returns servlet info as <tt>String</tt>.
450: *
451: * @return Info about this servlet as <tt>String</tt>.
452: */
453: public String getServletInfo() {
454: return "jwma (Java WebMail) SendMail Servlet";
455: }//getServletInfo()
456:
457: }//JwmaSendMail
|