Source code for /engineering/autohit-2003/src/autohit/call/modules/MIMEMessageModule.javaOriginal file MIMEMessageModule.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.ByteArrayOutputStream;
  24 import java.io.OutputStream;
  25 import java.nio.charset.Charset;
  26 import java.util.Date;
  27 
  28 import javax.activation.DataHandler;
  29 import javax.activation.DataSource;
  30 import javax.mail.Message;
  31 import javax.mail.Multipart;
  32 import javax.mail.Session;
  33 import javax.mail.internet.InternetAddress;
  34 import javax.mail.internet.MimeBodyPart;
  35 import javax.mail.internet.MimeMessage;
  36 import javax.mail.internet.MimeMultipart;
  37 import javax.mail.internet.MimeUtility;
  38 
  39 import autohit.call.CallException;
  40 import autohit.common.Constants;
  41 import autohit.universe.UniverseException;
  42 
  43 /**
  44  * MIME message module.  It will form MIME messages using javamail 
  45  * functions.
  46  * <p>
  47  * It uses system properties for javax.mail Session.
  48  * <p>
  49  * If an encoding is set, strings will be converted to bytes using the
  50  * system default encoding and then encoded to the specified type.
  51  * If the encoding is specified and an object is read from the universe,
  52  * it will assume it is a byte stream and will encode it according to the
  53  * specified charset.
  54  * <p>
  55  * <code>
  56  * start() start a new message, with no multipart<br>
  57  * multipart() start a new multipart message<br>
  58  * setencoding(enc) set encoding to use for subsiquent operations.<br>
  59  * resetencoding() use the default encoding.<br>
  60  * from(address, optional{personal}) set FROM address<br>
  61  * to(address,  optional{personal}) add a TO address.  Additional calls add new recipients.<br>
  62  * cc(address,  optional{personal}) add a CC address.  Additional calls add new recipients.<br>
  63  * bcc(address, optional{personal}) add a BCC address.  Additional calls add new recipients.<br>
  64  * subject(string) set the subject line<br>
  65  * header(n,v) add a header name/value pair.<br>
  66  * addcontent(text) add content text to the non-multipart message<br>
  67  * addpart(text,contentid,description) add part from string using default encodings<br>
  68  * addpartenc(text, tenc, contentid, cenc, description, denc) add part using specified encodings<br>
  69  * addpartuni(uniobj,contentid,description, type) add part from universe object using default encodings<br>
  70  * addpartunienc(uniobj, tenc, contentid, cenc, description, denc, type) add part from universe object using specified encodings (uniobject currently ingnored)<br>
  71  * save() validate message and freeze send time.<br>
  72  * tostring() return the message as a string.  It must be a save()'d message.<br>
  73  * touni(uniobj) save the message to a universe object.  It must be a save()'d message.<br>
  74  * </code>
  75  * @author Erich P. Gatejen
  76  * @version 1.0
  77  * <i>Version History</i>
  78  * <code>EPG - Initial - 7Aug03</code>
  79  */
  80 public class MIMEMessageModule extends Module {
  81 
  82 	private final static String myNAME = "MIMEMessage";
  83 
  84 	private final static String CONTENTTYPE_HEADERSTRING = "Context-Type";
  85 
  86 	/**
  87 	 * METHODS
  88 	 */
  89 	private final static String method_START = "start";
  90 	private final static String method_MULTIPART = "multipart";
  91 	private final static String method_SETENCODING = "setencoding";
  92 	private final static String method_SETENCODING_1_ENC = "enc";
  93 	private final static String method_RESETENCODING = "resetencoding";
  94 	private final static String method_FROM = "from";
  95 	private final static String method_FROM_1_ADDR = "address";
  96 	private final static String method_FROM_2_PERSONAL = "personal";
  97 	private final static String method_TO = "to";
  98 	private final static String method_TO_1_ADDR = "address";
  99 	private final static String method_TO_2_PERSONAL = "personal";
 100 	private final static String method_CC = "cc";
 101 	private final static String method_CC_1_ADDR = "address";
 102 	private final static String method_CC_2_PERSONAL = "personal";
 103 	private final static String method_BCC = "bcc";
 104 	private final static String method_BCC_1_ADDR = "address";
 105 	private final static String method_BCC_2_PERSONAL = "personal";
 106 	private final static String method_SUBJECT = "subject";
 107 	private final static String method_SUBJECT_1_STRING = "string";
 108 	private final static String method_HEADER = "header";
 109 	private final static String method_HEADER_1_NAME = "n";
 110 	private final static String method_HEADER_2_VALUE = "v";
 111 	private final static String method_ADDCONTENT = "addcontent";
 112 	private final static String method_ADDCONTENT_1_TEXT = "text";
 113 	private final static String method_ADDPART = "addpart";
 114 	private final static String method_ADDPART_1_TEXT = "text";
 115 	private final static String method_ADDPART_2_CONTENTID = "contentid";
 116 	private final static String method_ADDPART_3_DESCRIPTION = "description";
 117 	private final static String method_ADDPARTENC = "addpartenc";
 118 	private final static String method_ADDPARTENC_1_TEXT = "text";
 119 	private final static String method_ADDPARTENC_2_TENC = "tenc";
 120 	private final static String method_ADDPARTENC_3_CONTENTID = "contentid";
 121 	private final static String method_ADDPARTENC_4_CENC = "cenc";
 122 	private final static String method_ADDPARTENC_5_DESCRIPTION = "description";
 123 	private final static String method_ADDPARTENC_6_DENC = "denc";
 124 	private final static String method_ADDPARTUNI = "addpartuni";
 125 	private final static String method_ADDPARTUNI_1_UNIOBJ = "uniobj";
 126 	private final static String method_ADDPARTUNI_2_CONTENTID = "contentid";
 127 	private final static String method_ADDPARTUNI_3_DESCRIPTION = "description";
 128 	private final static String method_ADDPARTUNI_4_TYPE = "type";
 129 	private final static String method_ADDPARTUNIENC = "addpartunienc";
 130 	private final static String method_ADDPARTUNIENC_1_UNIOBJ = "uniobj";
 131 	private final static String method_ADDPARTUNIENC_2_TENC = "tenc";
 132 	private final static String method_ADDPARTUNIENC_3_CONTENTID = "contentid";
 133 	private final static String method_ADDPARTUNIENC_4_CENC = "cenc";
 134 	private final static String method_ADDPARTUNIENC_5_DESCRIPTION =
 135 		"description";
 136 	private final static String method_ADDPARTUNIENC_6_DENC = "denc";
 137 	private final static String method_ADDPARTUNIENC_7_TYPE = "type";
 138 	private final static String method_SAVE = "save";
 139 	private final static String method_TOSTRING = "tostring";
 140 	private final static String method_TOUNI = "touni";
 141 	private final static String method_TOUNI_1_UNIOBJ = "uniobj";
 142 
 143 	private MimeMessage msg;
 144 	private Multipart mp;
 145 	private Session defaultSession;
 146 	private boolean valid;
 147 
 148 	// If null, then dont re-encode the strings.
 149 	private String encoding;
 150 
 151 	/**
 152 	 * Constructor
 153 	 */
 154 	public MIMEMessageModule() {
 155 
 156 	}
 157 
 158 	// IMPLEMENTORS
 159 
 160 	/**
 161 	 * Execute a named method.  You must implement this method.
 162 	 * You can call any of the helpers for data and services.
 163 	 * The returned object better be a string (for now).
 164 	 * @param name name of the method
 165 	 * @see autohit.common.NOPair
 166 	 * @throws CallException
 167 	 */
 168 	public Object execute_chain(String name) throws CallException {
 169 
 170 		Object response = Constants.EMPTY_LEFT;
 171 		Object thingie;
 172 
 173 		if (name.equals(method_START)) {
 174 			this.start();
 175 
 176 		} else if (name.equals(method_MULTIPART)) {
 177 			this.multipart();
 178 
 179 		} else if (name.equals(method_SETENCODING)) {
 180 			this.setencoding(this.required(method_SETENCODING_1_ENC, name));
 181 
 182 		} else if (name.equals(method_RESETENCODING)) {
 183 			this.resetencoding();
 184 
 185 		} else if (name.equals(method_FROM)) {
 186 			String param1 = this.required(method_FROM_1_ADDR, name);
 187 			String param2 = this.optional(method_FROM_2_PERSONAL);
 188 			if (param2 == null)
 189 				param2 = param1;
 190 			this.from(param1, param2);
 191 
 192 		} else if (name.equals(method_TO)) {
 193 			String param1 = this.required(method_TO_1_ADDR, name);
 194 			String param2 = this.optional(method_TO_2_PERSONAL);
 195 			if (param2 == null)
 196 				param2 = param1;
 197 			this.recipient(param1, param2, Message.RecipientType.TO);
 198 
 199 		} else if (name.equals(method_CC)) {
 200 			String param1 = this.required(method_CC_1_ADDR, name);
 201 			String param2 = this.optional(method_CC_2_PERSONAL);
 202 			if (param2 == null)
 203 				param2 = param1;
 204 			this.recipient(param1, param2, Message.RecipientType.CC);
 205 
 206 		} else if (name.equals(method_BCC)) {
 207 			String param1 = this.required(method_BCC_1_ADDR, name);
 208 			String param2 = this.optional(method_BCC_2_PERSONAL);
 209 			if (param2 == null)
 210 				param2 = param1;
 211 			this.recipient(param1, param2, Message.RecipientType.BCC);
 212 
 213 		} else if (name.equals(method_SUBJECT)) {
 214 			this.subject(this.required(method_SUBJECT_1_STRING, name));
 215 
 216 		} else if (name.equals(method_HEADER)) {
 217 			this.header(
 218 				this.required(method_HEADER_1_NAME, name),
 219 				this.required(method_HEADER_2_VALUE, name));
 220 
 221 		} else if (name.equals(method_ADDCONTENT)) {
 222 			this.addcontent(this.required(method_ADDCONTENT_1_TEXT, name));
 223 
 224 		} else if (name.equals(method_ADDPART)) {
 225 			this.addpart(
 226 				this.required(method_ADDPART_1_TEXT, name),
 227 				this.required(method_ADDPART_2_CONTENTID, name),
 228 				this.required(method_ADDPART_3_DESCRIPTION, name));
 229 
 230 		} else if (name.equals(method_ADDPARTENC)) {
 231 			this.addpartenc(
 232 				this.required(method_ADDPARTENC_1_TEXT, name),
 233 				this.required(method_ADDPARTENC_2_TENC, name),
 234 				this.required(method_ADDPARTENC_3_CONTENTID, name),
 235 				this.required(method_ADDPARTENC_4_CENC, name),
 236 				this.required(method_ADDPARTENC_5_DESCRIPTION, name),
 237 				this.required(method_ADDPARTENC_6_DENC, name));
 238 
 239 		} else if (name.equals(method_ADDPARTUNI)) {
 240 			this.addpartuni(
 241 				this.required(method_ADDPARTUNI_1_UNIOBJ, name),
 242 				this.required(method_ADDPARTUNI_2_CONTENTID, name),
 243 				this.required(method_ADDPARTUNI_3_DESCRIPTION, name),
 244 				this.required(method_ADDPARTUNI_4_TYPE, name));
 245 
 246 		} else if (name.equals(method_ADDPARTUNIENC)) {
 247 			this.addpartunienc(
 248 				this.required(method_ADDPARTUNIENC_1_UNIOBJ, name),
 249 				this.required(method_ADDPARTUNIENC_2_TENC, name),
 250 				this.required(method_ADDPARTUNIENC_3_CONTENTID, name),
 251 				this.required(method_ADDPARTUNIENC_4_CENC, name),
 252 				this.required(method_ADDPARTUNIENC_5_DESCRIPTION, name),
 253 				this.required(method_ADDPARTUNIENC_6_DENC, name),
 254 				this.required(method_ADDPARTUNIENC_7_TYPE, name));
 255 
 256 		} else if (name.equals(method_SAVE)) {
 257 			this.save();
 258 
 259 		} else if (name.equals(method_TOSTRING)) {
 260 			response = this.mtostring();
 261 
 262 		} else if (name.equals(method_TOUNI)) {
 263 			this.touni(this.required(method_TOUNI_1_UNIOBJ, name));
 264 
 265 		} else {
 266 			error("Not a provided method.  method=" + name);
 267 		}
 268 		return response;
 269 	}
 270 
 271 	/**
 272 	 * Allow the subclass a chance to initialize.  At a minium, an 
 273 	 * implementor should create an empty method.
 274 	 * @throws CallException
 275 	 * @return the name
 276 	 */
 277 	protected String instantiation_chain() throws CallException {
 278 		valid = false;
 279 		msg = null;
 280 		mp = null;
 281 		defaultSession = Session.getDefaultInstance(System.getProperties());
 282 		return myNAME;
 283 	}
 284 
 285 	/**
 286 	 * Allow the subclass a chance to cleanup on free.  At a minium, an 
 287 	 * implementor should create an empty method.
 288 	 * @throws CallException
 289 	 */
 290 	protected void free_chain() throws CallException {
 291 		// just in case....
 292 	}
 293 
 294 	// PRIVATE IMPLEMENTATIONS
 295 
 296 	/**
 297 	 * Start a non-multipart session.
 298 	 * @throws CallException
 299 	 */
 300 	private void start() throws CallException {
 301 		try {
 302 			valid = false;
 303 			encoding = null;
 304 			mp = null;
 305 			msg = new MimeMessage(defaultSession);
 306 		} catch (Exception e) {
 307 			this.fault(
 308 				"Could not instantiate a javax MimeMessage.  e="
 309 					+ e.getMessage());
 310 		}
 311 	}
 312 
 313 	/**
 314 	 * Start a multipart session.
 315 	 * @throws CallException
 316 	 */
 317 	private void multipart() throws CallException {
 318 		try {
 319 			valid = false;
 320 			encoding = null;
 321 			mp = new MimeMultipart();
 322 			msg = new MimeMessage(defaultSession);
 323 		} catch (Exception e) {
 324 			this.fault(
 325 				"Could not instantiate a javax MimeMessage.  e="
 326 					+ e.getMessage());
 327 		}
 328 	}
 329 
 330 	/**
 331 	 * Reset the encoding.  Will use system default.
 332 	 */
 333 	private void resetencoding() throws CallException {
 334 		encoding = null;
 335 	}
 336 
 337 	/**
 338 	 * Set a new default encoding method. It mmust be a valid 
 339 	 * @param enc Encoding name
 340 	 * @throws CallException if the charset is not supported by the system
 341 	 */
 342 	private void setencoding(String enc) throws CallException {
 343 		if (Charset.isSupported(enc)) {
 344 			encoding = enc;
 345 		} else {
 346 			this.fault(
 347 				"Specified character encoding (Charset) is not supported in this system.  enc="
 348 					+ enc);
 349 		}
 350 	}
 351 
 352 	/**
 353 	 * Set FROM 
 354 	 * @param address the address
 355 	 * @param pname the personal name
 356 	 * @throws CallException if there is a problem
 357 	 */
 358 	private void from(String address, String pname) throws CallException {
 359 		InternetAddress ineta = buildaddy(address, pname);
 360 		try {
 361 			msg.setFrom(ineta);
 362 		} catch (Exception ex) {
 363 			this.fault("Could not set FROM address.  address=" + address, ex);
 364 		}
 365 	}
 366 
 367 	/**
 368 	 * Add recipient
 369 	 * @param address the address
 370 	 * @param pname the personal name
 371 	 * @throws CallException if there is a problem
 372 	 */
 373 	private void recipient(
 374 		String address,
 375 		String pname,
 376 		Message.RecipientType type)
 377 		throws CallException {
 378 		InternetAddress ineta = buildaddy(address, pname);
 379 		try {
 380 			msg.addRecipient(type, ineta);
 381 		} catch (Exception ex) {
 382 			this.fault("Could not add address.  address=" + address, ex);
 383 		}
 384 	}
 385 
 386 	/**
 387 	 * Set SUBJECT 
 388 	 * @param text to set as the subject
 389 	 * @throws CallException if there is a problem
 390 	 */
 391 	private void subject(String text) throws CallException {
 392 		try {
 393 			if (encoding == null) {
 394 				msg.setSubject(text);
 395 			} else {
 396 				msg.setSubject(text, encoding);
 397 			}
 398 		} catch (Exception ex) {
 399 			this.fault("Could not set SUBJECT address.  subject=" + text, ex);
 400 		}
 401 	}
 402 
 403 	/**
 404 	 * Add header
 405 	 * @param name name of header item
 406 	 * @param value value of the item.
 407 	 * @throws CallException if there is a problem
 408 	 */
 409 	private void header(String name, String value) throws CallException {
 410 		try {
 411 			if (encoding == null) {
 412 				msg.addHeader(name, value);
 413 			} else {
 414 				msg.addHeader(
 415 					name,
 416 					MimeUtility.encodeText(value, encoding, null));
 417 			}
 418 		} catch (Exception ex) {
 419 			this.fault(
 420 				"Could not set SUBJECT address.  name="
 421 					+ name
 422 					+ ".  message="
 423 					+ ex.getMessage(),
 424 				ex);
 425 		}
 426 	}
 427 
 428 	/**
 429 	 * Add content.  Only use this is start() used.  It will be text/plain.
 430 	 * @param text to add
 431 	 * @throws CallException if there is a problem
 432 	 */
 433 	private void addcontent(String text) throws CallException {
 434 
 435 		if (mp != null) {
 436 			this.fault("You may not addcontent(text) to a multipart message.");
 437 		}
 438 
 439 		try {
 440 			if (encoding == null) {
 441 				msg.setText(text);
 442 			} else {
 443 				msg.setText(text, encoding);
 444 			}
 445 		} catch (Exception ex) {
 446 			this.fault(
 447 				"Could not set content text.  message=" + ex.getMessage(),
 448 				ex);
 449 		}
 450 	}
 451 
 452 	/**
 453 	 * Add a part to a multipart.  All fields are subject to the encodings.
 454 	 * If the text is pre-encoded, there should be no problems.  However,
 455 	 * if there are, you might want to use Universe Objects instead.
 456 	 * @param text is the text to add as a body part
 457 	 * @param cid is the Content ID
 458 	 * @param desc is the content description.
 459 	 * @throws CallException if there is a problem
 460 	 */
 461 	private void addpart(String text, String cid, String desc)
 462 		throws CallException {
 463 
 464 		if (mp == null) {
 465 			this.fault("You may not addpart(text) to a non-multipart message.");
 466 		}
 467 		try {
 468 
 469 			MimeBodyPart mbp = new MimeBodyPart();
 470 
 471 			if (encoding == null) {
 472 				mbp.setText(text);
 473 				mbp.setDescription(desc);
 474 				mbp.setContentID(cid);
 475 			} else {
 476 				mbp.setText(text, encoding);
 477 				mbp.setDescription(desc, encoding);
 478 				mbp.setContentID(MimeUtility.encodeText(cid, encoding, null));
 479 			}
 480 
 481 			mp.addBodyPart(mbp);
 482 
 483 		} catch (Exception ex) {
 484 			this.fault(
 485 				"Could not addpart(text).  message=" + ex.getMessage(),
 486 				ex);
 487 		}
 488 	}
 489 
 490 	/**
 491 	 * Add a part using specified encodings.
 492 	 * If the text is pre-encoded, there should be no problems.  However,
 493 	 * if there are, you might want to use Universe Objects instead.
 494 	 * @param text is the text to add as a body part
 495 	 * @param tenc text encoding
 496 	 * @param cid is the Content ID
 497 	 * @param cenc Content ID encoding
 498 	 * @param desc is the Content Cescription.
 499 	 * @param denc Content Description encoding.
 500 	 * @throws CallException if there is a problem
 501 	 */
 502 	private void addpartenc(
 503 		String text,
 504 		String tenc,
 505 		String cid,
 506 		String cenc,
 507 		String desc,
 508 		String denc)
 509 		throws CallException {
 510 
 511 		if (mp == null) {
 512 			this.fault(
 513 				"You may not addpartenc(...) to a non-multipart message.");
 514 		}
 515 		try {
 516 
 517 			MimeBodyPart mbp = new MimeBodyPart();
 518 
 519 			mbp.setText(text, tenc);
 520 			mbp.setDescription(desc, cenc);
 521 			mbp.setContentID(MimeUtility.encodeText(cid, denc, null));
 522 
 523 			mp.addBodyPart(mbp);
 524 
 525 		} catch (Exception ex) {
 526 			this.fault(
 527 				"Could not addpartenc(...).  message=" + ex.getMessage(),
 528 				ex);
 529 		}
 530 	}
 531 
 532 	/**
 533 	 * Add a part to a multipart from a universe object.  All fields are subject to the encodings.
 534 	 * If the text is pre-encoded, there should be no problems.  However,
 535 	 * if there are, you might want to use Universe Objects instead.
 536 	 * @param uniobj is the text to add as a body part
 537 	 * @param cid is the Content ID
 538 	 * @param desc is the content description.
 539 	 * @param type Content Type.  If empty string, it will get it from the data source.
 540 	 * @throws CallException if there is a problem
 541 	 */
 542 	private void addpartuni(
 543 		String uniobj,
 544 		String cid,
 545 		String desc,
 546 		String type)
 547 		throws CallException {
 548 
 549 		if (mp == null) {
 550 			this.fault(
 551 				"You may not addpartuni(uniobj) to a non-multipart message.");
 552 		}
 553 		try {
 554 
 555 			// Do actual body
 556 			MimeBodyPart mbp = new MimeBodyPart();
 557 			DataSource ds = visUniverse.getDataSource(uniobj);
 558 			mbp.setDataHandler(new DataHandler(ds));
 559 
 560 			if (encoding == null) {
 561 				mbp.setDescription(desc);
 562 				mbp.setContentID(cid);
 563 			} else {
 564 				mbp.setDescription(desc, encoding);
 565 				mbp.setContentID(MimeUtility.encodeText(cid, encoding, null));
 566 			}
 567 			if (type.length() > 0) {
 568 				mbp.setHeader(CONTENTTYPE_HEADERSTRING, type);
 569 			} else {
 570 				mbp.setHeader(CONTENTTYPE_HEADERSTRING, ds.getContentType());
 571 			}
 572 
 573 			mp.addBodyPart(mbp);
 574 
 575 		} catch (UniverseException uex) {
 576 			this.fault(
 577 				"Could not addpartuni(...).  Could not get the Universe object.  code="
 578 					+ uex.numeric
 579 					+ "  message="
 580 					+ uex.getMessage(),
 581 				uex);
 582 		} catch (Exception ex) {
 583 			this.fault(
 584 				"Could not addpartuni(...).  message=" + ex.getMessage(),
 585 				ex);
 586 		}
 587 	}
 588 
 589 	/**
 590 	 * Add a part to a multipart from a universe object.  All fields are subject to the encodings.
 591 	 * If the text is pre-encoded, there should be no problems.  However,
 592 	 * if there are, you might want to use Universe Objects instead.
 593 	 * @param uniobj is the universe object to add as a body part
 594 	 * @param tenc Encoding (Currently ignored)
 595 	 * @param cid is the Content ID
 596 	 * @param cenc Content ID encoding
 597 	 * @param desc is the Content Cescription.
 598 	 * @param denc Content Description encoding.
 599 	 * @param type Content Type.  If empty string, it will get it from the data source.
 600 	 * @throws CallException if there is a problem
 601 	 */
 602 	private void addpartunienc(
 603 		String uniobj,
 604 		String tenc,
 605 		String cid,
 606 		String cenc,
 607 		String desc,
 608 		String denc,
 609 		String type)
 610 		throws CallException {
 611 
 612 		if (mp == null) {
 613 			this.fault(
 614 				"You may not addpartunienc(uniobj) to a non-multipart message.");
 615 		}
 616 		try {
 617 
 618 			// Do actual body
 619 			MimeBodyPart mbp = new MimeBodyPart();
 620 			DataSource ds = visUniverse.getDataSource(uniobj);
 621 			mbp.setDataHandler(new DataHandler(ds));
 622 
 623 			mbp.setDescription(desc, cenc);
 624 			mbp.setContentID(MimeUtility.encodeText(cid, denc, null));
 625 
 626 			if (type.length() > 0) {
 627 				mbp.setHeader(CONTENTTYPE_HEADERSTRING, type);
 628 			} else {
 629 				mbp.setHeader(CONTENTTYPE_HEADERSTRING, ds.getContentType());
 630 			}
 631 
 632 			mp.addBodyPart(mbp);
 633 
 634 		} catch (UniverseException uex) {
 635 			this.fault(
 636 				"Could not addpartunienc(...).  Could not get the Universe object.  code="
 637 					+ uex.numeric
 638 					+ "  message="
 639 					+ uex.getMessage(),
 640 				uex);
 641 		} catch (Exception ex) {
 642 			this.fault(
 643 				"Could not addpartunienc(...).  message=" + ex.getMessage(),
 644 				ex);
 645 		}
 646 	}
 647 
 648 	/**
 649 	 * Validate message and freeze send time.
 650 	 * @throws CallException
 651 	 */
 652 	private void save() throws CallException {
 653 
 654 		try {
 655 
 656 			if (mp != null)
 657 				msg.setContent(mp);
 658 			msg.setSentDate(new Date());
 659 			//msg.saveChanges();
 660 			valid = true;
 661 		} catch (Exception ex) {
 662 			this.done();
 663 			this.fault(
 664 				"Could not validate message!  Session will be dumped.  message="
 665 					+ ex.getMessage(),
 666 				ex);
 667 		}
 668 	}
 669 
 670 	/**
 671 	 * Return the message as a string.  It must be a save()'d message.
 672 	 * The string will be subject to the local system encoding.
 673 	 * @throws CallException
 674 	 * TODO there has to be a better way that writing to a ByteArray and then toString
 675 	 */
 676 	private String mtostring() throws CallException {
 677 		String response = null;
 678 
 679 		if (!valid)
 680 			this.fault(
 681 				"Message is not valid.  You must save() it before mtostring().  message=");
 682 
 683 		try {
 684 
 685 			ByteArrayOutputStream bos = new ByteArrayOutputStream();
 686 			msg.writeTo(bos);
 687 			bos.flush();
 688 			response = bos.toString();
 689 
 690 		} catch (Exception ex) {
 691 			this.fault(
 692 				"Could not put message in a string.  message="
 693 					+ ex.getMessage(),
 694 				ex);
 695 		}
 696 		return response;
 697 	}
 698 
 699 	/**
 700 	 * Save the message to a universe object.  It must be a save()'d message.
 701 	 * The data will not be encoded at all!
 702 	 * @throws CallException
 703 	 */
 704 	private String touni(String un) throws CallException {
 705 		String response = null;
 706 
 707 		if (!valid)
 708 			this.fault(
 709 				"Message is not valid.  You must save() it before mtostring().  message=");
 710 
 711 		try {
 712 
 713 			OutputStream ois = visUniverse.putStream(un);
 714 			if (ois == null)
 715 				throw new Exception("Universe returned a null stream.  This should never happen.");
 716 			msg.writeTo(ois);
 717 
 718 		} catch (UniverseException uex) {
 719 			this.fault(
 720 				"Could not put the message in the Universe due to a Universe problem.  code="
 721 					+ uex.numeric
 722 					+ "  message="
 723 					+ uex.getMessage(),
 724 				uex);
 725 
 726 		} catch (Exception ex) {
 727 			this.fault(
 728 				"Could not put message in the Universe.  message="
 729 					+ ex.getMessage(),
 730 				ex);
 731 		}
 732 		return response;
 733 	}
 734 
 735 	/**
 736 	 * Done method.  Dispose of state and everything.
 737 	 * @throws CallException
 738 	 */
 739 	private void done() throws CallException {
 740 		encoding = null;
 741 		mp = null;
 742 		msg = null;
 743 		valid = false;
 744 	}
 745 
 746 	// HELPERS 
 747 
 748 	/**
 749 	 * HELPER
 750 	 * @param address the address
 751 	 * @param pname the personal name
 752 	 * @throws CallException if there is a problem
 753 	 */
 754 	private InternetAddress buildaddy(String address, String pname)
 755 		throws CallException {
 756 		InternetAddress ineta = null;
 757 		try {
 758 			if (encoding == null) {
 759 				ineta = new InternetAddress(address, pname);
 760 			} else {
 761 				ineta = new InternetAddress(address, pname, encoding);
 762 			}
 763 		} catch (Exception ex) {
 764 			this.fault(
 765 				"Could not form and encode address.  address=" + address,
 766 				ex);
 767 		}
 768 		return ineta;
 769 	}
 770 
 771 }