1 package net.suberic.pooka; 2 3 import java.util.*; 4 5 import javax.mail.*; 6 import javax.mail.internet.*; 7 8 import net.suberic.util.*; 9 import net.suberic.util.thread.*; 10 11 17 public class OutgoingMailServer implements net.suberic.util.Item, net.suberic.util.ValueChangeListener, NetworkConnectionListener { 18 19 String id = null; 20 21 String propertyName = null; 22 23 protected URLName sendMailURL = null; 24 25 String connectionID = null; 26 27 String outboxID = null; 28 29 String mProtocol = "smtp://"; 30 31 NetworkConnection.ConnectionLock connectionLock = null; 32 33 boolean sending = false; 34 35 boolean mStopped = false; 36 37 ActionThread mailServerThread = null; 38 39 42 public OutgoingMailServer (String newId) { 43 id = newId; 44 propertyName = "OutgoingServer." + newId; 45 46 configure(); 47 } 48 49 52 protected void configure() { 53 VariableBundle bundle = Pooka.getResources(); 54 55 connectionID = bundle.getProperty(getItemProperty() + ".connection", ""); 56 NetworkConnection currentConnection = getConnection(); 57 if (currentConnection != null) 58 currentConnection.addConnectionListener(this); 59 60 61 if (Pooka.getProperty(getItemProperty() + ".authenticated", "false").equalsIgnoreCase("true")) { 62 String mProtocol = "smtps://"; 63 } else { 64 String mProtocol = "smtp://"; 65 } 66 67 sendMailURL = new URLName(mProtocol + bundle.getProperty(getItemProperty() + ".server", "") + ":" + bundle.getProperty(getItemProperty() + ".port", "") + "/"); 68 69 outboxID = bundle.getProperty(getItemProperty() + ".outbox", ""); 70 71 bundle.addValueChangeListener(this, getItemProperty() + ".connection"); 72 bundle.addValueChangeListener(this, getItemProperty() + ".server"); 73 bundle.addValueChangeListener(this, getItemProperty() + ".outbox"); 74 75 mailServerThread = new net.suberic.util.thread.ActionThread(getItemID() + " - smtp thread"); 76 mailServerThread.start(); 77 } 78 79 83 public void valueChanged(String changedValue) { 84 VariableBundle bundle = Pooka.getResources(); 85 86 if (changedValue != null) { 87 if (changedValue.equals(getItemProperty() + ".connection")) { 88 NetworkConnection currentConnection = getConnection(); 89 if (currentConnection != null) 90 currentConnection.removeConnectionListener(this); 91 92 connectionID = bundle.getProperty(getItemProperty() + ".connection", ""); 93 currentConnection = getConnection(); 94 if (currentConnection != null) 95 currentConnection.addConnectionListener(this); 96 97 } else if (changedValue.equals(getItemProperty() + ".server")) { 98 sendMailURL = new URLName(mProtocol + bundle.getProperty(getItemProperty() + ".server", "") + ":" + bundle.getProperty(getItemProperty() + ".port", "") + "/"); 99 } else if (changedValue.equals(getItemProperty() + ".outbox")) { 100 String newOutboxID = bundle.getProperty(getItemProperty() + ".outbox", ""); 101 if (newOutboxID != outboxID) { 102 FolderInfo outbox = getOutbox(); 103 if (outbox != null) { 104 outbox.setOutboxFolder(null); 105 } 106 107 outboxID = newOutboxID; 108 loadOutboxFolder(); 109 } 110 } 111 } 112 } 113 114 117 public void loadOutboxFolder() { 118 FolderInfo outbox = getOutbox(); 119 if (outbox != null) { 120 outbox.setOutboxFolder(this); 121 } 122 } 123 124 128 public void stopServer() { 129 mStopped = true; 130 } 131 132 133 136 public void stopThread() { 137 if (mailServerThread != null) { 138 mailServerThread.setStop(true); 139 mailServerThread = null; 140 } 141 } 142 143 146 public void sendAll() { 147 mailServerThread.addToQueue(new javax.swing.AbstractAction () { 148 public void actionPerformed(java.awt.event.ActionEvent ae) { 149 try { 150 internal_sendAll(); 151 } catch (javax.mail.MessagingException me) { 152 Pooka.getUIFactory().showError(Pooka.getProperty("Error.sendingMessage", "Error sending message: "), me); 153 } 154 } 155 }, new java.awt.event.ActionEvent (this, 0, "message-send-all")); 156 } 157 158 161 protected synchronized void internal_sendAll() throws javax.mail.MessagingException { 162 163 try { 164 sending = true; 165 166 Transport sendTransport = prepareTransport(true); 167 168 try { 169 sendTransport.connect(); 170 171 sendAll(sendTransport); 172 } finally { 173 sendTransport.close(); 174 } 175 } finally { 176 sending = false; 177 if (connectionLock != null) 178 connectionLock.release(); 179 } 180 } 181 182 186 private void sendAll(Transport sendTransport) throws javax.mail.MessagingException { 187 188 LinkedList exceptionList = new LinkedList(); 189 190 FolderInfo outbox = getOutbox(); 191 192 if (outbox != null) { 193 Object runLock = outbox.getFolderThread().getRunLock(); 195 synchronized(runLock) { 196 if ( ! outbox.isConnected()) { 197 outbox.openFolder(Folder.READ_WRITE); 198 } 199 200 Folder outboxFolder = outbox.getFolder(); 201 202 if (outboxFolder != null) { 203 Message[] msgs = outboxFolder.getMessages(); 204 205 try { 206 for (int i = 0; i < msgs.length; i++) { 207 Message m = msgs[i]; 208 if (! m.isSet(Flags.Flag.DRAFT)) { 209 try { 210 sendTransport.sendMessage(m, m.getAllRecipients()); 211 m.setFlag(Flags.Flag.DELETED, true); 212 } catch (MessagingException me) { 213 exceptionList.add(me); 214 } 215 } 216 } 217 } finally { 218 if (exceptionList.size() > 0) { 219 final int exceptionCount = exceptionList.size(); 220 javax.swing.SwingUtilities.invokeLater( new Runnable () { 221 public void run() { 222 Pooka.getUIFactory().showError(Pooka.getProperty("error.OutgoingServer.queuedSendFailed", "Failed to send message(s) in the Outbox. Number of errors: ") + exceptionCount ); 223 } 224 } ); 225 } 226 outboxFolder.expunge(); 227 } 228 } 229 } 230 } 231 } 232 233 239 public synchronized void sendMessage(NewMessageInfo nmi, boolean connect) { 240 if (mStopped) { 241 throw new IllegalStateException ("MailServer " + getItemID() + " has been stopped and is not accepting new messages."); 242 } 243 244 final NewMessageInfo nmi_final = nmi; 245 final boolean connect_final = connect; 246 247 mailServerThread.addToQueue(new javax.swing.AbstractAction () { 248 public void actionPerformed(java.awt.event.ActionEvent ae) { 249 internal_sendMessage(nmi_final, connect_final); 250 } 251 }, new java.awt.event.ActionEvent (this, 0, "message-send-all")); 252 } 253 254 private synchronized void internal_sendMessage(NewMessageInfo nmi, boolean connect) { 255 sending = true; 256 257 try { 258 boolean connected = false; 259 Transport sendTransport = null; 260 try { 261 Pooka.getUIFactory().showStatusMessage(Pooka.getProperty("info.smtpServer.connecting", "Connecting to SMTP server...")); 262 263 sendTransport = prepareTransport(connect); 264 sendTransport.connect(); 265 connected = true; 266 } catch (MessagingException me) { 267 if (Pooka.getProperty("Pooka.outbox.autoSave", "false").equalsIgnoreCase("true")) { 268 try { 269 saveToOutbox(nmi); 270 } catch (MessagingException nme) { 271 ((net.suberic.pooka.gui.NewMessageProxy)nmi.getMessageProxy()).sendFailed(this, nme); 272 } 273 } else { 274 ((net.suberic.pooka.gui.NewMessageProxy)nmi.getMessageProxy()).sendFailed(this, me); 275 } 276 } 277 278 if (connected) { 280 try { 281 try { 282 286 Pooka.getUIFactory().showStatusMessage(Pooka.getProperty("info.smtpServer.sending", "Sending message over SMTP.")); 287 288 Map sendMessageMap = nmi.getSendMessageMap(); 289 if (sendMessageMap != null && sendMessageMap.keySet().size() > 0) { 290 Set keySet = sendMessageMap.keySet(); 291 Iterator iter = keySet.iterator(); 292 while (iter.hasNext()) { 293 Message m = (Message) iter.next(); 294 Address[] recipients = (Address[]) sendMessageMap.get(m); 295 if (recipients == null) 296 recipients = m.getAllRecipients(); 297 298 sendTransport.sendMessage(m, recipients); 299 } 300 } else { 301 sendTransport.sendMessage(nmi.getMessage(), nmi.getMessage().getAllRecipients()); 302 } 303 304 ((net.suberic.pooka.gui.NewMessageProxy)nmi.getMessageProxy()).sendSucceeded(true); 305 } catch (MessagingException me) { 306 ((net.suberic.pooka.gui.NewMessageProxy)nmi.getMessageProxy()).sendFailed(this, me); 307 } 308 try { 311 sendAll(sendTransport); 312 } catch (MessagingException exp) { 313 final MessagingException me = exp; 314 javax.swing.SwingUtilities.invokeLater(new Runnable () { 315 public void run() { 316 Pooka.getUIFactory().showError(Pooka.getProperty("error.OutgoingServer.outboxSendFailed", "Failed to send all messages in Outbox: "), me); 317 } 318 }); 319 } 320 } finally { 321 try { 322 sendTransport.close(); 323 } catch (MessagingException me) { 324 } 326 } 327 } 328 } finally { 329 sending = false; 330 if (connectionLock != null) 331 connectionLock.release(); 332 } 333 } 334 335 339 public void sendMessage(NewMessageInfo nmi) { 340 sendMessage(nmi, false); 341 } 342 343 346 protected Transport prepareTransport(boolean connect) throws javax.mail.MessagingException { 347 348 NetworkConnection connection = getConnection(); 349 350 if (connect) { 351 if (connection.getStatus() == NetworkConnection.DISCONNECTED) { 352 connection.connect(); 353 } 354 } 355 356 if (connection.getStatus() != NetworkConnection.CONNECTED) { 357 throw new MessagingException(Pooka.getProperty("error.connectionDown", "Connection down for Mail Server: ") + getItemID()); 358 } else { 359 connectionLock = connection.getConnectionLock(); 360 } 361 362 Session session = Pooka.getDefaultSession(); 363 364 365 boolean useAuth = Pooka.getProperty(getItemProperty() + ".authenticated", "false").equalsIgnoreCase("true"); 366 367 if (useAuth) { 368 java.util.Properties sysProps = new java.util.Properties (System.getProperties()); 369 sysProps.setProperty("mail.mbox.mailspool", Pooka.getProperty("Pooka.spoolDir", "/var/spool/mail")); 370 sysProps.setProperty("mail.smtp.auth", "true"); 371 String userName = Pooka.getProperty(getItemProperty() + ".user", ""); 372 if (! userName.equals("")) 373 sysProps.setProperty("mail.smtp.user", userName); 374 String password = Pooka.getProperty(getItemProperty() + ".password", ""); 375 376 if (! password.equals("")) { 377 password = net.suberic.util.gui.propedit.PasswordEditorPane.descrambleString(password); 378 sysProps.setProperty("mail.smtp.password", password); 379 } 380 381 sysProps.setProperty("mail.smtp.starttls.enable", "true"); 382 383 session = javax.mail.Session.getInstance(sysProps, new FailoverAuthenticator (userName, password)); 384 if (Pooka.getProperty("Pooka.sessionDebug", "false").equalsIgnoreCase("true")) 385 session.setDebug(true); 386 } 387 Transport sendTransport = session.getTransport(getSendMailURL()); 388 return sendTransport; 389 } 390 391 394 public void saveToOutbox(NewMessageInfo nmi) throws MessagingException { 395 Pooka.getUIFactory().showStatusMessage(Pooka.getProperty("info.smtpServer.outbox.connecting", "SMTP server not available; getting outbox.")); 396 397 FolderInfo outbox = getOutbox(); 398 399 if (outbox != null) { 400 Pooka.getUIFactory().showStatusMessage(Pooka.getProperty("info.smtpServer.outbox.waitForLock", "SMTP server not available; waiting for outbox lock.")); 401 402 Object runLock = outbox.getFolderThread().getRunLock(); 404 synchronized(runLock) { 405 try { 406 if ( ! outbox.isConnected()) { 407 Pooka.getUIFactory().showStatusMessage(Pooka.getProperty("info.smtpServer.outbox.opening", "SMTP server not available; opening outbox.")); 408 outbox.openFolder(Folder.READ_WRITE); 409 } 410 411 Pooka.getUIFactory().showStatusMessage(Pooka.getProperty("info.smtpServer.outbox.appending", "SMTP server not available; appending to outbox.")); 412 outbox.appendMessages(new MessageInfo[] { nmi }); 413 414 if (Pooka.getProperty("Pooka.outbox.autoSave", "false").equalsIgnoreCase("true")) { 415 javax.swing.SwingUtilities.invokeLater(new Runnable () { 418 public void run() { 419 Pooka.getUIFactory().showError(Pooka.getProperty("error.MessageWindow.sendDelayed", "Connection unavailable. Message saved to Outbox.")); 420 } 421 }); 422 } 423 424 ((net.suberic.pooka.gui.NewMessageProxy)nmi.getMessageProxy()).sendSucceeded(true); 425 } catch(MessagingException nme) { 426 ((net.suberic.pooka.gui.NewMessageProxy)nmi.getMessageProxy()).sendFailed(this, nme); 427 } 428 } 429 } else { 430 throw new MessagingException("Error saving to Outbox -- no Outbox specified."); 431 } 432 } 433 434 437 public NetworkConnection getConnection() { 438 NetworkConnectionManager connectionManager = Pooka.getConnectionManager(); 439 NetworkConnection returnValue = connectionManager.getConnection(connectionID); 440 if (returnValue != null) 441 return returnValue; 442 else 443 return connectionManager.getDefaultConnection(); 444 } 445 446 450 public void connectionStatusChanged(NetworkConnection connection, int newStatus) { 451 if (newStatus == NetworkConnection.CONNECTED && ! sending && Pooka.getProperty(getItemProperty() + ".sendOnConnect", "false").equalsIgnoreCase("true")) { 452 sendAll(); 453 } 454 } 455 456 460 public FolderInfo getOutbox() { 461 462 return Pooka.getStoreManager().getFolder(outboxID); 463 } 464 465 468 public String getItemID() { 469 return id; 470 } 471 472 476 public String getItemProperty() { 477 return propertyName; 478 } 479 480 483 public URLName getSendMailURL() { 484 return sendMailURL; 485 } 486 487 491 public boolean isSending() { 492 return sending; 493 } 494 495 499 public boolean isStopped() { 500 return mStopped; 501 } 502 503 507 class FailoverAuthenticator extends net.suberic.pooka.gui.SimpleAuthenticator { 508 509 String mUser; 510 String mPassword; 511 boolean firstTry = true; 512 513 Authenticator mAuth; 514 515 519 public FailoverAuthenticator(String pUser, String pPassword) { 520 super(); 521 mUser = pUser; 522 mPassword = pPassword; 523 } 524 525 protected PasswordAuthentication getPasswordAuthentication() { 526 if (firstTry) { 527 firstTry = false; 528 529 return new PasswordAuthentication(mUser, mPassword); 530 } else { 531 return super.getPasswordAuthentication(); 532 } 533 } 534 535 } 536 } 537 | Popular Tags |