1 56 package org.opencrx.mail.workflow; 57 58 import java.io.UnsupportedEncodingException ; 59 import java.util.Arrays ; 60 import java.util.Collection ; 61 import java.util.Date ; 62 import java.util.HashMap ; 63 import java.util.Iterator ; 64 import java.util.Map ; 65 import java.util.Properties ; 66 67 import javax.jmi.reflect.RefObject; 68 import javax.mail.Address ; 69 import javax.mail.AuthenticationFailedException ; 70 import javax.mail.Message ; 71 import javax.mail.MessagingException ; 72 import javax.mail.Session ; 73 import javax.mail.Transport ; 74 import javax.mail.URLName ; 75 import javax.mail.event.ConnectionAdapter ; 76 import javax.mail.event.ConnectionEvent ; 77 import javax.mail.event.TransportAdapter ; 78 import javax.mail.event.TransportEvent ; 79 import javax.mail.internet.AddressException ; 80 import javax.mail.internet.InternetAddress ; 81 import javax.mail.internet.MimeMessage ; 82 83 import org.opencrx.kernel.base.cci.BooleanProperty; 84 import org.opencrx.kernel.base.cci.IntegerProperty; 85 import org.opencrx.kernel.base.cci.Property; 86 import org.opencrx.kernel.base.cci.StringProperty; 87 import org.opencrx.kernel.base.cci.UriProperty; 88 import org.opencrx.kernel.generic.SecurityKeys; 89 import org.opencrx.kernel.home1.cci.EMailAccount; 90 import org.opencrx.kernel.home1.cci.UserHome; 91 import org.opencrx.kernel.home1.cci.WfActionLogEntry; 92 import org.opencrx.kernel.home1.cci.WfProcessInstance; 93 import org.opencrx.kernel.workflow.ASynchWorkflow_1_0; 94 import org.opencrx.ssl.DefaultSSLSocketFactory; 95 import org.openmdx.application.gui.generic.servlet.Action; 96 import org.openmdx.application.gui.generic.servlet.WebKeys; 97 import org.openmdx.application.log.AppLog; 98 import org.openmdx.base.accessor.jmi.cci.RefPackage_1_0; 99 import org.openmdx.base.exception.ServiceException; 100 import org.openmdx.base.text.conversion.Base64; 101 import org.openmdx.compatibility.base.dataprovider.cci.DataproviderOperations; 102 import org.openmdx.compatibility.base.naming.Path; 103 import org.openmdx.kernel.id.UUIDs; 104 105 public abstract class AbstractMailingWorkflow 106 implements ASynchWorkflow_1_0 { 107 108 private static class SendMailTransportListener 110 extends TransportAdapter { 111 112 public void messageDelivered( 113 TransportEvent e 114 ) { 115 AppLog.info("Message delivered. Invalid addresses", e.getInvalidAddresses() == null ? null : Arrays.asList(e.getInvalidAddresses())); 116 AppLog.info("Message delivered. Valid sent addresses", e.getValidSentAddresses() == null ? null : Arrays.asList(e.getValidSentAddresses())); 117 AppLog.info("Message delivered. Valid unsent addresses", e.getValidUnsentAddresses() == null ? null : Arrays.asList(e.getValidUnsentAddresses())); 118 } 119 120 public void messageNotDelivered( 121 TransportEvent e 122 ) { 123 AppLog.warning("Message not delivered. Invalid addresses", e.getInvalidAddresses() == null ? null : Arrays.asList(e.getInvalidAddresses())); 124 AppLog.warning("Message not delivered. Valid sent addresses", e.getValidSentAddresses() == null ? null : Arrays.asList(e.getValidSentAddresses())); 125 AppLog.warning("Message not delivered. Valid unsent addresses", e.getValidUnsentAddresses() == null ? null : Arrays.asList(e.getValidUnsentAddresses())); 126 } 127 128 public void messagePartiallyDelivered( 129 TransportEvent e 130 ) { 131 AppLog.warning("Message partially delivered. Invalid addresses", e.getInvalidAddresses() == null ? null : Arrays.asList(e.getInvalidAddresses())); 132 AppLog.warning("Message partially delivered. Valid sent addresses", e.getValidSentAddresses() == null ? null : Arrays.asList(e.getValidSentAddresses())); 133 AppLog.warning("Message partially delivered. Valid unsent addresses", e.getValidUnsentAddresses() == null ? null : Arrays.asList(e.getValidUnsentAddresses())); 134 } 135 136 } 137 138 private static class SendMailConnectionListener 140 extends ConnectionAdapter { 141 142 public void closed( 143 ConnectionEvent e 144 ) { 145 AppLog.info("Connection closed"); 146 } 147 148 public void disconnected( 149 ConnectionEvent e 150 ) { 151 AppLog.info("Connection disconnected"); 152 } 153 154 public void opened( 155 ConnectionEvent e 156 ) { 157 AppLog.info("Connection opened"); 158 } 159 160 } 161 162 protected String getWebAccessUrl( 164 UserHome userHome 165 ) { 166 Path userHomeIdentity = userHome.refGetPath(); 167 return userHome.getWebAccessUrl() == null 168 ? "http://localhost/opencrx-core-" + userHomeIdentity.get(2) + "/" + WebKeys.SERVLET_NAME 169 : userHome.getWebAccessUrl() + "/" + WebKeys.SERVLET_NAME; 170 } 171 172 protected String getText( 174 Message message, 175 RefPackage_1_0 rootPkg, 176 Path targetIdentity, 177 Path wfProcessInstanceIdentity, 178 UserHome userHome, 179 Map params 180 ) throws ServiceException { 181 String text = "#ERR"; 182 try { 183 String webAccessUrl = this.getWebAccessUrl(userHome); 184 String subscriptionId = (params.get("triggeredBySubscription") == null ? "N/A" : ((Path)params.get("triggeredBySubscription")).getBase()); 185 Action selectTargetAction = 186 new Action( 187 Action.EVENT_SELECT_OBJECT, 188 new Action.Parameter[]{ 189 new Action.Parameter(Action.PARAMETER_OBJECTXRI, targetIdentity.toXri()) 190 }, 191 "", 192 true 193 ); 194 Action selectWfProcessInstanceAction = 195 new Action( 196 Action.EVENT_SELECT_OBJECT, 197 new Action.Parameter[]{ 198 new Action.Parameter(Action.PARAMETER_OBJECTXRI, wfProcessInstanceIdentity.toXri()) 199 }, 200 "", 201 true 202 ); 203 Action selectTriggeredBySubscriptionAction = params.get("triggeredBySubscription") == null 204 ? null 205 : new Action( 206 Action.EVENT_SELECT_OBJECT, 207 new Action.Parameter[]{ 208 new Action.Parameter(Action.PARAMETER_OBJECTXRI, ((Path)params.get("triggeredBySubscription")).toXri()) 209 }, 210 "", 211 true 212 ); 213 text = 214 "Object Invoked:\n" + 215 webAccessUrl + "?event=" + Action.EVENT_SELECT_OBJECT + "¶meter.enc=" + selectTargetAction.getParameterEncoded() + "\n\n" + 216 "By Workflow:\n" + 217 webAccessUrl + "?event=" + + Action.EVENT_SELECT_OBJECT + "¶meter.enc=" + selectWfProcessInstanceAction.getParameterEncoded() + "\n\n" + 218 "Triggered By Event:\n" + 219 (params.get("triggeredByEventType") == null ? "N/A" : DataproviderOperations.toString(((Number )params.get("triggeredByEventType")).intValue())) + "\n\n" + 220 "Triggered By Subscription:\n" + 221 subscriptionId + "\n" + 222 (selectTriggeredBySubscriptionAction == null ? "N/A" : webAccessUrl + "?event=" + + Action.EVENT_SELECT_OBJECT + "¶meter.enc=" + selectTriggeredBySubscriptionAction.getParameterEncoded()); 223 message.setText(text); 224 } 225 catch(MessagingException e) { 226 throw new ServiceException(e); 227 } 228 return text; 229 } 230 231 protected String getSubject( 233 RefPackage_1_0 rootPkg, 234 Path targetIdentity, 235 UserHome userHome, 236 Map params 237 ) throws ServiceException { 238 Path userHomeIdentity = userHome.refGetPath(); 239 String subject = null; 240 String subscriptionId = (params.get("triggeredBySubscription") == null ? "N/A" : ((Path)params.get("triggeredBySubscription")).getBase()); 241 String sendMailSubjectPrefix = userHome.getSendMailSubjectPrefix() == null 242 ? "openCRX SendMail" 243 : userHome.getSendMailSubjectPrefix(); 244 String webAccessUrl = this.getWebAccessUrl(userHome); 245 if((params.get("confidential") == null) || !((Boolean )params.get("confidential")).booleanValue()) { 246 try { 247 RefObject targetObject = rootPkg.refObject(targetIdentity.toXri()); 248 if(subject == null) { 249 try { 250 if(targetObject.refGetValue("name") != null) { 251 subject = sendMailSubjectPrefix + ": " + targetObject.refGetValue("name"); 252 } 253 } catch(Exception e) {} 254 } 255 if(subject == null) { 256 try { 257 if(targetObject.refGetValue("title") != null) { 258 subject = sendMailSubjectPrefix + ": " + targetObject.refGetValue("title"); 259 } 260 } catch(Exception e) {} 261 } 262 if(subject == null) { 263 try { 264 if(targetObject.refGetValue("fullName") != null) { 265 subject = sendMailSubjectPrefix + ": " + targetObject.refGetValue("fullName"); 266 } 267 } catch(Exception e) {} 268 } 269 } 270 catch(Exception e) {} 271 } 272 if(subject == null) { 273 subject = 274 sendMailSubjectPrefix + ": " + 275 "from=" + userHomeIdentity.get(2) + "/" + userHomeIdentity.get(4)+ "/" + userHomeIdentity.get(6) + "; " + 276 "trigger=" + subscriptionId + "; " + 277 "access=" + webAccessUrl; 278 } 279 return subject; 280 } 281 282 protected String setContent( 284 Message message, 285 Session session, 286 RefPackage_1_0 rootPkg, 287 Path targetIdentity, 288 Path wfProcessInstanceIdentity, 289 UserHome userHome, 290 Map params 291 ) throws ServiceException { 292 String text = null; 293 try { 294 text = this.getText( 295 message, 296 rootPkg, 297 targetIdentity, 298 wfProcessInstanceIdentity, 299 userHome, 300 params 301 ); 302 message.setText(text); 303 } 304 catch(MessagingException e) { 305 throw new ServiceException(e); 306 } 307 return text; 308 } 309 310 protected Address [] setRecipients( 312 Message message, 313 RefPackage_1_0 rootPkg, 314 Path targetIdentity, 315 EMailAccount eMailAccount 316 ) throws ServiceException { 317 Address [] recipients = null; 318 try { 319 message.setFrom( 321 new InternetAddress ( 322 eMailAccount.getReplyEMailAddress() == null 323 ? "noreply@localhost" 324 : eMailAccount.getReplyEMailAddress() 325 ) 326 ); 327 recipients = InternetAddress.parse( 329 eMailAccount.getEMailAddress() == null 330 ? "noreply@localhost" 331 : eMailAccount.getEMailAddress() 332 ); 333 message.setRecipients( 334 Message.RecipientType.TO, 335 recipients 336 ); 337 } 338 catch(AddressException e) { 339 throw new ServiceException(e); 340 } 341 catch(MessagingException e) { 342 throw new ServiceException(e); 343 } 344 return recipients; 345 } 346 347 private void createLogEntry( 349 WfProcessInstance wfProcessInstance, 350 String name, 351 String description 352 ) throws ServiceException { 353 org.opencrx.kernel.home1.cci.home1Package home1Pkg = (org.opencrx.kernel.home1.cci.home1Package)wfProcessInstance.refImmediatePackage(); 354 WfActionLogEntry logEntry = home1Pkg.getWfActionLogEntryClass().createWfActionLogEntry(); 355 try { 356 home1Pkg.refBegin(); 357 wfProcessInstance.addActionLog( 358 UUIDs.getGenerator().next().toString(), 359 logEntry 360 ); 361 logEntry.setName(name); 362 logEntry.setDescription(description); 363 home1Pkg.refCommit(); 364 } 365 catch(Exception e) { 366 new ServiceException(e).log(); 367 try { 368 home1Pkg.refRollback(); 369 } catch(Exception e0) {} 370 } 371 } 372 373 public void execute( 375 WfProcessInstance wfProcessInstance 376 ) throws ServiceException { 377 378 Transport transport = null; 379 Address [] recipients = null; 380 try { 381 RefPackage_1_0 rootPkg = (RefPackage_1_0)wfProcessInstance.refOutermostPackage(); 382 Path wfProcessInstanceIdentity = new Path(wfProcessInstance.refMofId()); 383 384 Map params = new HashMap (); 386 for( 387 Iterator i = wfProcessInstance.getProperty().iterator(); 388 i.hasNext(); 389 ) { 390 Property p = (Property)i.next(); 391 if(p instanceof StringProperty) { 392 params.put( 393 p.getName(), 394 ((StringProperty)p).getStringValue() 395 ); 396 } 397 else if(p instanceof IntegerProperty) { 398 params.put( 399 p.getName(), 400 new Integer (((IntegerProperty)p).getIntegerValue()) 401 ); 402 } 403 else if(p instanceof UriProperty) { 404 params.put( 405 p.getName(), 406 new Path(((UriProperty)p).getUriValue()) 407 ); 408 } 409 else if(p instanceof BooleanProperty) { 410 params.put( 411 p.getName(), 412 new Boolean (((BooleanProperty)p).isBooleanValue()) 413 ); 414 } 415 } 416 417 UserHome userHome = (UserHome)rootPkg.refObject(new Path(wfProcessInstance.refMofId()).getParent().getParent().toXri()); 419 Path userHomeIdentity = new Path(userHome.refMofId()); 420 UserHome userHomeAdmin = (UserHome)rootPkg.refObject( 421 userHomeIdentity.getParent().getChild( 422 SecurityKeys.ADMIN_USER_ID + SecurityKeys.ID_SEPARATOR + userHomeIdentity.get(4) 423 ).toXri() 424 ); 425 426 Path targetIdentity = new Path(wfProcessInstance.getTargetObject()); 428 429 Collection eMailAccounts = userHome.getEMailAccount(); 431 EMailAccount eMailAccountUser = null; 432 for( 433 Iterator i = eMailAccounts.iterator(); 434 i.hasNext(); 435 ) { 436 EMailAccount obj = (EMailAccount)i.next(); 437 if((obj.isDefault() != null) && obj.isDefault().booleanValue()) { 438 eMailAccountUser = obj; 439 break; 440 } 441 } 442 EMailAccount eMailAccountSystem = null; 445 try { 446 eMailAccountSystem = userHomeAdmin.getEMailAccount("SYSTEM"); 447 } 448 catch(Exception e) { 449 } 451 452 String subject = null; 453 String text = null; 454 455 if(eMailAccountUser == null) { 457 subject = "ERROR: " + this.getSubject( 458 rootPkg, 459 targetIdentity, 460 userHome, 461 params 462 ); 463 text = "ERROR: email not sent. No default email account\n" + text; 464 } 465 else { 467 EMailAccount eMailAccountInUse = 468 (eMailAccountSystem != null) && (eMailAccountUser.getIncomingMailServer() == null) && (eMailAccountUser.getOutgoingMailServer() == null) 469 ? eMailAccountSystem 470 : eMailAccountUser; 471 boolean sameAuthSettingsAsIncoming = eMailAccountInUse.isSameAuthSettingsAsIncoming() == null 472 ? false 473 : eMailAccountInUse.isSameAuthSettingsAsIncoming().booleanValue(); 474 String host = sameAuthSettingsAsIncoming 476 ? (eMailAccountInUse.getIncomingMailServer() == null ? "localhost" : eMailAccountInUse.getIncomingMailServer()) 477 : eMailAccountInUse.getOutgoingMailServer(); 478 int port = eMailAccountInUse.getOutgoingMailServerPort() == null ? -1 : eMailAccountInUse.getOutgoingMailServerPort().shortValue(); 480 if(port == 0) port = -1; 481 482 String username = sameAuthSettingsAsIncoming 484 ? (eMailAccountInUse.getIncomingUsername() == null ? null : eMailAccountInUse.getIncomingUsername()) 485 : (eMailAccountInUse.getOutgoingUsername() == null ? null : eMailAccountInUse.getOutgoingUsername()); 486 String encodedPassword = sameAuthSettingsAsIncoming 488 ? (eMailAccountInUse.getIncomingPassword() == null ? null : eMailAccountInUse.getIncomingPassword()) 489 : (eMailAccountInUse.getOutgoingPassword() == null ? null : eMailAccountInUse.getOutgoingPassword()); 490 String password = encodedPassword; 491 try { 492 password = (encodedPassword == null) || !encodedPassword.startsWith(SecurityKeys.PASSWORD_ENCODING_SCHEME) 493 ? null 494 : new String (Base64.decode(encodedPassword.substring(SecurityKeys.PASSWORD_ENCODING_SCHEME.length())), "UTF-8"); 495 } catch(UnsupportedEncodingException e) {} 496 boolean requiresSsl = sameAuthSettingsAsIncoming 498 ? eMailAccountInUse.isIncomingRequiresSsl().booleanValue() 499 : eMailAccountInUse.isOutgoingRequiresSsl().booleanValue(); 500 boolean requiresAuth = eMailAccountInUse.isOutgoingRequiresAuth().booleanValue(); 502 503 Properties props = new Properties (); 505 props.setProperty("mail.debug", params.get("mail.debug") == null ? "false" : "" + params.get("mail.debug")); 506 props.setProperty("mail.transport.protocol", "smtp"); 507 props.setProperty("mail.smtp.host", host); 508 props.setProperty("mail.smtp.connectiontimeout", params.get("mail.smtp.connectiontimeout") == null ? "" + TIMEOUT_MILLIS : "" + params.get("mail.smtp.connectiontimeout")); 509 props.setProperty("mail.smtp.timeout", params.get("mail.smtp.timeout") == null ? "" + TIMEOUT_MILLIS : "" + params.get("mail.smtp.timeout")); 510 if(requiresSsl) { 511 props.setProperty("mail.SSLSocketFactory.class", DefaultSSLSocketFactory.class.getName()); 512 513 520 props.setProperty("mail.smtp.socketFactory.fallback", "false"); 521 props.setProperty("mail.smtp.socketFactory.port", "" + port); 522 props.setProperty("mail.smtp.starttls.enable", "true"); 523 } 524 if(port > 0) { 525 props.setProperty("mail.smtp.port", "" + port); 526 } 527 if(requiresAuth) { 528 props.put("mail.smtp.user", username); 529 props.put("mail.smtp.auth", "true"); 530 } 531 Session session = Session.getDefaultInstance(props, null); 533 Message message = new MimeMessage (session); 535 message.setSentDate( 537 new Date () 538 ); 539 message.setHeader( 541 "X-Mailer", 542 "openCRX SendMail" 543 ); 544 recipients = this.setRecipients( 545 message, 546 rootPkg, 547 targetIdentity, 548 eMailAccountUser 549 ); 550 message.setSubject( 552 subject = this.getSubject( 553 rootPkg, 554 targetIdentity, 555 userHome, 556 params 557 ) 558 ); 559 text = this.setContent( 561 message, 562 session, 563 rootPkg, 564 targetIdentity, 565 wfProcessInstanceIdentity, 566 userHome, 567 params 568 ); 569 570 if(requiresAuth) { 572 transport = session.getTransport( 573 new URLName ("smtp", host, port, null, username, password) 574 ); 575 transport.addConnectionListener( 576 new SendMailConnectionListener() 577 ); 578 transport.addTransportListener( 579 new SendMailTransportListener() 580 ); 581 AppLog.detail("Connect"); 582 transport.connect(); 583 AppLog.detail("Send message"); 584 transport.sendMessage( 585 message, 586 recipients 587 ); 588 AppLog.detail("Close"); 589 transport.close(); 590 AppLog.detail("Done"); 591 } 592 else { 594 AppLog.detail("Send message"); 595 Transport.send(message); 596 AppLog.detail("Done"); 597 } 598 } 599 this.createLogEntry( 600 wfProcessInstance, 601 subject, 602 text 603 ); 604 } 605 catch(AuthenticationFailedException e) { 606 AppLog.warning("Can not send message to recipients (reason=AuthenticationFailedException)", Arrays.asList(recipients)); 607 ServiceException e0 = new ServiceException(e); 608 AppLog.detail( 609 e0.getMessage(), 610 e0.getCause(), 611 1 612 ); 613 this.createLogEntry( 614 wfProcessInstance, 615 "Can not send mail: AuthenticationFailedException", 616 e.getMessage() 617 ); 618 throw e0; 619 } 620 catch(AddressException e) { 621 AppLog.warning("Can not send message to recipients (reason=AddressException)", Arrays.asList(recipients)); 622 ServiceException e0 = new ServiceException(e); 623 AppLog.detail( 624 e0.getMessage(), 625 e0.getCause(), 626 1 627 ); 628 this.createLogEntry( 629 wfProcessInstance, 630 "Can not send mail: AddressException", 631 e.getMessage() 632 ); 633 throw e0; 634 } 635 catch(MessagingException e) { 636 AppLog.warning("Can not send message to recipients (reason=MessagingException)", Arrays.asList(recipients)); 637 ServiceException e0 = new ServiceException(e); 638 AppLog.detail( 639 e0.getMessage(), 640 e0.getCause(), 641 1 642 ); 643 this.createLogEntry( 644 wfProcessInstance, 645 "Can not send mail: MessagingException", 646 e.getMessage() 647 ); 648 throw e0; 649 } 650 finally { 651 if(transport != null) { 652 try { 653 transport.close(); 654 } catch(Exception e) {} 655 } 656 } 657 } 658 659 private static final long TIMEOUT_MILLIS = 60000; 663 664 protected final static short PARTY_TYPE_FROM = 210; 665 protected final static short PARTY_TYPE_TO = 220; 666 protected final static short PARTY_TYPE_CC = 230; 667 protected final static short PARTY_TYPE_BCC = 240; 668 669 } 670 671 672 | Popular Tags |