001: /*
002: * Helma License Notice
003: *
004: * The contents of this file are subject to the Helma License
005: * Version 2.0 (the "License"). You may not use this file except in
006: * compliance with the License. A copy of the License is available at
007: * http://adele.helma.org/download/helma/license.txt
008: *
009: * Copyright 1998-2003 Helma Software. All Rights Reserved.
010: *
011: * $RCSfile$
012: * $Author: root $
013: * $Revision: 8604 $
014: * $Date: 2007-09-28 15:16:38 +0200 (Fre, 28 Sep 2007) $
015: */
016:
017: package helma.scripting.rhino.extensions;
018:
019: import helma.util.*;
020: import org.mozilla.javascript.*;
021: import java.io.*;
022: import java.util.*;
023: import java.lang.reflect.Member;
024: import java.lang.reflect.Method;
025: import javax.activation.*;
026: import javax.mail.Address;
027: import javax.mail.Message;
028: import javax.mail.Multipart;
029: import javax.mail.Session;
030: import javax.mail.Transport;
031: import javax.mail.internet.AddressException;
032: import javax.mail.internet.InternetAddress;
033: import javax.mail.internet.MimeBodyPart;
034: import javax.mail.internet.MimeMessage;
035: import javax.mail.internet.MimeMultipart;
036: import javax.mail.internet.MimeUtility;
037:
038: /**
039: * A JavaScript wrapper around a JavaMail message class to send
040: * mail via SMTP from Helma
041: */
042: public class MailObject extends ScriptableObject implements
043: Serializable {
044:
045: public static final int OK = 0;
046: public static final int SUBJECT = 10;
047: public static final int TEXT = 11;
048: public static final int MIMEPART = 12;
049: public static final int TO = 20;
050: public static final int CC = 21;
051: public static final int BCC = 22;
052: public static final int FROM = 23;
053: public static final int REPLYTO = 24;
054: public static final int SEND = 30;
055:
056: MimeMessage message;
057: Multipart multipart;
058: String multipartType = "mixed";
059: StringBuffer buffer;
060: int status;
061:
062: // these are only set on the prototype object
063: Session session = null;
064: Properties props = null;
065: String host = null;
066:
067: /**
068: * Creates a new Mail object.
069: */
070: MailObject(Session session) {
071: this .status = OK;
072: message = new MimeMessage(session);
073: }
074:
075: /**
076: * Creates a new MailObject prototype.
077: *
078: * @param mprops the Mail properties
079: */
080: MailObject(Properties mprops) {
081: this .status = OK;
082: this .props = mprops;
083: }
084:
085: /**
086: * Overrides abstract method in ScriptableObject
087: */
088: public String getClassName() {
089: return "Mail";
090: }
091:
092: /**
093: * Get the cached JavaMail session. This is similar to Session.getDefaultSession(),
094: * except that we check if the properties have changed.
095: */
096: protected Session getSession() {
097: if (props == null) {
098: throw new NullPointerException(
099: "getSession() called on non-prototype MailObject");
100: }
101:
102: // set the mail encoding system property if it isn't set. Necessary
103: // on Macs, where we otherwise get charset=MacLatin
104: // http://java.sun.com/products/javamail/javadocs/overview-summary.html
105: System.setProperty("mail.mime.charset", props.getProperty(
106: "mail.charset", "ISO-8859-15"));
107:
108: // get the host property - first try "mail.host", then "smtp" property
109: String newHost = props.getProperty("mail.host");
110: if (newHost == null) {
111: newHost = props.getProperty("smtp");
112: }
113:
114: // has the host changed?
115: boolean hostChanged = (host == null && newHost != null)
116: || (host != null && !host.equals(newHost));
117:
118: if (session == null || hostChanged) {
119: host = newHost;
120:
121: // create properties and for the Session. Only set mail host if it is
122: // explicitly set, otherwise we'll go with the system default.
123: Properties sessionProps = new Properties();
124: if (host != null) {
125: sessionProps.put("mail.smtp.host", host);
126: }
127:
128: session = Session.getInstance(sessionProps);
129: }
130:
131: return session;
132: }
133:
134: /**
135: * JavaScript constructor, called by the Rhino runtime.
136: */
137: public static MailObject mailObjCtor(Context cx, Object[] args,
138: Function ctorObj, boolean inNewExpr) {
139: MailObject proto = (MailObject) ctorObj.get("prototype",
140: ctorObj);
141: return new MailObject(proto.getSession());
142: }
143:
144: /**
145: * Initialize Mail extension for the given scope, called by RhinoCore.
146: */
147: public static void init(Scriptable scope, Properties props) {
148: Method[] methods = MailObject.class.getDeclaredMethods();
149: MailObject proto = new MailObject(props);
150: proto.setPrototype(getObjectPrototype(scope));
151: Member ctorMember = null;
152: for (int i = 0; i < methods.length; i++) {
153: if ("mailObjCtor".equals(methods[i].getName())) {
154: ctorMember = methods[i];
155: break;
156: }
157: }
158: FunctionObject ctor = new FunctionObject("Mail", ctorMember,
159: scope);
160: ctor.addAsConstructor(scope, proto);
161: ctor.put("props", ctor, props);
162: String[] mailFuncs = { "addBCC", "addCC", "addPart", "addText",
163: "addTo", "send", "setFrom", "setSubject", "setText",
164: "setTo", "setReplyTo", "setMultipartType",
165: "getMultipartType" };
166: try {
167: proto.defineFunctionProperties(mailFuncs, MailObject.class,
168: 0);
169: proto.defineProperty("status", MailObject.class, 0);
170: } catch (Exception ignore) {
171: System.err.println("Error defining function properties: "
172: + ignore);
173: }
174: }
175:
176: /**
177: * Set the error status of this message
178: *
179: * @param status the new error status
180: */
181: protected void setStatus(int status) {
182: // Only register the first error that occurrs
183: if (this .status == 0) {
184: this .status = status;
185: }
186: }
187:
188: /**
189: * Returns the error status of this message.
190: *
191: * @return the error status of this message
192: */
193: public int getStatus() {
194: return status;
195: }
196:
197: /**
198: * Add some text to a plain text message.
199: */
200: public void addText(String text) {
201: if (text != null) {
202: if (buffer == null) {
203: buffer = new StringBuffer();
204: }
205: buffer.append(text);
206: }
207: }
208:
209: /**
210: * Set the text to a plain text message, clearing any previous text.
211: */
212: public void setText(String text) {
213: if (text != null) {
214: buffer = new StringBuffer(text);
215: }
216: }
217:
218: /**
219: * Returns the MIME multipart message subtype. The default value is
220: * "mixed" for messages of type multipart/mixed. A common value
221: * is "alternative" for the multipart/alternative MIME type.
222: * @return the MIME subtype such as "mixed" or "alternative"
223: */
224: public String getMultipartType() {
225: return multipartType;
226: }
227:
228: /**
229: * Sets the MIME multipart message subtype. The default value is
230: * "mixed" for messages of type multipart/mixed. A common value
231: * is "alternative" for the multipart/alternative MIME type.
232: * @param subtype the MIME subtype such as "mixed" or "alternative".
233: */
234: public void setMultipartType(String subtype) {
235: multipartType = subtype;
236: }
237:
238: /**
239: * Add a MIME message part to a multipart message
240: *
241: * @param obj the MIME part object. Supported classes are java.lang.String,
242: * java.io.File and helma.util.MimePart.
243: * @param filename optional file name for the mime part
244: */
245: public void addPart(Object obj, Object filename) {
246: try {
247: if (obj == null || obj == Undefined.instance) {
248: throw new IOException(
249: "mail.addPart called with wrong number of arguments.");
250: }
251:
252: if (multipart == null) {
253: multipart = new MimeMultipart(multipartType);
254: }
255:
256: MimeBodyPart part = new MimeBodyPart();
257:
258: // if param is wrapped JavaObject unwrap.
259: if (obj instanceof Wrapper) {
260: obj = ((Wrapper) obj).unwrap();
261: }
262:
263: if (obj instanceof String) {
264: part.setContent(obj.toString(), "text/plain");
265: } else if (obj instanceof File) {
266: FileDataSource source = new FileDataSource((File) obj);
267:
268: part.setDataHandler(new DataHandler(source));
269: } else if (obj instanceof MimePart) {
270: MimePartDataSource source = new MimePartDataSource(
271: (MimePart) obj);
272:
273: part.setDataHandler(new DataHandler(source));
274: }
275:
276: // check if an explicit file name was given for this part
277: if (filename != null && filename != Undefined.instance) {
278: try {
279: part.setFileName(filename.toString());
280: } catch (Exception x) {
281: }
282: } else if (obj instanceof File) {
283: try {
284: part.setFileName(((File) obj).getName());
285: } catch (Exception x) {
286: }
287: }
288:
289: multipart.addBodyPart(part);
290: } catch (Exception mx) {
291: System.err.println("Error in MailObject.addPart(): " + mx);
292: setStatus(MIMEPART);
293: }
294:
295: }
296:
297: /**
298: * Set the subject of this message
299: *
300: * @param subject the message subject
301: */
302: public void setSubject(Object subject) {
303: if (subject == null || subject == Undefined.instance) {
304: return;
305: }
306:
307: try {
308: message.setSubject(MimeUtility.encodeWord(subject
309: .toString()));
310: } catch (Exception mx) {
311: System.err.println("Error in MailObject.setSubject(): "
312: + mx);
313: setStatus(SUBJECT);
314: }
315: }
316:
317: /**
318: * Set the Reply-to address for this message
319: *
320: * @param addstr the email address to set in the Reply-to header
321: */
322: public void setReplyTo(String addstr) {
323: try {
324: if (addstr.indexOf("@") < 0) {
325: throw new AddressException();
326: }
327:
328: Address[] replyTo = new Address[1];
329:
330: replyTo[0] = new InternetAddress(addstr);
331: message.setReplyTo(replyTo);
332: } catch (Exception mx) {
333: System.err.println("Error in MailObject.setReplyTo(): "
334: + mx);
335: setStatus(REPLYTO);
336: }
337: }
338:
339: /**
340: * Set the From address for this message
341: *
342: * @param addstr the email address to set in the From header
343: * @param name the name this address belongs to
344: */
345: public void setFrom(String addstr, Object name) {
346: try {
347: if (addstr.indexOf("@") < 0) {
348: throw new AddressException();
349: }
350:
351: Address address = null;
352:
353: if (name != null && name != Undefined.instance) {
354: address = new InternetAddress(addstr, MimeUtility
355: .encodeWord(name.toString()));
356: } else {
357: address = new InternetAddress(addstr);
358: }
359:
360: message.setFrom(address);
361: } catch (Exception mx) {
362: System.err.println("Error in MailObject.setFrom(): " + mx);
363: setStatus(FROM);
364: }
365: }
366:
367: /**
368: * Set the To address for this message
369: *
370: * @param addstr the email address to set in the To header
371: * @param name the name this address belongs to
372: */
373: public void setTo(String addstr, Object name) {
374: try {
375: addRecipient(addstr, name, Message.RecipientType.TO);
376: } catch (Exception mx) {
377: System.err.println("Error in MailObject.setTo(): " + mx);
378: setStatus(TO);
379: }
380:
381: }
382:
383: /**
384: * Add a To address for this message
385: *
386: * @param addstr the email address to set in the To header
387: * @param name the name this address belongs to
388: */
389: public void addTo(String addstr, Object name) {
390: try {
391: addRecipient(addstr, name, Message.RecipientType.TO);
392: } catch (Exception mx) {
393: System.err.println("Error in MailObject.addTO(): " + mx);
394: setStatus(TO);
395: }
396:
397: }
398:
399: /**
400: * ADd a CC address for this message
401: *
402: * @param addstr the email address to set in the CC header
403: * @param name the name this address belongs to
404: */
405: public void addCC(String addstr, Object name) {
406: try {
407: addRecipient(addstr, name, Message.RecipientType.CC);
408: } catch (Exception mx) {
409: System.err.println("Error in MailObject.addCC(): " + mx);
410: setStatus(CC);
411: }
412: }
413:
414: /**
415: * Add a BCC address for this message
416: *
417: * @param addstr the email address to set in the BCC header
418: * @param name the name this address belongs to
419: */
420: public void addBCC(String addstr, Object name) {
421: try {
422: addRecipient(addstr, name, Message.RecipientType.BCC);
423: } catch (Exception mx) {
424: System.err.println("Error in MailObject.addBCC(): " + mx);
425: setStatus(BCC);
426: }
427: }
428:
429: /**
430: * Add a recipient for this message
431: *
432: * @param addstr the email address
433: * @param name the name this address belongs to
434: * @param type the type of the recipient such as To, CC, BCC
435: *
436: * @throws Exception ...
437: * @throws AddressException ...
438: */
439: private void addRecipient(String addstr, Object name,
440: Message.RecipientType type) throws Exception {
441: if (addstr.indexOf("@") < 0) {
442: throw new AddressException();
443: }
444:
445: Address address = null;
446:
447: if (name != null && name != Undefined.instance) {
448: address = new InternetAddress(addstr, MimeUtility
449: .encodeWord(name.toString()));
450: } else {
451: address = new InternetAddress(addstr);
452: }
453:
454: message.addRecipient(type, address);
455: }
456:
457: /**
458: * Send the message.
459: */
460: public void send() {
461: // only send message if everything's ok
462: if (status != OK) {
463: System.err.println("Error sending mail. Status=" + status);
464: }
465: try {
466: if (buffer != null) {
467: // if we also have a multipart body, add
468: // plain string as first part to it.
469: if (multipart != null) {
470: MimeBodyPart part = new MimeBodyPart();
471:
472: part.setContent(buffer.toString(), "text/plain");
473: multipart.addBodyPart(part, 0);
474: message.setContent(multipart);
475: } else {
476: message.setText(buffer.toString());
477: }
478: } else if (multipart != null) {
479: message.setContent(multipart);
480: } else {
481: message.setText("");
482: }
483:
484: Transport.send(message);
485: } catch (Exception mx) {
486: System.err.println("Error in MailObject.send(): " + mx);
487: setStatus(SEND);
488: }
489: }
490: }
|