001    package railo.runtime.tag;
002    
003    
004    import javax.mail.internet.InternetAddress;
005    
006    import railo.commons.io.res.Resource;
007    import railo.commons.io.res.util.ResourceUtil;
008    import railo.commons.lang.StringUtil;
009    import railo.runtime.exp.ApplicationException;
010    import railo.runtime.exp.ExpressionException;
011    import railo.runtime.exp.PageException;
012    import railo.runtime.ext.tag.BodyTagImpl;
013    import railo.runtime.net.mail.MailException;
014    import railo.runtime.net.mail.MailPart;
015    import railo.runtime.net.smtp.SMTPClient;
016    import railo.runtime.op.Caster;
017    import railo.runtime.op.Decision;
018    // TODO test proxy
019    /**
020     * 
021    * Sends e-mail messages by an SMTP server.
022    *
023    *
024    *
025    **/
026    public final class Mail extends BodyTagImpl {
027    
028            /** Specifies the query column to use when you group sets of records together to send as an e-mail 
029            **              message. For example, if you send a set of billing statements to customers, you might group on 
030            **              "Customer_ID." The group attribute, which is case sensitive, eliminates adjacent duplicates when the 
031            **              data is sorted by the specified field. See the Usage section for exceptions. */
032            private String group;
033    
034            /** Boolean indicating whether to group with regard to case or not. The default value is YES; 
035            **              case is considered while grouping. If the query attribute specifies a query object that was generated 
036            **              by a case-insensitive SQL query, set the groupCaseSensitive attribute to NO to keep the recordset 
037            **              intact. */
038            private boolean groupcasesensitive;
039    
040            /** The name of the cfquery from which to draw data for message(s) to send. Specify this 
041            **              attribute to send more than one mail message, or to send the results of a query within a message. */
042            private String query;
043    
044            /** Specifies the maximum number of e-mail messages to send. */
045            private double maxrows;
046    
047            /** Specifies the row in the query to start from. */
048            private double startrow;
049        
050    
051    
052            //private railo.runtime.mail.Mail mail=new railo.runtime.mail.Mail();
053            private SMTPClient smtp=new SMTPClient();
054            private railo.runtime.net.mail.MailPart part=null;//new railo.runtime.mail.MailPart("UTF-8");
055    
056            private String charset;
057    
058            private int priority;
059    
060            private boolean remove;
061            
062            
063    
064            @Override
065            public void release()   {
066                    super.release();
067    //       do not clear because spooler
068            //mail=new railo.runtime.mail.Mail();   
069                    smtp=new SMTPClient();
070            part=null;//new railo.runtime.mail.MailPart("UTF-8");
071                    group=null;
072                    groupcasesensitive=false;
073                    query=null;
074                    maxrows=0d;
075                    startrow=0d;
076                    charset=null;
077                    remove=false;
078            }
079            
080            
081            /**
082             * @param remove the remove to set
083             */
084            public void setRemove(boolean remove) {
085                    this.remove = remove;
086            }
087    
088    
089            /**
090         * @param proxyserver The proxyserver to set.
091             * @throws ApplicationException 
092         */
093        public void setProxyserver(String proxyserver) throws ApplicationException {
094                    try {
095                            smtp.getProxyData().setServer(proxyserver);
096                    } catch (Exception e) {
097                            throw new ApplicationException("attribute [proxyserver] of the tag [mail] is invalid",e.getMessage());
098                    }
099        }
100            
101            /** set the value proxyport
102            *  The port number on the proxy server from which the object is requested. Default is 80. When 
103            *       used with resolveURL, the URLs of retrieved documents that specify a port number are automatically 
104            *       resolved to preserve links in the retrieved document.
105            * @param proxyport value to set
106             * @throws ApplicationException 
107            **/
108            public void setProxyport(double proxyport) throws ApplicationException  {
109                    try {
110                            smtp.getProxyData().setPort((int)proxyport);
111                    } catch (Exception e) {
112                            throw new ApplicationException("attribute [proxyport] of the tag [mail] is invalid",e.getMessage());
113                    }
114            }
115    
116            /** set the value username
117            *  When required by a proxy server, a valid username.
118            * @param proxyuser value to set
119             * @throws ApplicationException 
120            **/
121            public void setProxyuser(String proxyuser) throws ApplicationException  {
122                    try {
123                            smtp.getProxyData().setUsername(proxyuser);
124                    } catch (Exception e) {
125                            throw new ApplicationException("attribute [proxyuser] of the tag [mail] is invalid",e.getMessage());
126                    }
127            }
128    
129        
130            /** set the value password
131            *  When required by a proxy server, a valid password.
132            * @param proxypassword value to set
133             * @throws ApplicationException 
134            **/
135            public void setProxypassword(String proxypassword) throws ApplicationException  {
136                    try {
137                            smtp.getProxyData().setPassword(proxypassword);
138                    } catch (Exception e) {
139                            throw new ApplicationException("attribute [proxypassword] of the tag [mail] is invalid",e.getMessage());
140                    }
141            }
142    
143            
144    
145            /** set the value from
146            *  The sender of the e-mail message.
147            * @param strForm value to set
148             * @throws PageException 
149            **/
150            public void setFrom(Object from) throws PageException   {
151                    if(StringUtil.isEmpty(from)) return;
152                    try {
153                            smtp.setFrom(from);
154                    } catch (Exception e) {
155                            throw Caster.toPageException(e);
156                    }
157            }
158    
159            /** set the value to
160            *  The name of the e-mail message recipient.
161            * @param strTo value to set
162             * @throws ApplicationException
163            **/
164            public void setTo(Object to) throws ApplicationException        {
165                    if(StringUtil.isEmpty(to)) return;
166                    try {
167                            smtp.addTo(to);
168                    } catch (Exception e) {
169                            throw new ApplicationException("attribute [to] of the tag [mail] is invalid",e.getMessage());
170                    }
171            }
172    
173            /** set the value cc
174            *  Indicates addresses to copy the e-mail message to; "cc" stands for "carbon copy."
175            * @param strCc value to set
176             * @throws ApplicationException
177            **/
178            public void setCc(Object cc) throws ApplicationException        {
179                    if(StringUtil.isEmpty(cc)) return;
180                    try {
181                            smtp.addCC(cc);
182                    } catch (Exception e) {
183                            throw new ApplicationException("attribute [cc] of the tag [mail] is invalid",e.getMessage());
184                    }
185            }
186    
187    
188    
189            /** set the value bcc
190            *  Indicates addresses to copy the e-mail message to, without listing them in the message header. 
191            *               "bcc" stands for "blind carbon copy."
192            * @param strBcc value to set
193             * @throws ApplicationException
194            **/
195            public void setBcc(Object bcc) throws ApplicationException      {
196                    if(StringUtil.isEmpty(bcc)) return;
197                    try {
198                            smtp.addBCC(bcc);
199                    } catch (Exception e) {
200                            throw new ApplicationException("attribute [bcc] of the tag [mail] is invalid",e.getMessage());
201                    }
202            }       
203            
204            /**
205             * @param strFailto The failto to set.
206             * @throws ApplicationException
207             */
208            public void setFailto(Object failto) throws ApplicationException {
209                    if(StringUtil.isEmpty(failto)) return;
210                    try {
211                            smtp.addFailTo(failto);
212                    } catch (Exception e) {
213                            throw new ApplicationException("attribute [failto] of the tag [mail] is invalid",e.getMessage());
214                    }
215            }
216            /**
217             * @param strReplyto The replyto to set.
218             * @throws ApplicationException
219             */
220            public void setReplyto(Object replyto) throws ApplicationException {
221                    if(StringUtil.isEmpty(replyto)) return;
222                    try {
223                            smtp.addReplyTo(replyto);
224                    } catch (Exception e) {
225                            throw new ApplicationException("attribute [replyto] of the tag [mail] is invalid",e.getMessage());
226                    }
227            }
228            
229            /** set the value type
230            *  Specifies extended type attributes for the message.
231            * @param type value to set
232             * @throws ApplicationException
233            **/
234            public void setType(String type) throws ApplicationException    {
235                    type=type.toLowerCase().trim();
236                    if(type.equals("text/plain") || type.equals("plain") || type.equals("text"))
237                        getPart().isHTML(false);
238                        //mail.setType(railo.runtime.mail.Mail.TYPE_TEXT);
239                    else if(type.equals("text/html") || type.equals("html") || type.equals("htm"))
240                            getPart().isHTML(true);
241                    else
242                            throw new ApplicationException("attribute type of tag mail has an invalid values","valid values are [plain,text,html] but value is now ["+type+"]");
243                            //throw new ApplicationException(("invalid type "+type);
244            }
245    
246            /** set the value subject
247            *  The subject of the mail message. This field may be driven dynamically on 
248            *               a message-by-message basis
249            * @param subject value to set
250            **/
251            public void setSubject(String subject)  {
252                    smtp.setSubject(subject);
253            }
254            /**
255             * @param username The username to set.
256             */
257            public void setUsername(String username) {
258                    smtp.setUsername(username);
259            }
260            /**
261             * @param password The password to set.
262             */
263            public void setPassword(String password) {
264                    smtp.setPassword(password);
265            }
266            
267            /** set the value mimeattach
268            *  Specifies the path of the file to be attached to the e-mail message. An attached file 
269            *               is MIME-encoded.
270            * @param strMimeattach value to set
271             * @param type mimetype of the file
272             * @param contentID 
273             * @param disposition 
274             * @throws PageException 
275            **/
276            public void setMimeattach(String strMimeattach, String type, String disposition, String contentID,boolean removeAfterSend) throws PageException {
277                    Resource file=ResourceUtil.toResourceNotExisting(pageContext,strMimeattach);
278            pageContext.getConfig().getSecurityManager().checkFileLocation(file);
279                    if(!file.exists())
280                            throw new ApplicationException("can't attach file "+strMimeattach+", this file doesn't exist");
281                    
282    
283            smtp.addAttachment(file,type,disposition,contentID,removeAfterSend);
284                    
285            }
286            public void setMimeattach(String strMimeattach) throws PageException    {
287                    setMimeattach(strMimeattach, "", null, null,false);
288            }
289            
290            /**
291             * @param spoolenable The spoolenable to set.
292             */
293            public void setSpoolenable(boolean spoolenable) {
294                    smtp.setSpoolenable(spoolenable);
295            }
296            
297            /** set the value server
298            * @param strServer value to set
299             * @throws PageException 
300            **/
301            public void setServer(String strServer) throws PageException {
302                    smtp.setHost(strServer);
303            }
304     
305            /** set the value mailerid
306            * @param mailerid value to set
307            **/
308            public void setMailerid(String mailerid)        {
309                    smtp.setXMailer(mailerid);
310            }
311            
312            /** set the value port
313            *  The TCP/IP port on which the SMTP server listens for requests. This is normally 25.
314            * @param port value to set
315            **/
316            public void setPort(double port)        {
317                    smtp.setPort((int)port);
318            }
319            
320            /**
321             * @param wraptext The wraptext to set.
322             */
323            public void setWraptext(double wraptext) {
324                    getPart().setWraptext((int)wraptext);
325            }
326    
327            /** set the value timeout
328            *  The number of seconds to wait before timing out the connection to the SMTP server.
329            * @param timeout value to set
330            **/
331            public void setTimeout(double timeout)  {
332                    smtp.setTimeout((int)(timeout*1000));
333            }
334            
335            /**
336             * @param charset The charset to set.
337             */
338            public void setCharset(String charset) {
339                    this.charset=charset;
340            }
341    
342            /** set the value group
343            *  Specifies the query column to use when you group sets of records together to send as an e-mail 
344            *               message. For example, if you send a set of billing statements to customers, you might group on 
345            *               "Customer_ID." The group attribute, which is case sensitive, eliminates adjacent duplicates when the 
346            *               data is sorted by the specified field. See the Usage section for exceptions.
347            * @param group value to set
348            **/
349            public void setGroup(String group)      {
350                    this.group=group;
351            }
352    
353            /** set the value groupcasesensitive
354            *  Boolean indicating whether to group with regard to case or not. The default value is YES; 
355            *               case is considered while grouping. If the query attribute specifies a query object that was generated 
356            *               by a case-insensitive SQL query, set the groupCaseSensitive attribute to NO to keep the recordset 
357            *               intact.
358            * @param groupcasesensitive value to set
359            **/
360            public void setGroupcasesensitive(boolean groupcasesensitive)   {
361                    this.groupcasesensitive=groupcasesensitive;
362            }
363    
364            /** set the value query
365            *  The name of the cfquery from which to draw data for message(s) to send. Specify this 
366            *               attribute to send more than one mail message, or to send the results of a query within a message.
367            * @param query value to set
368            **/
369            public void setQuery(String query)      {
370                    this.query=query;
371            }
372    
373            /** set the value maxrows
374            *  Specifies the maximum number of e-mail messages to send.
375            * @param maxrows value to set
376            **/
377            public void setMaxrows(double maxrows)  {
378                    this.maxrows=maxrows;
379            }
380            
381    
382            public void setTls(boolean tls) {
383                    smtp.setTLS(tls);
384            }       
385            
386            public void setUsetls(boolean tls)      {
387                    smtp.setTLS(tls);
388            }       
389            
390            public void setStarttls(boolean tls)    {
391                    smtp.setTLS(tls);
392            }
393    
394            public void setSsl(boolean ssl) {
395                    smtp.setSSL(ssl);
396            }
397    
398            public void setUsessl(boolean ssl)      {
399                    smtp.setSSL(ssl);
400            }
401    
402            public void setSecure(boolean ssl)      {
403                    smtp.setSSL(ssl);
404            }
405            public void setPriority(String strPriority) throws ExpressionException  {
406                    strPriority=strPriority.trim().toLowerCase();
407                    boolean valid=true;
408                    if(Decision.isNumeric(strPriority)) {
409                            int p=Caster.toIntValue(strPriority,-1);
410                            if(p<1 || p>5)valid=false;
411                            else this.priority=p;
412                    }
413                    else {
414                            if("highest".equals(strPriority))priority=1;
415                            else if("urgent".equals(strPriority))priority=1;
416                            else if("high".equals(strPriority))priority=2;
417                            else if("normal".equals(strPriority))priority=3;
418                            else if("low".equals(strPriority))priority=4;
419                            else if("lowest".equals(strPriority))priority=5;
420                            else if("non-urgent".equals(strPriority))priority=5;
421                            else if("none-urgent".equals(strPriority))priority=5;
422                            else valid=false;
423                    }
424                    
425                    if(!valid)throw new ExpressionException("the value of attribute priority is invalid ["+strPriority+"], " +
426                                    "the value should be an integer between [1-5] or " +
427                                    "one of the following [highest,urgent,high,normal,low,lowest,non-urgent]");
428                    
429            }
430    
431            /** set the value startrow
432            *  Specifies the row in the query to start from.
433            * @param startrow value to set
434            **/
435            public void setStartrow(double startrow)        {
436                    this.startrow=startrow;
437            }
438    
439        /**
440         * @param part
441         */
442        public void addPart(MailPart part) {
443            String type = part.getType();
444                    if(StringUtil.isEmpty(part.getCharset())) part.setCharset(getCharset());
445                    if(type!=null && (type.equals("text/plain") || type.equals("plain") || type.equals("text"))){
446                            part.isHTML(false);
447                            addClassicBodyPart(part);
448                    }
449                    else if(type!=null && (type.equals("text/html") || type.equals("html") || type.equals("htm"))){
450                            part.isHTML(true);
451                            addClassicBodyPart(part);
452                    }   
453                    else {
454                            addBodyPart(part);
455                    }
456        }
457            
458        // this was not supported in prior releases
459            private void addBodyPart(MailPart part) {
460                    smtp.setPart(part);
461            }
462    
463            /**
464         * @param part
465         */
466        private void addClassicBodyPart(MailPart part) {
467            if(part.isHTML()) {
468                if(!smtp.hasHTMLText())smtp.setHTMLText(part.getBody(), part.getCharset());
469            }
470            else {
471                if(!smtp.hasPlainText())smtp.setPlainText(part.getBody(), part.getCharset());
472            }
473        }
474    
475    
476        @Override
477            public int doStartTag() throws ApplicationException     {
478                    if(isEmpty(smtp.getTos()) && isEmpty(smtp.getCcs()) && isEmpty(smtp.getBccs())) 
479                            throw new ApplicationException("One of the following attribtues must be defined [to, cc, bcc]");
480                            
481                    return EVAL_BODY_BUFFERED;
482            }
483    
484            private boolean isEmpty(InternetAddress[] addrs) {
485                    return addrs==null || addrs.length==0;
486            }
487    
488    
489            @Override
490            public void doInitBody()        {
491                    
492            }
493    
494            @Override
495            public int doAfterBody()        {
496                    getPart().setBody(bodyContent.getString());
497                    smtp.setCharset(getCharset());
498                    getPart().setCharset(getCharset());
499                    addClassicBodyPart(getPart());
500                    return SKIP_BODY;
501            }
502            
503            @Override
504            public int doEndTag() throws PageException      {
505                    smtp.setTimeZone(pageContext.getTimeZone());
506                    try {
507                            smtp.send(pageContext.getConfig());
508                    } 
509                    catch (MailException e) {
510                            throw Caster.toPageException(e);
511                    }
512                    return EVAL_PAGE;
513            }
514    
515            /**
516             * sets a mail param
517             * @param type
518             * @param file
519             * @param name
520             * @param value
521             * @param contentID 
522             * @param disposition 
523             * @throws PageException 
524             */
525            public void setParam(String type, String file, String name, String value, String disposition, String contentID,Boolean oRemoveAfterSend) throws PageException {
526                    if(file!=null){
527                            boolean removeAfterSend=(oRemoveAfterSend==null)?remove:oRemoveAfterSend.booleanValue();
528                                    
529                            setMimeattach(file,type,disposition,contentID,removeAfterSend);
530                    }
531                    else {
532                            if(name.equalsIgnoreCase("bcc"))                        setBcc(value);
533                            else if(name.equalsIgnoreCase("cc"))            setCc(value);
534                            else if(name.equalsIgnoreCase("charset"))       setCharset(value);
535                            else if(name.equalsIgnoreCase("failto"))        setFailto(value);
536                            else if(name.equalsIgnoreCase("from"))          setFrom(value);
537                            else if(name.equalsIgnoreCase("mailerid"))      setMailerid(value);
538                            else if(name.equalsIgnoreCase("mimeattach"))setMimeattach(value);
539                            else if(name.equalsIgnoreCase("priority"))      setPriority(value);
540                            else if(name.equalsIgnoreCase("replyto"))       setReplyto(value);
541                            else if(name.equalsIgnoreCase("subject"))       setSubject(value);
542                            else if(name.equalsIgnoreCase("to"))            setTo(value);
543                            
544                            else smtp.addHeader(name,value);
545                    }
546            }       
547            
548            private railo.runtime.net.mail.MailPart getPart() {
549                    if(part==null)part=new railo.runtime.net.mail.MailPart(pageContext.getConfig().getMailDefaultEncoding());
550                    return part;
551            }
552    
553    
554            /**
555             * @return the charset
556             */
557            public String getCharset() {
558                    if(charset==null)charset=pageContext.getConfig().getMailDefaultEncoding();
559                    return charset;
560            }
561    }