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 }