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