001 /**************************************************************** 002 * Licensed to the Apache Software Foundation (ASF) under one * 003 * or more contributor license agreements. See the NOTICE file * 004 * distributed with this work for additional information * 005 * regarding copyright ownership. The ASF licenses this file * 006 * to you under the Apache License, Version 2.0 (the * 007 * "License"); you may not use this file except in compliance * 008 * with the License. You may obtain a copy of the License at * 009 * * 010 * http://www.apache.org/licenses/LICENSE-2.0 * 011 * * 012 * Unless required by applicable law or agreed to in writing, * 013 * software distributed under the License is distributed on an * 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * 015 * KIND, either express or implied. See the License for the * 016 * specific language governing permissions and limitations * 017 * under the License. * 018 ****************************************************************/ 019 020 package org.apache.james.mime4j.message; 021 022 import java.io.IOException; 023 import java.io.InputStream; 024 import java.io.OutputStream; 025 import java.util.Arrays; 026 import java.util.Collection; 027 import java.util.Collections; 028 import java.util.Date; 029 import java.util.TimeZone; 030 031 import org.apache.james.mime4j.MimeException; 032 import org.apache.james.mime4j.MimeIOException; 033 import org.apache.james.mime4j.field.AddressListField; 034 import org.apache.james.mime4j.field.DateTimeField; 035 import org.apache.james.mime4j.field.FieldName; 036 import org.apache.james.mime4j.field.Fields; 037 import org.apache.james.mime4j.field.MailboxField; 038 import org.apache.james.mime4j.field.MailboxListField; 039 import org.apache.james.mime4j.field.UnstructuredField; 040 import org.apache.james.mime4j.field.address.Address; 041 import org.apache.james.mime4j.field.address.AddressList; 042 import org.apache.james.mime4j.field.address.Mailbox; 043 import org.apache.james.mime4j.field.address.MailboxList; 044 import org.apache.james.mime4j.parser.Field; 045 import org.apache.james.mime4j.parser.MimeEntityConfig; 046 import org.apache.james.mime4j.parser.MimeStreamParser; 047 import org.apache.james.mime4j.storage.DefaultStorageProvider; 048 import org.apache.james.mime4j.storage.StorageProvider; 049 050 /** 051 * Represents a MIME message. The following code parses a stream into a 052 * <code>Message</code> object. 053 * 054 * <pre> 055 * Message msg = new Message(new FileInputStream("mime.msg")); 056 * </pre> 057 */ 058 public class Message extends Entity implements Body { 059 060 /** 061 * Creates a new empty <code>Message</code>. 062 */ 063 public Message() { 064 } 065 066 /** 067 * Creates a new <code>Message</code> from the specified 068 * <code>Message</code>. The <code>Message</code> instance is 069 * initialized with copies of header and body of the specified 070 * <code>Message</code>. The parent entity of the new message is 071 * <code>null</code>. 072 * 073 * @param other 074 * message to copy. 075 * @throws UnsupportedOperationException 076 * if <code>other</code> contains a {@link SingleBody} that 077 * does not support the {@link SingleBody#copy() copy()} 078 * operation. 079 * @throws IllegalArgumentException 080 * if <code>other</code> contains a <code>Body</code> that 081 * is neither a {@link Message}, {@link Multipart} or 082 * {@link SingleBody}. 083 */ 084 public Message(Message other) { 085 super(other); 086 } 087 088 /** 089 * Parses the specified MIME message stream into a <code>Message</code> 090 * instance. 091 * 092 * @param is 093 * the stream to parse. 094 * @throws IOException 095 * on I/O errors. 096 * @throws MimeIOException 097 * on MIME protocol violations. 098 */ 099 public Message(InputStream is) throws IOException, MimeIOException { 100 this(is, null, DefaultStorageProvider.getInstance()); 101 } 102 103 /** 104 * Parses the specified MIME message stream into a <code>Message</code> 105 * instance using given {@link MimeEntityConfig}. 106 * 107 * @param is 108 * the stream to parse. 109 * @throws IOException 110 * on I/O errors. 111 * @throws MimeIOException 112 * on MIME protocol violations. 113 */ 114 public Message(InputStream is, MimeEntityConfig config) throws IOException, 115 MimeIOException { 116 this(is, config, DefaultStorageProvider.getInstance()); 117 } 118 119 /** 120 * Parses the specified MIME message stream into a <code>Message</code> 121 * instance using given {@link MimeEntityConfig} and {@link StorageProvider}. 122 * 123 * @param is 124 * the stream to parse. 125 * @param config 126 * {@link MimeEntityConfig} to use. 127 * @param storageProvider 128 * {@link StorageProvider} to use for storing text and binary 129 * message bodies. 130 * @throws IOException 131 * on I/O errors. 132 * @throws MimeIOException 133 * on MIME protocol violations. 134 */ 135 public Message(InputStream is, MimeEntityConfig config, 136 StorageProvider storageProvider) throws IOException, 137 MimeIOException { 138 try { 139 MimeStreamParser parser = new MimeStreamParser(config); 140 parser.setContentHandler(new MessageBuilder(this, storageProvider)); 141 parser.parse(is); 142 } catch (MimeException e) { 143 throw new MimeIOException(e); 144 } 145 } 146 147 /** 148 * Write the content to the given output stream using the 149 * {@link MessageWriter#DEFAULT default} message writer. 150 * 151 * @param out 152 * the output stream to write to. 153 * @throws IOException 154 * in case of an I/O error 155 * @see MessageWriter 156 */ 157 public void writeTo(OutputStream out) throws IOException { 158 MessageWriter.DEFAULT.writeEntity(this, out); 159 } 160 161 /** 162 * Returns the value of the <i>Message-ID</i> header field of this message 163 * or <code>null</code> if it is not present. 164 * 165 * @return the identifier of this message. 166 */ 167 public String getMessageId() { 168 Field field = obtainField(FieldName.MESSAGE_ID); 169 if (field == null) 170 return null; 171 172 return field.getBody(); 173 } 174 175 /** 176 * Creates and sets a new <i>Message-ID</i> header field for this message. 177 * A <code>Header</code> is created if this message does not already have 178 * one. 179 * 180 * @param hostname 181 * host name to be included in the identifier or 182 * <code>null</code> if no host name should be included. 183 */ 184 public void createMessageId(String hostname) { 185 Header header = obtainHeader(); 186 187 header.setField(Fields.messageId(hostname)); 188 } 189 190 /** 191 * Returns the (decoded) value of the <i>Subject</i> header field of this 192 * message or <code>null</code> if it is not present. 193 * 194 * @return the subject of this message. 195 */ 196 public String getSubject() { 197 UnstructuredField field = obtainField(FieldName.SUBJECT); 198 if (field == null) 199 return null; 200 201 return field.getValue(); 202 } 203 204 /** 205 * Sets the <i>Subject</i> header field for this message. The specified 206 * string may contain non-ASCII characters, in which case it gets encoded as 207 * an 'encoded-word' automatically. A <code>Header</code> is created if 208 * this message does not already have one. 209 * 210 * @param subject 211 * subject to set or <code>null</code> to remove the subject 212 * header field. 213 */ 214 public void setSubject(String subject) { 215 Header header = obtainHeader(); 216 217 if (subject == null) { 218 header.removeFields(FieldName.SUBJECT); 219 } else { 220 header.setField(Fields.subject(subject)); 221 } 222 } 223 224 /** 225 * Returns the value of the <i>Date</i> header field of this message as 226 * <code>Date</code> object or <code>null</code> if it is not present. 227 * 228 * @return the date of this message. 229 */ 230 public Date getDate() { 231 DateTimeField dateField = obtainField(FieldName.DATE); 232 if (dateField == null) 233 return null; 234 235 return dateField.getDate(); 236 } 237 238 /** 239 * Sets the <i>Date</i> header field for this message. This method uses the 240 * default <code>TimeZone</code> of this host to encode the specified 241 * <code>Date</code> object into a string. 242 * 243 * @param date 244 * date to set or <code>null</code> to remove the date header 245 * field. 246 */ 247 public void setDate(Date date) { 248 setDate(date, null); 249 } 250 251 /** 252 * Sets the <i>Date</i> header field for this message. The specified 253 * <code>TimeZone</code> is used to encode the specified <code>Date</code> 254 * object into a string. 255 * 256 * @param date 257 * date to set or <code>null</code> to remove the date header 258 * field. 259 * @param zone 260 * a time zone. 261 */ 262 public void setDate(Date date, TimeZone zone) { 263 Header header = obtainHeader(); 264 265 if (date == null) { 266 header.removeFields(FieldName.DATE); 267 } else { 268 header.setField(Fields.date(FieldName.DATE, date, zone)); 269 } 270 } 271 272 /** 273 * Returns the value of the <i>Sender</i> header field of this message as 274 * <code>Mailbox</code> object or <code>null</code> if it is not 275 * present. 276 * 277 * @return the sender of this message. 278 */ 279 public Mailbox getSender() { 280 return getMailbox(FieldName.SENDER); 281 } 282 283 /** 284 * Sets the <i>Sender</i> header field of this message to the specified 285 * mailbox address. 286 * 287 * @param sender 288 * address to set or <code>null</code> to remove the header 289 * field. 290 */ 291 public void setSender(Mailbox sender) { 292 setMailbox(FieldName.SENDER, sender); 293 } 294 295 /** 296 * Returns the value of the <i>From</i> header field of this message as 297 * <code>MailboxList</code> object or <code>null</code> if it is not 298 * present. 299 * 300 * @return value of the from field of this message. 301 */ 302 public MailboxList getFrom() { 303 return getMailboxList(FieldName.FROM); 304 } 305 306 /** 307 * Sets the <i>From</i> header field of this message to the specified 308 * mailbox address. 309 * 310 * @param from 311 * address to set or <code>null</code> to remove the header 312 * field. 313 */ 314 public void setFrom(Mailbox from) { 315 setMailboxList(FieldName.FROM, from); 316 } 317 318 /** 319 * Sets the <i>From</i> header field of this message to the specified 320 * mailbox addresses. 321 * 322 * @param from 323 * addresses to set or <code>null</code> or no arguments to 324 * remove the header field. 325 */ 326 public void setFrom(Mailbox... from) { 327 setMailboxList(FieldName.FROM, from); 328 } 329 330 /** 331 * Sets the <i>From</i> header field of this message to the specified 332 * mailbox addresses. 333 * 334 * @param from 335 * addresses to set or <code>null</code> or an empty collection 336 * to remove the header field. 337 */ 338 public void setFrom(Collection<Mailbox> from) { 339 setMailboxList(FieldName.FROM, from); 340 } 341 342 /** 343 * Returns the value of the <i>To</i> header field of this message as 344 * <code>AddressList</code> object or <code>null</code> if it is not 345 * present. 346 * 347 * @return value of the to field of this message. 348 */ 349 public AddressList getTo() { 350 return getAddressList(FieldName.TO); 351 } 352 353 /** 354 * Sets the <i>To</i> header field of this message to the specified 355 * address. 356 * 357 * @param to 358 * address to set or <code>null</code> to remove the header 359 * field. 360 */ 361 public void setTo(Address to) { 362 setAddressList(FieldName.TO, to); 363 } 364 365 /** 366 * Sets the <i>To</i> header field of this message to the specified 367 * addresses. 368 * 369 * @param to 370 * addresses to set or <code>null</code> or no arguments to 371 * remove the header field. 372 */ 373 public void setTo(Address... to) { 374 setAddressList(FieldName.TO, to); 375 } 376 377 /** 378 * Sets the <i>To</i> header field of this message to the specified 379 * addresses. 380 * 381 * @param to 382 * addresses to set or <code>null</code> or an empty collection 383 * to remove the header field. 384 */ 385 public void setTo(Collection<Address> to) { 386 setAddressList(FieldName.TO, to); 387 } 388 389 /** 390 * Returns the value of the <i>Cc</i> header field of this message as 391 * <code>AddressList</code> object or <code>null</code> if it is not 392 * present. 393 * 394 * @return value of the cc field of this message. 395 */ 396 public AddressList getCc() { 397 return getAddressList(FieldName.CC); 398 } 399 400 /** 401 * Sets the <i>Cc</i> header field of this message to the specified 402 * address. 403 * 404 * @param cc 405 * address to set or <code>null</code> to remove the header 406 * field. 407 */ 408 public void setCc(Address cc) { 409 setAddressList(FieldName.CC, cc); 410 } 411 412 /** 413 * Sets the <i>Cc</i> header field of this message to the specified 414 * addresses. 415 * 416 * @param cc 417 * addresses to set or <code>null</code> or no arguments to 418 * remove the header field. 419 */ 420 public void setCc(Address... cc) { 421 setAddressList(FieldName.CC, cc); 422 } 423 424 /** 425 * Sets the <i>Cc</i> header field of this message to the specified 426 * addresses. 427 * 428 * @param cc 429 * addresses to set or <code>null</code> or an empty collection 430 * to remove the header field. 431 */ 432 public void setCc(Collection<Address> cc) { 433 setAddressList(FieldName.CC, cc); 434 } 435 436 /** 437 * Returns the value of the <i>Bcc</i> header field of this message as 438 * <code>AddressList</code> object or <code>null</code> if it is not 439 * present. 440 * 441 * @return value of the bcc field of this message. 442 */ 443 public AddressList getBcc() { 444 return getAddressList(FieldName.BCC); 445 } 446 447 /** 448 * Sets the <i>Bcc</i> header field of this message to the specified 449 * address. 450 * 451 * @param bcc 452 * address to set or <code>null</code> to remove the header 453 * field. 454 */ 455 public void setBcc(Address bcc) { 456 setAddressList(FieldName.BCC, bcc); 457 } 458 459 /** 460 * Sets the <i>Bcc</i> header field of this message to the specified 461 * addresses. 462 * 463 * @param bcc 464 * addresses to set or <code>null</code> or no arguments to 465 * remove the header field. 466 */ 467 public void setBcc(Address... bcc) { 468 setAddressList(FieldName.BCC, bcc); 469 } 470 471 /** 472 * Sets the <i>Bcc</i> header field of this message to the specified 473 * addresses. 474 * 475 * @param bcc 476 * addresses to set or <code>null</code> or an empty collection 477 * to remove the header field. 478 */ 479 public void setBcc(Collection<Address> bcc) { 480 setAddressList(FieldName.BCC, bcc); 481 } 482 483 /** 484 * Returns the value of the <i>Reply-To</i> header field of this message as 485 * <code>AddressList</code> object or <code>null</code> if it is not 486 * present. 487 * 488 * @return value of the reply to field of this message. 489 */ 490 public AddressList getReplyTo() { 491 return getAddressList(FieldName.REPLY_TO); 492 } 493 494 /** 495 * Sets the <i>Reply-To</i> header field of this message to the specified 496 * address. 497 * 498 * @param replyTo 499 * address to set or <code>null</code> to remove the header 500 * field. 501 */ 502 public void setReplyTo(Address replyTo) { 503 setAddressList(FieldName.REPLY_TO, replyTo); 504 } 505 506 /** 507 * Sets the <i>Reply-To</i> header field of this message to the specified 508 * addresses. 509 * 510 * @param replyTo 511 * addresses to set or <code>null</code> or no arguments to 512 * remove the header field. 513 */ 514 public void setReplyTo(Address... replyTo) { 515 setAddressList(FieldName.REPLY_TO, replyTo); 516 } 517 518 /** 519 * Sets the <i>Reply-To</i> header field of this message to the specified 520 * addresses. 521 * 522 * @param replyTo 523 * addresses to set or <code>null</code> or an empty collection 524 * to remove the header field. 525 */ 526 public void setReplyTo(Collection<Address> replyTo) { 527 setAddressList(FieldName.REPLY_TO, replyTo); 528 } 529 530 private Mailbox getMailbox(String fieldName) { 531 MailboxField field = obtainField(fieldName); 532 if (field == null) 533 return null; 534 535 return field.getMailbox(); 536 } 537 538 private void setMailbox(String fieldName, Mailbox mailbox) { 539 Header header = obtainHeader(); 540 541 if (mailbox == null) { 542 header.removeFields(fieldName); 543 } else { 544 header.setField(Fields.mailbox(fieldName, mailbox)); 545 } 546 } 547 548 private MailboxList getMailboxList(String fieldName) { 549 MailboxListField field = obtainField(fieldName); 550 if (field == null) 551 return null; 552 553 return field.getMailboxList(); 554 } 555 556 private void setMailboxList(String fieldName, Mailbox mailbox) { 557 setMailboxList(fieldName, mailbox == null ? null : Collections 558 .singleton(mailbox)); 559 } 560 561 private void setMailboxList(String fieldName, Mailbox... mailboxes) { 562 setMailboxList(fieldName, mailboxes == null ? null : Arrays 563 .asList(mailboxes)); 564 } 565 566 private void setMailboxList(String fieldName, Collection<Mailbox> mailboxes) { 567 Header header = obtainHeader(); 568 569 if (mailboxes == null || mailboxes.isEmpty()) { 570 header.removeFields(fieldName); 571 } else { 572 header.setField(Fields.mailboxList(fieldName, mailboxes)); 573 } 574 } 575 576 private AddressList getAddressList(String fieldName) { 577 AddressListField field = obtainField(fieldName); 578 if (field == null) 579 return null; 580 581 return field.getAddressList(); 582 } 583 584 private void setAddressList(String fieldName, Address address) { 585 setAddressList(fieldName, address == null ? null : Collections 586 .singleton(address)); 587 } 588 589 private void setAddressList(String fieldName, Address... addresses) { 590 setAddressList(fieldName, addresses == null ? null : Arrays 591 .asList(addresses)); 592 } 593 594 private void setAddressList(String fieldName, Collection<Address> addresses) { 595 Header header = obtainHeader(); 596 597 if (addresses == null || addresses.isEmpty()) { 598 header.removeFields(fieldName); 599 } else { 600 header.setField(Fields.addressList(fieldName, addresses)); 601 } 602 } 603 604 }