Source code for /engineering/autohit-2003/src/autohit/call/modules/SimpleSmtpModule.javaOriginal file SimpleSmtpModule.java
   1 /**
   2  * AUTOHIT 2003
   3  * Copyright Erich P Gatejen (c) 1989,1997,2003,2004
   4  * 
   5  * This program is free software; you can redistribute it and/or modify 
   6  * it under the terms of the GNU General Public License as published by 
   7  * the Free Software Foundation; either version 2 of the License, or (at
   8  * your option) any later version.
   9  * This program is distributed in the hope that it will be useful, but
  10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  11  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  12  * more details.
  13  * 
  14  * You should have received a copy of the GNU General Public License along
  15  * with this program; if not, write to the Free Software Foundation, Inc.,
  16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  17  *
  18  * Additional license information can be found in the documentation.
  19  * @author Erich P Gatejen
  20  */
  21 package autohit.call.modules;
  22 
  23 import java.io.BufferedReader;
  24 import java.io.IOException;
  25 import java.io.InputStream;
  26 import java.io.InputStreamReader;
  27 import java.io.OutputStream;
  28 import java.io.Writer;
  29 
  30 import org.apache.commons.net.io.Util;
  31 import org.apache.commons.net.smtp.RelayPath;
  32 import org.apache.commons.net.smtp.SMTPClient;
  33 import org.apache.commons.net.smtp.SMTPConnectionClosedException;
  34 
  35 import autohit.call.CallException;
  36 import autohit.common.Constants;
  37 import autohit.universe.UniverseException;
  38 import autohit.universe.UniverseUtils;
  39 import autohit.vm.process.StringProcessors;
  40 
  41 /**
  42  * Simple SMTP module. There is a client/per module at this time. <code>
  43  * start(address,optional{port}) start an SMTP session<br>
  44  * login(optional{hostname}) login to peer<br>
  45  * sender(address) set the sender with address<br>
  46  * addsenderrelay(address) add to the sender relay path<br>
  47  * senderrelay() set the sender with sender relay path.  clear the accumulated relay path.<br>
  48  * recipient(address) add a recipient with address<br>
  49  * addrecipientrelay(address) add to the recipient relay path<br>
  50  * newrecipientrelay(address) start a new recipient relay path<br>
  51  * recipientrelay() add a recipient with recipient relay path.  clear the accumulated relay path.<br>
  52  * send(text) send message from text.<br>
  53  * senduni(uniobj) send message from universe object.<br>
  54  * senduniscrub(uniobj) send message from universe object.  scrub it first with variable replacements<br>
  55  * reset() reset the smtp state.<br>
  56  * done() complete a session.  It will logout and close.<br>
  57  * mailit(from,to,text,host,optional{port}) convenience method for sending small message.
  58  * mailituni(from,to,uniobj,host,optional{port}) convenience method for sending small message.
  59  * </code>
  60  * 
  61  * @author Erich P. Gatejen
  62  * @version 1.0 <i>Version History</i><code>EPG - Initial - 11Aug03</code>
  63  */
  64 public class SimpleSmtpModule extends Module {
  65 
  66 	private final static String myNAME = "SimpleSmtp";
  67 	private final static String TEMPFILE = "temp/smtpscrub";
  68 
  69 	private final static String ERROR_STRING_FOR_FAILURE = "451";
  70 	private final static int SMTP_ERROR_THRESHOLD = 300;
  71 	// An SMTP error code
  72 
  73 	/**
  74 	 * METHODS */
  75 	private final static String method_START = "start";
  76 	private final static String method_START_1_ADDRESS = "address";
  77 	private final static String method_START_2_PORT = "port";
  78 	private final static String method_LOGIN = "login";
  79 	private final static String method_LOGIN_1_HOSTNAME = "hostname";
  80 	private final static String method_SENDER = "sender";
  81 	private final static String method_SENDER_1_ADDRESS = "address";
  82 	private final static String method_ADDSENDERRELAY = "addsenderrelay";
  83 	private final static String method_ADDSENDERRELAY_1_ADDRESS = "address";
  84 	private final static String method_SENDERRELAY = "senderrelay";
  85 	private final static String method_RECIPIENT = "recipient";
  86 	private final static String method_RECIPIENT_1_ADDRESS = "address";
  87 	private final static String method_ADDRECIPIENTRELAY = "addrecipientrelay";
  88 	private final static String method_NEWRECIPIENTRELAY = "newrecipientrelay";
  89 	private final static String method_ADDRECIPIENTRELAY_1_ADDRESS = "address";
  90 	private final static String method_RECIPIENTRELAY = "recipientrelay";
  91 	private final static String method_SEND = "send";
  92 	private final static String method_SEND_1_TEXT = "text";
  93 	private final static String method_SENDUNI = "senduni";
  94 	private final static String method_SENDUNI_1_TEXT = "uniobj";
  95 	private final static String method_SENDUNISCRUB = "senduniscrub";
  96 	private final static String method_SENDUNISCRUB_1_TEXT = "uniobj";	
  97 	private final static String method_RESET = "reset";
  98 	private final static String method_DONE = "done";
  99 	private final static String method_MAILIT = "mailit";
 100 	private final static String method_MAILIT_1_TO = "to";
 101 	private final static String method_MAILIT_2_FROM = "from";
 102 	private final static String method_MAILIT_3_TEXT = "text";
 103 	private final static String method_MAILIT_4_HOSTNAME = "host";
 104 	private final static String method_MAILIT_5_PORT = "port";
 105 	private final static String method_MAILITUNI = "mailituni";
 106 	private final static String method_MAILITUNI_1_TO = "to";
 107 	private final static String method_MAILITUNI_2_FROM = "from";
 108 	private final static String method_MAILITUNI_3_UNIOBJ = "uniobj";
 109 	private final static String method_MAILITUNI_4_HOSTNAME = "host";
 110 	private final static String method_MAILITUNI_5_PORT = "port";
 111 
 112 	SMTPClient client;
 113 	RelayPath senderrelay;
 114 	RelayPath recipientrelay;
 115 
 116 	/**
 117 	 * Constructor */
 118 	public SimpleSmtpModule() {
 119 
 120 	}
 121 
 122 	// IMPLEMENTORS
 123 
 124 	/**
 125 	 * Execute a named method. You must implement this method. You can call any
 126 	 * of the helpers for data and services. The returned object better be a
 127 	 * string (for now).
 128 	 * 
 129 	 * @param name
 130 	 *           name of the method
 131 	 * @see autohit.common.NOPair
 132 	 * @throws CallException
 133 	 */
 134 	public Object execute_chain(String name) throws CallException {
 135 
 136 		Object response = Constants.EMPTY_LEFT;
 137 		Object thingie;
 138 
 139 		if (name.equals(method_START)) {
 140 			String param1 = this.required(method_START_1_ADDRESS, name);
 141 			String param2 = this.optional(method_START_2_PORT);
 142 			this.start(param1, param2);
 143 
 144 		} else if (name.equals(method_LOGIN)) {
 145 			String param1 = this.optional(method_LOGIN_1_HOSTNAME);
 146 			this.login(param1);
 147 
 148 		} else if (name.equals(method_SENDER)) {
 149 			String param1 = this.required(method_SENDER_1_ADDRESS, name);
 150 			this.sender(param1);
 151 
 152 		} else if (name.equals(method_ADDSENDERRELAY)) {
 153 			String param1 = this.required(method_ADDSENDERRELAY_1_ADDRESS, name);
 154 			this.addsenderrelay(param1);
 155 
 156 		} else if (name.equals(method_SENDERRELAY)) {
 157 			this.senderrelay();
 158 
 159 		} else if (name.equals(method_RECIPIENT)) {
 160 			String param1 = this.required(method_RECIPIENT_1_ADDRESS, name);
 161 			this.recipient(param1);
 162 
 163 		} else if (name.equals(method_ADDRECIPIENTRELAY)) {
 164 			String param1 = this.required(method_ADDRECIPIENTRELAY_1_ADDRESS, name);
 165 			this.addrecipientrelay(param1);
 166 
 167 		} else if (name.equals(method_NEWRECIPIENTRELAY)) {
 168 			this.newrecipientrelay();
 169 
 170 		} else if (name.equals(method_RECIPIENTRELAY)) {
 171 			this.recipientrelay();
 172 
 173 		} else if (name.equals(method_SEND)) {
 174 			String param1 = this.required(method_SEND_1_TEXT, name);
 175 			response = this.send(param1);
 176 
 177 		} else if (name.equals(method_SENDUNI)) {
 178 			String param1 = this.required(method_SENDUNI_1_TEXT, name);
 179 			response = this.senduni(param1);
 180 
 181 		} else if (name.equals(method_SENDUNISCRUB)) {
 182 			String param1 = this.required(method_SENDUNISCRUB_1_TEXT, name);
 183 			response = this.senduniscrub(param1);			
 184 			
 185 		} else if (name.equals(method_RESET)) {
 186 			this.reset();
 187 
 188 		} else if (name.equals(method_DONE)) {
 189 			this.done();
 190 
 191 		} else if (name.equals(method_MAILIT)) {
 192 			String param1 = this.required(method_MAILIT_1_TO, name);
 193 			String param2 = this.required(method_MAILIT_2_FROM, name);
 194 			String param3 = this.required(method_MAILIT_3_TEXT, name);
 195 			String param4 = this.required(method_MAILIT_4_HOSTNAME, name);
 196 			String param5 = this.optional(method_MAILIT_5_PORT);
 197 			response = this.mailit(param1, param2, param3, param4, param5);
 198 
 199 		} else if (name.equals(method_MAILITUNI)) {
 200 			String param1 = this.required(method_MAILITUNI_1_TO, name);
 201 			String param2 = this.required(method_MAILITUNI_2_FROM, name);
 202 			String param3 = this.required(method_MAILITUNI_3_UNIOBJ, name);
 203 			String param4 = this.required(method_MAILITUNI_4_HOSTNAME, name);
 204 			String param5 = this.optional(method_MAILITUNI_5_PORT);
 205 			response = this.mailituni(param1, param2, param3, param4, param5);
 206 
 207 		} else {
 208 			error("Not a provided method.  method=" + name);
 209 		}
 210 		return response;
 211 	}
 212 
 213 	/**
 214 	 * Allow the subclass a chance to initialize. At a minium, an implementor
 215 	 * should create an empty method.
 216 	 * 
 217 	 * @throws CallException
 218 	 * @return the name
 219 	 */
 220 	protected String instantiation_chain() throws CallException {
 221 		client = null;
 222 		return myNAME;
 223 	}
 224 
 225 	/**
 226 	 * Allow the subclass a chance to cleanup on free. At a minium, an
 227 	 * implementor should create an empty method.
 228 	 * 
 229 	 * @throws CallException
 230 	 */
 231 	protected void free_chain() throws CallException {
 232 		try {
 233 			this.done();
 234 		} catch (Exception e) {
 235 			// don't care
 236 		}
 237 	}
 238 
 239 	// PRIVATE IMPLEMENTATIONS
 240 
 241 	/**
 242 	 * Start method. It will open a connection to an SMTP server/relay. If a
 243 	 * session is already started, it will report an error. If it cannot make
 244 	 * the connection, it will cause a fault.
 245 	 * 
 246 	 * @param addr
 247 	 *           the domain name address. Do not include protocol or port.
 248 	 * @param post
 249 	 *           this should be a parsable integer. If it is null, the default
 250 	 *           will be used.
 251 	 * @throws CallException
 252 	 */
 253 	private void start(String addr, String port) throws CallException {
 254 
 255 		SMTPClient candidate = null;
 256 
 257 		// Already started?
 258 		if (client != null) {
 259 			this.error("Session already started.  Ignoring new start().");
 260 			return;
 261 		}
 262 		// Passed a port number?
 263 		int portNum = 0;
 264 		if (port != null) {
 265 			try {
 266 				portNum = Integer.parseInt(port);
 267 			} catch (Exception e) {
 268 				this.fault("Malformed 'port' number.  It must be a parsable integer.  text=" + port);
 269 			}
 270 		}
 271 		// Try and construct it
 272 		try {
 273 			candidate = new SMTPClient();
 274 			if (port == null) {
 275 				candidate.connect(addr);
 276 			} else {
 277 				candidate.connect(addr, portNum);
 278 			}
 279 
 280 		} catch (Exception ex) {
 281 			if(client.isConnected()) {
 282 				try {
 283 					client.disconnect();
 284 				} catch(IOException f) {
 285 					// do nothing
 286 				}
 287 			}
 288 			this.fault("Could not connect to host.  message=" + ex.getMessage());
 289 		}
 290 
 291 		// NO CODE AFTER THIS!
 292 		this.log("Connection started.");
 293 		client = candidate;
 294 	}
 295 
 296 	/**
 297 	 * Done method. Dispose of state and everything. */
 298 	private void done() {
 299 
 300 		// Brute force close. Don't care about errors
 301 		if (client != null) {
 302 			try {
 303 				client.logout();
 304 			} catch (Exception exx) {
 305 			}
 306 			try {
 307 				client.disconnect();
 308 			} catch (Exception exx) {
 309 			}
 310 		}
 311 		senderrelay = null;
 312 		client = null;
 313 		this.log("Connection closed.");
 314 		// NO NEW CODE BEFORE THIS LINE!
 315 	}
 316 
 317 	/**
 318 	 * Login method. It will fault if a session has not been started or has
 319 	 * expired. The hostname is optional; pass null if not used.
 320 	 * 
 321 	 * @param hostname
 322 	 *           hostname to use instead of localhost.
 323 	 * @throws CallException
 324 	 */
 325 	private void login(String hostname) throws CallException {
 326 
 327 		// Is it started?
 328 		if (client == null) {
 329 			this.fault("Session not start()'ed.");
 330 		}
 331 
 332 		try {
 333 			if (hostname == null) {
 334 				client.login();
 335 			} else {
 336 				client.login(hostname);
 337 			}
 338 
 339 			// what happened?
 340 			int code = client.getReplyCode();
 341 			if (code >= SMTP_ERROR_THRESHOLD) {
 342 				this.error("Login failed with code=" + code + " reply=" + client.getReplyString());
 343 			} else if (this.isDebugging()) {
 344 				this.debug("Login complete.  code=" + code);
 345 			}
 346 
 347 		} catch (SMTPConnectionClosedException ex) {
 348 			this.done();
 349 			this.fault("Cannot login.  Connection expired and closed itself.");
 350 		} catch (Exception ex) {
 351 			this.done();
 352 			this.fault("Cannot login due to exception.  message=" + ex.getMessage());
 353 		}
 354 	}
 355 
 356 	/**
 357 	 * Sender method. It will fault if a session has not been started or has
 358 	 * expired. It will set the sender. Subsequent calls will overwrite the
 359 	 * value. The address should be a valid email address.
 360 	 * 
 361 	 * @param hostname
 362 	 *           hostname to use instead of localhost.
 363 	 * @throws CallException
 364 	 */
 365 	private void sender(String s) throws CallException {
 366 
 367 		// Is it started?
 368 		if (client == null) {
 369 			this.fault("Session not start()'ed.");
 370 		}
 371 
 372 		try {
 373 			client.setSender(s);
 374 
 375 			// what happened?
 376 			int code = client.getReplyCode();
 377 			if (code >= SMTP_ERROR_THRESHOLD) {
 378 				this.error("Sender failed with code=" + code + " reply=" + client.getReplyString());
 379 			} else if (this.isDebugging()) {
 380 				this.debug("Sender complete.  code=" + code);
 381 			}
 382 
 383 		} catch (SMTPConnectionClosedException ex) {
 384 			this.done();
 385 			this.fault("Cannot set sender.  Connection expired and closed itself.");
 386 		} catch (Exception ex) {
 387 			this.done();
 388 			this.fault("Cannot set sender due to exception.  message=" + ex.getMessage());
 389 		}
 390 	}
 391 
 392 	/**
 393 	 * Add sender relay leg method. It will fault if a session has not been
 394 	 * started or has expired. It will start accumulating a sender relay path.
 395 	 * You must call this at least once, if you are going to use senderrelay().
 396 	 * The first call should contain a complete email address at the root of
 397 	 * the relay chain. The accumulation will be reset after done() or other
 398 	 * connection ending event.
 399 	 * 
 400 	 * @param leg
 401 	 *           relay leg.
 402 	 * @throws CallException
 403 	 */
 404 	private void addsenderrelay(String leg) throws CallException {
 405 
 406 		// Is it started?
 407 		if (client == null) {
 408 			this.fault("Session not start()'ed.");
 409 		}
 410 
 411 		try {
 412 			if (senderrelay == null) {
 413 				senderrelay = new RelayPath(leg);
 414 			} else {
 415 				senderrelay.addRelay(leg);
 416 			}
 417 
 418 		} catch (Exception ex) {
 419 			this.done();
 420 			this.fault("Cannot add sender relay due to exception.  message=" + ex.getMessage());
 421 		}
 422 	}
 423 
 424 	/**
 425 	 * Sender relay method. It will fault if a session has not been started or
 426 	 * has expired. It will set the sender as the accumulated relay. You must
 427 	 * have called addsenderrelay() at least once or you will get an error.
 428 	 * Subsequent calls will overwrite the value.
 429 	 * 
 430 	 * @param hostname
 431 	 *           hostname to use instead of localhost.
 432 	 * @throws CallException
 433 	 */
 434 	private void senderrelay() throws CallException {
 435 
 436 		// Is it started?
 437 		if (client == null) {
 438 			this.fault("Session not start()'ed.");
 439 		}
 440 
 441 		// Is there a relay?
 442 		if (senderrelay == null) {
 443 			this.error("Sender relay not ready.  You need to call addsenderrelay() at least once before this method.");
 444 			return;
 445 		}
 446 
 447 		try {
 448 			client.setSender(senderrelay);
 449 
 450 			// what happened?
 451 			int code = client.getReplyCode();
 452 			if (code >= SMTP_ERROR_THRESHOLD) {
 453 				this.error("Set sender relay failed with code=" + code + " reply=" + client.getReplyString());
 454 			} else if (this.isDebugging()) {
 455 				this.debug("Set sender relay complete.  code=" + code);
 456 			}
 457 
 458 		} catch (SMTPConnectionClosedException ex) {
 459 			this.done();
 460 			this.fault("Cannot set sender.  Connection expired and closed itself.");
 461 		} catch (Exception ex) {
 462 			this.done();
 463 			this.fault("Cannot set sender due to exception.  message=" + ex.getMessage());
 464 		}
 465 	}
 466 
 467 	/**
 468 	 * Recipient method. It will fault if a session has not been started or has
 469 	 * expired. It will add a recipient. Subsequent calls will add to the list
 470 	 * of recipients. The address should be a valid email address. The only way
 471 	 * to clear the list is to call reset() and start over.
 472 	 * 
 473 	 * @param hostname
 474 	 *           hostname to use instead of localhost.
 475 	 * @throws CallException
 476 	 */
 477 	private void recipient(String s) throws CallException {
 478 
 479 		// Is it started?
 480 		if (client == null) {
 481 			this.fault("Session not start()'ed.");
 482 		}
 483 
 484 		try {
 485 			client.addRecipient(s);
 486 
 487 			// what happened?
 488 			int code = client.getReplyCode();
 489 			if (code >= SMTP_ERROR_THRESHOLD) {
 490 				this.error("Add recipient failed with code=" + code + " reply=" + client.getReplyString());
 491 			} else if (this.isDebugging()) {
 492 				this.debug("Add recipient complete.  code=" + code);
 493 			}
 494 
 495 		} catch (SMTPConnectionClosedException ex) {
 496 			this.done();
 497 			this.fault("Cannot add recipient.  Connection expired and closed itself.");
 498 		} catch (Exception ex) {
 499 			this.done();
 500 			this.fault("Cannot add recipient due to exception.  message=" + ex.getMessage());
 501 		}
 502 	}
 503 
 504 	/**
 505 	 * Add recipeint relay leg method. It will fault if a session has not been
 506 	 * started or has expired. It will start accumulating a recipeint relay
 507 	 * path. You must call this at least once, if you are going to use
 508 	 * recipient relay(). The first call should contain a complete email
 509 	 * address at the root of the relay chain. The accumulation will be reset
 510 	 * after done(), a call to new recipientrelay(), or some other connection
 511 	 * ending event.
 512 	 * 
 513 	 * @param leg
 514 	 *           relay leg.
 515 	 * @throws CallException
 516 	 */
 517 	private void addrecipientrelay(String leg) throws CallException {
 518 
 519 		// Is it started?
 520 		if (client == null) {
 521 			this.fault("Session not start()'ed.");
 522 		}
 523 
 524 		try {
 525 			if (recipientrelay == null) {
 526 				recipientrelay = new RelayPath(leg);
 527 			} else {
 528 				recipientrelay.addRelay(leg);
 529 			}
 530 		} catch (Exception ex) {
 531 			this.done();
 532 			this.fault("Cannot add recipient relay due to exception.  message=" + ex.getMessage());
 533 		}
 534 	}
 535 
 536 	/**
 537 	 * Recipient relay method. It will fault if a session has not been started
 538 	 * or has expired. It will set the sender as the accumulated relay. You
 539 	 * must have called addrecipientrelay() at least once or you will get an
 540 	 * error. Subsequent calls will overwrite the value.
 541 	 * 
 542 	 * @param hostname
 543 	 *           hostname to use instead of localhost.
 544 	 * @throws CallException
 545 	 */
 546 	private void recipientrelay() throws CallException {
 547 
 548 		// Is it started?
 549 		if (client == null) {
 550 			this.fault("Session not start()'ed.");
 551 		}
 552 
 553 		// Is there a relay?
 554 		if (recipientrelay == null) {
 555 			this.error(
 556 				"Recipient relay not ready.  You need to call addrecipientrelay() at least once before this method.");
 557 			return;
 558 		}
 559 
 560 		try {
 561 			client.addRecipient(recipientrelay);
 562 
 563 			// what happened?
 564 			int code = client.getReplyCode();
 565 			if (code >= SMTP_ERROR_THRESHOLD) {
 566 				this.error("Add recipient relay failed with code=" + code + " reply=" + client.getReplyString());
 567 			} else if (this.isDebugging()) {
 568 				this.debug("Add recipient relay complete.  code=" + code);
 569 			}
 570 
 571 		} catch (SMTPConnectionClosedException ex) {
 572 			this.done();
 573 			this.fault("Cannot add recipient.  Connection expired and closed itself.");
 574 		} catch (Exception ex) {
 575 			this.done();
 576 			this.fault("Cannot add recipient due to exception.  message=" + ex.getMessage());
 577 		} finally {
 578 			this.done();
 579 		}
 580 	}
 581 
 582 	/**
 583 	 * Clear the recipeint relay accumulation. Do this if you want to start on
 584 	 * a new recipeint relay. This will never report any kind of error.
 585 	 * 
 586 	 * @param leg
 587 	 *           relay leg.
 588 	 * @throws CallException
 589 	 */
 590 	private void newrecipientrelay() {
 591 		recipientrelay = null;
 592 	}
 593 
 594 	/**
 595 	 * Send the message in the text. Returns the SMTP reply code.
 596 	 * 
 597 	 * @param hostname
 598 	 *           hostname to use instead of localhost.
 599 	 * @throws CallException
 600 	 */
 601 	private String send(String text) throws CallException {
 602 
 603 		String result = ERROR_STRING_FOR_FAILURE;
 604 
 605 		// Is it started?
 606 		if (client == null) {
 607 			this.fault("Session not start()'ed.");
 608 		}
 609 
 610 		// Is there something to send? PARANOID
 611 		if (text == null) {
 612 			this.error("Nothing to send.");
 613 			return result;
 614 		}
 615 
 616 		try {
 617 			client.sendShortMessageData(text);
 618 			//client.completePendingCommand(); // don't care if it was ok.
 619 			result = Integer.toString(client.getReplyCode());
 620 
 621 			// what happened?
 622 			int code = client.getReplyCode();
 623 			if (code >= SMTP_ERROR_THRESHOLD) {
 624 				this.error("Message send FAILED.  code=" + code + " reply=" + client.getReplyString());
 625 			} else if (this.isDebugging()) {
 626 				this.debug("Message send complete.  code=" + code + " reply=" + client.getReplyString());
 627 			}
 628 
 629 		} catch (SMTPConnectionClosedException ex) {
 630 			this.done();
 631 			this.fault("Send failed.  Connection expired and closed itself.");
 632 		} catch (Exception ex) {
 633 			this.done();
 634 			this.fault("Send failed due to exception.  message=" + ex.getMessage());
 635 		} finally {
 636 			this.done();
 637 		}
 638 		return result;
 639 	}
 640 
 641 	/**
 642 	 * Send the message in a Universe Object. Returns the SMTP reply code.
 643 	 * 
 644 	 * @param hostname
 645 	 *           hostname to use instead of localhost.
 646 	 * @throws CallException
 647 	 */
 648 	private String senduni(String uniobject) throws CallException {
 649 
 650 		String result = ERROR_STRING_FOR_FAILURE;
 651 
 652 		// Is it started?
 653 		if (client == null) {
 654 			this.fault("Session not start()'ed.");
 655 		}
 656 
 657 		try {
 658 			// get the hoses
 659 			InputStream unio = visUniverse.getStream(uniobject);
 660 			BufferedReader bin = new BufferedReader(new InputStreamReader(unio));
 661 			Writer mwriter = client.sendMessageData();
 662 
 663 			// and pipe them together
 664 			if (mwriter != null) {
 665 				Util.copyReader(bin, mwriter);
 666 				mwriter.close();
 667 				unio.close();
 668 				client.completePendingCommand(); // don't care if it was ok.
 669 
 670 				// what happened?
 671 				int code = client.getReplyCode();
 672 				if (code >= SMTP_ERROR_THRESHOLD) {
 673 					this.error(
 674 						"Message send FAILED (from Universe).  code=" + code + " reply=" + client.getReplyString());
 675 				} else if (this.isDebugging()) {
 676 					this.debug("Message send complete (from Universe).  code=");
 677 				}
 678 				result = Integer.toString(code);
 679 
 680 			} else {
 681 				this.log(
 682 					"Message send FAILED (from Universe) because SMTP connection was completely ready.  reply="
 683 						+ client.getReplyString());
 684 			}
 685 
 686 		} catch (UniverseException uex) {
 687 			this.fault(
 688 				"Could not send universe object due to Universe problem.  code="
 689 					+ uex.numeric
 690 					+ "  message="
 691 					+ uex.getMessage(),
 692 				uex);
 693 		} catch (SMTPConnectionClosedException ex) {
 694 			this.done();
 695 			this.fault("Send failed (from Universe).  Connection expired and closed itself.");
 696 		} catch (Exception ex) {
 697 			this.done();
 698 			this.fault("Send failed (from Universe) due to exception.  message=" + ex.getMessage());
 699 		}
 700 		return result;
 701 	}
 702 
 703 	/**
 704 	 * Send the message in a Universe Object. Returns the SMTP reply code. It
 705 	 * will run a variable replace on it before sending it.
 706 	 * 
 707 	 * @param hostname
 708 	 *           hostname to use instead of localhost.
 709 	 * @throws CallException
 710 	 */
 711 	private String senduniscrub(String uniobject) throws CallException {
 712 
 713 		String result = ERROR_STRING_FOR_FAILURE;
 714 		String tempObj = null;
 715 
 716 		// Is it started?
 717 		if (client == null) {
 718 			this.fault("Session not start()'ed.");
 719 		}
 720 
 721 		try {
 722 
 723 			// Process it
 724 			try {
 725 				tempObj = visUniverse.reserveUnique(TEMPFILE);
 726 				String thing = UniverseUtils.load2String(visUniverse.getStream(uniobject));
 727 				String thang = StringProcessors.evalString2Core(thing, visCore);
 728 				OutputStream os = visUniverse.putStream(tempObj);
 729 				UniverseUtils.saveString(os, thang);
 730 
 731 			} catch (UniverseException ue) {
 732 				throw ue;
 733 			} catch (Exception e) {
 734 				this.fault("Send failed (from Universe).  Could not create scrubbed intermediary tempfile.  message=" + e.getMessage());
 735 			}
 736 
 737 			// get the hoses
 738 			InputStream unio = visUniverse.getStream(tempObj);
 739 			BufferedReader bin = new BufferedReader(new InputStreamReader(unio));
 740 			Writer mwriter = client.sendMessageData();
 741 
 742 			// and pipe them together
 743 			if (mwriter != null) {
 744 				Util.copyReader(bin, mwriter);
 745 				mwriter.close();
 746 				unio.close();
 747 				client.completePendingCommand(); // don't care if it was ok.
 748 
 749 				// what happened?
 750 				int code = client.getReplyCode();
 751 				if (code >= SMTP_ERROR_THRESHOLD) {
 752 					this.error(
 753 						"Message send FAILED (from Universe).  code=" + code + " reply=" + client.getReplyString());
 754 				} else if (this.isDebugging()) {
 755 					this.debug("Message send complete (from Universe).  code=");
 756 				}
 757 				result = Integer.toString(code);
 758 
 759 			} else {
 760 				this.log(
 761 					"Message send FAILED (from Universe) because SMTP connection was completely ready.  reply="
 762 						+ client.getReplyString());
 763 			}
 764 
 765 		} catch (CallException cex) {
 766 			throw cex;
 767 		} catch (UniverseException uex) {
 768 			this.fault(
 769 				"Could not send universe object due to Universe problem.  code="
 770 					+ uex.numeric
 771 					+ "  message="
 772 					+ uex.getMessage(),
 773 				uex);
 774 		} catch (SMTPConnectionClosedException ex) {
 775 			this.done();
 776 			this.fault("Send failed (from Universe).  Connection expired and closed itself.");
 777 		} catch (Exception ex) {
 778 			this.done();
 779 			this.fault("Send failed (from Universe) due to exception.  message=" + ex.getMessage());
 780 		} finally {
 781 			if ((!this.isDebugging())&&(tempObj != null)) {
 782 				try {
 783 					visUniverse.remove(tempObj);
 784 				} catch (Exception ee) { 
 785 					// Don't care
 786 				}
 787 			}
 788 		}
 789 		return result;
 790 	}
 791 
 792 	/**
 793 	 * Reset method. It will never give an error, even if not start()'ed.
 794 	 * 
 795 	 * @throws CallException
 796 	 */
 797 	private void reset() throws CallException {
 798 		try {
 799 			if (client != null)
 800 				client.reset();
 801 
 802 			// what happened?
 803 			int code = client.getReplyCode();
 804 			if (code >= SMTP_ERROR_THRESHOLD) {
 805 				this.error("Reset FAILED with code=" + code + " reply=" + client.getReplyString());
 806 				this.done();
 807 			} else if (this.isDebugging()) {
 808 				this.debug("Reset complete.  code=" + code);
 809 			}
 810 
 811 		} catch (Exception ex) {
 812 		}
 813 	}
 814 
 815 	/**
 816 	 * Mailit method. A complete transaction wrapped into a convenient method.
 817 	 * No session should be started.
 818 	 * 
 819 	 * @param to
 820 	 *           TO address.
 821 	 * @param from
 822 	 *           FROM address.
 823 	 * @param text
 824 	 *           to send as the message
 825 	 * @param host
 826 	 *           address of smpt server or relay
 827 	 * @param port
 828 	 *           optional port
 829 	 * @throws CallException
 830 	 */
 831 	private String mailit(String to, String from, String text, String host, String port) throws CallException {
 832 
 833 		String response = null;
 834 
 835 		// Is it started?
 836 		if (client != null) {
 837 			this.fault("Session already start()'ed.  You can't use this method on an open session.");
 838 		}
 839 
 840 		try {
 841 			this.start(host, port);
 842 			this.login(null);
 843 			this.sender(from);
 844 			this.recipient(to);
 845 			response = this.send(text);
 846 
 847 		} catch (CallException ce) {
 848 			throw ce;
 849 		} finally {
 850 			this.done();
 851 		}
 852 		return response;
 853 	}
 854 
 855 	/**
 856 	 * Mailituni method. A complete transaction wrapped into a convenient
 857 	 * method. No session should be started.
 858 	 * 
 859 	 * @param to
 860 	 *           TO address.
 861 	 * @param from
 862 	 *           FROM address.
 863 	 * @param uniobj
 864 	 *           to send as the message from universe
 865 	 * @param host
 866 	 *           address of smpt server or relay
 867 	 * @param port
 868 	 *           optional port
 869 	 * @throws CallException
 870 	 */
 871 	private String mailituni(String to, String from, String uniobj, String host, String port) throws CallException {
 872 
 873 		String response = null;
 874 
 875 		// Is it started?
 876 		if (client != null) {
 877 			this.fault("Session already start()'ed.  You can't use this method on an open session.");
 878 		}
 879 
 880 		try {
 881 			this.start(host, port);
 882 			this.login(null);
 883 			this.sender(from);
 884 			this.recipient(to);
 885 			response = this.senduni(uniobj);
 886 
 887 		} catch (CallException ce) {
 888 			throw ce;
 889 		} finally {
 890 			this.done();
 891 		}
 892 		return response;
 893 	}
 894 }