1. /*
  2. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  3. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  4. */
  5. package javax.mail.internet;
  6. import javax.mail.*;
  7. import javax.activation.*;
  8. import java.lang.*;
  9. import java.io.*;
  10. import java.util.*;
  11. import java.text.ParseException;
  12. import com.sun.mail.util.ASCIIUtility;
  13. import com.sun.mail.util.LineOutputStream;
  14. import com.sun.mail.util.SharedByteArrayInputStream;
  15. /**
  16. * This class represents a MIME style email message. It implements
  17. * the <code>Message</code> abstract class and the <code>MimePart</code>
  18. * interface. <p>
  19. *
  20. * Clients wanting to create new MIME style messages will instantiate
  21. * an empty MimeMessage object and then fill it with appropriate
  22. * attributes and content. <p>
  23. *
  24. * Service providers that implement MIME compliant backend stores may
  25. * want to subclass MimeMessage and override certain methods to provide
  26. * specific implementations. The simplest case is probably a provider
  27. * that generates a MIME style input stream and leaves the parsing of
  28. * the stream to this class. <p>
  29. *
  30. * MimeMessage uses the <code>InternetHeaders</code> class to parse and
  31. * store the toplevel RFC 822 headers of a message. <p>
  32. *
  33. * <hr><strong>A note on RFC 822 and MIME headers</strong><p>
  34. *
  35. * RFC 822 header fields <strong>must</strong> contain only
  36. * US-ASCII characters. MIME allows non ASCII characters to be present
  37. * in certain portions of certain headers, by encoding those characters.
  38. * RFC 2047 specifies the rules for doing this. The MimeUtility
  39. * class provided in this package can be used to to achieve this.
  40. * Callers of the <code>setHeader</code>, <code>addHeader</code>, and
  41. * <code>addHeaderLine</code> methods are responsible for enforcing
  42. * the MIME requirements for the specified headers. In addition, these
  43. * header fields must be folded (wrapped) before being sent if they
  44. * exceed the line length limitation for the transport (1000 bytes for
  45. * SMTP). Received headers may have been folded. The application is
  46. * responsible for folding and unfolding headers as appropriate. <p>
  47. *
  48. * @author John Mani
  49. * @author Bill Shannon
  50. * @author Max Spivak
  51. * @see javax.mail.internet.MimeUtility
  52. * @see javax.mail.Part
  53. * @see javax.mail.Message
  54. * @see javax.mail.internet.MimePart
  55. */
  56. public class MimeMessage extends Message implements MimePart {
  57. /**
  58. * The DataHandler object representing this Message's content.
  59. */
  60. protected DataHandler dh;
  61. /**
  62. * Byte array that holds the bytes of this Message's content.
  63. */
  64. protected byte[] content;
  65. /**
  66. * If the data for this message was supplied by an
  67. * InputStream that implements the SharedInputStream interface,
  68. * <code>contentStream</code> is another such stream representing
  69. * the content of this message. In this case, <code>content</code>
  70. * will be null.
  71. *
  72. * @since JavaMail 1.2
  73. */
  74. protected InputStream contentStream;
  75. /**
  76. * The InternetHeaders object that stores the header
  77. * of this message.
  78. */
  79. protected InternetHeaders headers;
  80. /**
  81. * The Flags for this message.
  82. */
  83. protected Flags flags;
  84. /**
  85. * A flag indicating whether the message has been modified.
  86. * If the message has not been modified, any data in the
  87. * <code>content</code> array is assumed to be valid and is used
  88. * directly in the <code>writeTo</code> method. This flag is
  89. * set to true when an empty message is created or when the
  90. * <code>saveChanges</code> method is called.
  91. *
  92. * @since JavaMail 1.2
  93. */
  94. protected boolean modified = false;
  95. /**
  96. * Does the <code>saveChanges</code> method need to be called on
  97. * this message? This flag is set to false by the public constructor
  98. * and set to true by the <code>saveChanges</code> method. The
  99. * <code>writeTo</code> method checks this flag and calls the
  100. * <code>saveChanges</code> method as necessary. This avoids the
  101. * common mistake of forgetting to call the <code>saveChanges</code>
  102. * method on a newly constructed message.
  103. *
  104. * @since JavaMail 1.2
  105. */
  106. protected boolean saved = false;
  107. // Used to parse dates
  108. private static MailDateFormat mailDateFormat = new MailDateFormat();
  109. /**
  110. * Default constructor. An empty message object is created.
  111. * The <code>headers</code> field is set to an empty InternetHeaders
  112. * object. The <code>flags</code> field is set to an empty Flags
  113. * object. The <code>modified</code> flag is set to true.
  114. */
  115. public MimeMessage(Session session) {
  116. super(session);
  117. modified = true;
  118. headers = new InternetHeaders();
  119. flags = new Flags(); // empty flags object
  120. }
  121. /**
  122. * Constructs a MimeMessage by reading and parsing the data from the
  123. * specified MIME InputStream. The InputStream will be left positioned
  124. * at the end of the data for the message. Note that the input stream
  125. * parse is done within this constructor itself.
  126. *
  127. * @param session Session object for this message
  128. * @param is the message input stream
  129. * @exception MessagingException
  130. */
  131. public MimeMessage(Session session, InputStream is)
  132. throws MessagingException {
  133. super(session);
  134. flags = new Flags(); // empty Flags object
  135. parse(is);
  136. saved = true;
  137. }
  138. /**
  139. * Constructs a new MimeMessage with content initialized from the
  140. * <code>source</code> MimeMessage. The new message is independent
  141. * of the original. <p>
  142. *
  143. * Note: The current implementation is rather inefficient, copying
  144. * the data more times than strictly necessary.
  145. *
  146. * @param source the message to copy content from
  147. * @exception MessagingException
  148. * @since JavaMail 1.2
  149. */
  150. public MimeMessage(MimeMessage source) throws MessagingException {
  151. super(source.session);
  152. flags = source.getFlags();
  153. ByteArrayOutputStream bos;
  154. int size = source.getSize();
  155. if (size > 0)
  156. bos = new ByteArrayOutputStream(size);
  157. else
  158. bos = new ByteArrayOutputStream();
  159. try {
  160. source.writeTo(bos);
  161. bos.close();
  162. SharedByteArrayInputStream bis =
  163. new SharedByteArrayInputStream(bos.toByteArray());
  164. parse(bis);
  165. bis.close();
  166. saved = true;
  167. } catch (IOException ex) {
  168. // should never happen, but just in case...
  169. throw new MessagingException("IOException while copying message",
  170. ex);
  171. }
  172. }
  173. /**
  174. * Constructs an empty MimeMessage object with the given Folder
  175. * and message number. <p>
  176. *
  177. * This method is for providers subclassing <code>MimeMessage</code>.
  178. */
  179. protected MimeMessage(Folder folder, int msgnum) {
  180. super(folder, msgnum);
  181. flags = new Flags(); // empty Flags object
  182. saved = true;
  183. }
  184. /**
  185. * Constructs a MimeMessage by reading and parsing the data from the
  186. * specified MIME InputStream. The InputStream will be left positioned
  187. * at the end of the data for the message. Note that the input stream
  188. * parse is done within this constructor itself. <p>
  189. *
  190. * This method is for providers subclassing <code>MimeMessage</code>.
  191. *
  192. * @param folder The containing folder.
  193. * @param is the message input stream
  194. * @param msgnum Message number of this message within its folder
  195. * @exception MessagingException
  196. */
  197. protected MimeMessage(Folder folder, InputStream is, int msgnum)
  198. throws MessagingException {
  199. this(folder, msgnum);
  200. parse(is);
  201. }
  202. /**
  203. * Constructs a MimeMessage from the given InternetHeaders object
  204. * and content.
  205. *
  206. * This method is for providers subclassing <code>MimeMessage</code>.
  207. *
  208. * @param folder The containing folder.
  209. * @param is the message input stream
  210. * @param msgnum Message number of this message within its folder
  211. * @exception MessagingException
  212. */
  213. protected MimeMessage(Folder folder, InternetHeaders headers,
  214. byte[] content, int msgnum) throws MessagingException {
  215. this(folder, msgnum);
  216. this.headers = headers;
  217. this.content = content;
  218. }
  219. /**
  220. * Parse the InputStream setting the <code>headers</code> and
  221. * <code>content</code> fields appropriately. Also resets the
  222. * <code>modified</code> flag. <p>
  223. *
  224. * This method is intended for use by subclasses that need to
  225. * control when the InputStream is parsed.
  226. *
  227. * @param is The message input stream
  228. * @exception MessagingException
  229. */
  230. protected void parse(InputStream is) throws MessagingException {
  231. if (!(is instanceof ByteArrayInputStream) &&
  232. !(is instanceof BufferedInputStream))
  233. is = new BufferedInputStream(is);
  234. headers = createInternetHeaders(is);
  235. if (is instanceof SharedInputStream) {
  236. SharedInputStream sis = (SharedInputStream)is;
  237. contentStream = sis.newStream(sis.getPosition(), -1);
  238. } else {
  239. try {
  240. content = ASCIIUtility.getBytes(is);
  241. } catch (IOException ioex) {
  242. throw new MessagingException("IOException", ioex);
  243. }
  244. }
  245. modified = false;
  246. }
  247. /**
  248. * Returns the value of the RFC 822 "From" header fields. If this
  249. * header field is absent, the "Sender" header field is used.
  250. * If the "Sender" header field is also absent, <code>null</code>
  251. * is returned.<p>
  252. *
  253. * This implementation uses the <code>getHeader</code> method
  254. * to obtain the requisite header field.
  255. *
  256. * @return Address object
  257. * @exception MessagingException
  258. * @see #headers
  259. */
  260. public Address[] getFrom() throws MessagingException {
  261. Address[] a = getAddressHeader("From");
  262. if (a == null)
  263. a = getAddressHeader("Sender");
  264. return a;
  265. }
  266. /**
  267. * Set the RFC 822 "From" header field. Any existing values are
  268. * replaced with the given address. If address is <code>null</code>,
  269. * this header is removed.
  270. *
  271. * @param address the sender of this message
  272. * @exception IllegalWriteException if the underlying
  273. * implementation does not support modification
  274. * of existing values
  275. * @exception IllegalStateException if this message is
  276. * obtained from a READ_ONLY folder.
  277. * @exception MessagingException
  278. */
  279. public void setFrom(Address address) throws MessagingException {
  280. if (address == null)
  281. removeHeader("From");
  282. else
  283. setHeader("From", address.toString());
  284. }
  285. /**
  286. * Set the RFC 822 "From" header field using the value of the
  287. * <code>InternetAddress.getLocalAddress</code> method.
  288. *
  289. * @exception IllegalWriteException if the underlying
  290. * implementation does not support modification
  291. * of existing values
  292. * @exception IllegalStateException if this message is
  293. * obtained from a READ_ONLY folder.
  294. * @exception MessagingException
  295. */
  296. public void setFrom() throws MessagingException {
  297. InternetAddress me = InternetAddress.getLocalAddress(session);
  298. if (me != null)
  299. setFrom(me);
  300. else
  301. throw new MessagingException("No From address");
  302. }
  303. /**
  304. * Add the specified addresses to the existing "From" field. If
  305. * the "From" field does not already exist, it is created.
  306. *
  307. * @param addresses the senders of this message
  308. * @exception IllegalWriteException if the underlying
  309. * implementation does not support modification
  310. * of existing values
  311. * @exception IllegalStateException if this message is
  312. * obtained from a READ_ONLY folder.
  313. * @exception MessagingException
  314. */
  315. public void addFrom(Address[] addresses) throws MessagingException {
  316. addAddressHeader("From", addresses);
  317. }
  318. /**
  319. * This inner class extends the javax.mail.Message.RecipientType
  320. * class to add additional RecipientTypes. The one additional
  321. * RecipientType currently defined here is NEWSGROUPS.
  322. *
  323. * @see javax.mail.Message.RecipientType
  324. */
  325. public static class RecipientType extends Message.RecipientType {
  326. /**
  327. * The "Newsgroup" (Usenet news) recipients.
  328. */
  329. public static final RecipientType NEWSGROUPS =
  330. new RecipientType("Newsgroups");
  331. protected RecipientType(String type) {
  332. super(type);
  333. }
  334. protected Object readResolve() throws ObjectStreamException {
  335. if (type.equals("Newsgroups"))
  336. return NEWSGROUPS;
  337. else
  338. return super.readResolve();
  339. }
  340. }
  341. /**
  342. * Returns the recepients specified by the type. The mapping
  343. * between the type and the corresponding RFC 822 header is
  344. * as follows:
  345. * <pre>
  346. * Message.RecipientType.TO "To"
  347. * Message.RecipientType.CC "Cc"
  348. * Message.RecipientType.BCC "Bcc"
  349. * MimeMessage.RecipientType.NEWSGROUPS "Newsgroups"
  350. * </pre><br>
  351. *
  352. * Returns null if the header specified by the type is not found
  353. * or if its value is empty. <p>
  354. *
  355. * This implementation uses the <code>getHeader</code> method
  356. * to obtain the requisite header field.
  357. *
  358. * @param Type of recepient
  359. * @return array of Address objects
  360. * @exception MessagingException if header could not
  361. * be retrieved
  362. * @exception AddressException if the header is misformatted
  363. * @see #headers
  364. * @see javax.mail.Message.RecipientType#TO
  365. * @see javax.mail.Message.RecipientType#CC
  366. * @see javax.mail.Message.RecipientType#BCC
  367. * @see javax.mail.internet.MimeMessage.RecipientType#NEWSGROUPS
  368. */
  369. public Address[] getRecipients(Message.RecipientType type)
  370. throws MessagingException {
  371. if (type == RecipientType.NEWSGROUPS) {
  372. String s = getHeader("Newsgroups", ",");
  373. return (s == null) ? null : NewsAddress.parse(s);
  374. } else
  375. return getAddressHeader(getHeaderName(type));
  376. }
  377. /**
  378. * Get all the recipient addresses for the message.
  379. * Extracts the TO, CC, BCC, and NEWSGROUPS recipients.
  380. *
  381. * @return array of Address objects
  382. * @exception MessagingException
  383. * @see javax.mail.Message.RecipientType#TO
  384. * @see javax.mail.Message.RecipientType#CC
  385. * @see javax.mail.Message.RecipientType#BCC
  386. * @see javax.mail.internet.MimeMessage.RecipientType#NEWSGROUPS
  387. */
  388. public Address[] getAllRecipients() throws MessagingException {
  389. Address[] all = super.getAllRecipients();
  390. Address[] ng = getRecipients(RecipientType.NEWSGROUPS);
  391. if (ng == null)
  392. return all; // the common case
  393. int numRecip =
  394. (all != null ? all.length : 0) +
  395. (ng != null ? ng.length : 0);
  396. Address[] addresses = new Address[numRecip];
  397. int pos = 0;
  398. if (all != null) {
  399. System.arraycopy(all, 0, addresses, pos, all.length);
  400. pos += all.length;
  401. }
  402. if (ng != null) {
  403. System.arraycopy(ng, 0, addresses, pos, ng.length);
  404. pos += ng.length;
  405. }
  406. return addresses;
  407. }
  408. /**
  409. * Set the specified recipient type to the given addresses.
  410. * If the address parameter is <code>null</code>, the corresponding
  411. * recipient field is removed.
  412. *
  413. * @param type Recipient type
  414. * @param addresses Addresses
  415. * @exception IllegalWriteException if the underlying
  416. * implementation does not support modification
  417. * of existing values
  418. * @exception IllegalStateException if this message is
  419. * obtained from a READ_ONLY folder.
  420. * @exception MessagingException
  421. * @see #getRecipients
  422. */
  423. public void setRecipients(Message.RecipientType type, Address[] addresses)
  424. throws MessagingException {
  425. if (type == RecipientType.NEWSGROUPS) {
  426. if (addresses == null || addresses.length == 0)
  427. removeHeader("Newsgroups");
  428. else
  429. setHeader("Newsgroups", NewsAddress.toString(addresses));
  430. } else
  431. setAddressHeader(getHeaderName(type), addresses);
  432. }
  433. /**
  434. * Set the specified recipient type to the given addresses.
  435. * If the address parameter is <code>null</code>, the corresponding
  436. * recipient field is removed.
  437. *
  438. * @param type Recipient type
  439. * @param addresses Addresses
  440. * @exception AddressException if the attempt to parse the
  441. * addresses String fails
  442. * @exception IllegalWriteException if the underlying
  443. * implementation does not support modification
  444. * of existing values
  445. * @exception IllegalStateException if this message is
  446. * obtained from a READ_ONLY folder.
  447. * @exception MessagingException
  448. * @see #getRecipients
  449. * @since JavaMail 1.2
  450. */
  451. public void setRecipients(Message.RecipientType type, String addresses)
  452. throws MessagingException {
  453. if (type == RecipientType.NEWSGROUPS) {
  454. if (addresses == null || addresses.length() == 0)
  455. removeHeader("Newsgroups");
  456. else
  457. setHeader("Newsgroups", addresses);
  458. } else
  459. setAddressHeader(getHeaderName(type), InternetAddress.parse(addresses));
  460. }
  461. /**
  462. * Add the given addresses to the specified recipient type.
  463. *
  464. * @param type Recipient type
  465. * @param addresses Addresses
  466. * @exception IllegalWriteException if the underlying
  467. * implementation does not support modification
  468. * of existing values
  469. * @exception IllegalStateException if this message is
  470. * obtained from a READ_ONLY folder.
  471. * @exception MessagingException
  472. */
  473. public void addRecipients(Message.RecipientType type, Address[] addresses)
  474. throws MessagingException {
  475. if (type == RecipientType.NEWSGROUPS) {
  476. String s = NewsAddress.toString(addresses);
  477. if (s != null)
  478. addHeader("Newsgroups", s);
  479. } else
  480. addAddressHeader(getHeaderName(type), addresses);
  481. }
  482. /**
  483. * Add the given addresses to the specified recipient type.
  484. *
  485. * @param type Recipient type
  486. * @param addresses Addresses
  487. * @exception AddressException if the attempt to parse the
  488. * addresses String fails
  489. * @exception IllegalWriteException if the underlying
  490. * implementation does not support modification
  491. * of existing values
  492. * @exception IllegalStateException if this message is
  493. * obtained from a READ_ONLY folder.
  494. * @exception MessagingException
  495. * @since JavaMail 1.2
  496. */
  497. public void addRecipients(Message.RecipientType type, String addresses)
  498. throws MessagingException {
  499. if (type == RecipientType.NEWSGROUPS) {
  500. if (addresses != null && addresses.length() != 0)
  501. addHeader("Newsgroups", addresses);
  502. } else
  503. addAddressHeader(getHeaderName(type), InternetAddress.parse(addresses));
  504. }
  505. /**
  506. * Return the value of the RFC 822 "Reply-To" header field. If
  507. * this header is unavailable or its value is absent, then
  508. * the <code>getFrom</code> method is called and its value is returned.
  509. *
  510. * This implementation uses the <code>getHeader</code> method
  511. * to obtain the requisite header field.
  512. *
  513. * @exception MessagingException
  514. * @see #headers
  515. */
  516. public Address[] getReplyTo() throws MessagingException {
  517. Address[] a = getAddressHeader("Reply-To");
  518. if (a == null)
  519. a = getFrom();
  520. return a;
  521. }
  522. /**
  523. * Set the RFC 822 "Reply-To" header field. If the address
  524. * parameter is <code>null</code>, this header is removed.
  525. *
  526. * @exception IllegalWriteException if the underlying
  527. * implementation does not support modification
  528. * of existing values
  529. * @exception IllegalStateException if this message is
  530. * obtained from a READ_ONLY folder.
  531. * @exception MessagingException
  532. */
  533. public void setReplyTo(Address[] addresses) throws MessagingException {
  534. setAddressHeader("Reply-To", addresses);
  535. }
  536. // Convenience method to get addresses
  537. private Address[] getAddressHeader(String name)
  538. throws MessagingException {
  539. String s = getHeader(name, ",");
  540. return (s == null) ? null : InternetAddress.parse(s);
  541. }
  542. // Convenience method to set addresses
  543. private void setAddressHeader(String name, Address[] addresses)
  544. throws MessagingException {
  545. String s = InternetAddress.toString(addresses);
  546. if (s == null)
  547. removeHeader(name);
  548. else
  549. setHeader(name, s);
  550. }
  551. private void addAddressHeader(String name, Address[] addresses)
  552. throws MessagingException {
  553. String s = InternetAddress.toString(addresses);
  554. if (s == null)
  555. return;
  556. addHeader(name, s);
  557. }
  558. /**
  559. * Returns the value of the "Subject" header field. Returns null
  560. * if the subject field is unavailable or its value is absent. <p>
  561. *
  562. * If the subject is encoded as per RFC 2047, it is decoded and
  563. * converted into Unicode. If the decoding or conversion fails, the
  564. * raw data is returned as is. <p>
  565. *
  566. * This implementation uses the <code>getHeader</code> method
  567. * to obtain the requisite header field.
  568. *
  569. * @return Subject
  570. * @exception MessagingException
  571. * @see #headers
  572. */
  573. public String getSubject() throws MessagingException {
  574. String rawvalue = getHeader("Subject", null);
  575. if (rawvalue == null)
  576. return null;
  577. try {
  578. return MimeUtility.decodeText(rawvalue);
  579. } catch (UnsupportedEncodingException ex) {
  580. return rawvalue;
  581. }
  582. }
  583. /**
  584. * Set the "Subject" header field. If the subject contains
  585. * non US-ASCII characters, it will be encoded using the
  586. * platform's default charset. If the subject contains only
  587. * US-ASCII characters, no encoding is done and it is used
  588. * as-is. If the subject is null, the existing "Subject" field
  589. * is removed. <p>
  590. *
  591. * Note that if the charset encoding process fails, a
  592. * MessagingException is thrown, and an UnsupportedEncodingException
  593. * is included in the chain of nested exceptions within the
  594. * MessagingException.
  595. *
  596. * @param subject The subject
  597. * @exception IllegalWriteException if the underlying
  598. * implementation does not support modification
  599. * of existing values
  600. * @exception IllegalStateException if this message is
  601. * obtained from a READ_ONLY folder.
  602. * @exception MessagingException. An
  603. * UnsupportedEncodingException may be included
  604. * in the exception chain if the charset
  605. * conversion fails.
  606. */
  607. public void setSubject(String subject) throws MessagingException {
  608. setSubject(subject, null);
  609. }
  610. /**
  611. * Set the "Subject" header field. If the subject contains non
  612. * US-ASCII characters, it will be encoded using the specified
  613. * charset. If the subject contains only US-ASCII characters, no
  614. * encoding is done and it is used as-is. If the subject is null,
  615. * the existing "Subject" header field is removed. <p>
  616. *
  617. * Note that if the charset encoding process fails, a
  618. * MessagingException is thrown, and an UnsupportedEncodingException
  619. * is included in the chain of nested exceptions within the
  620. * MessagingException.
  621. *
  622. * @param subject The subject
  623. * @param charset The charset
  624. * @exception IllegalWriteException if the underlying
  625. * implementation does not support modification
  626. * of existing values
  627. * @exception IllegalStateException if this message is
  628. * obtained from a READ_ONLY folder.
  629. * @exception MessagingException. An
  630. * UnsupportedEncodingException may be included
  631. * in the exception chain if the charset
  632. * conversion fails.
  633. */
  634. public void setSubject(String subject, String charset)
  635. throws MessagingException {
  636. if (subject == null)
  637. removeHeader("Subject");
  638. try {
  639. setHeader("Subject",
  640. MimeUtility.encodeText(subject, charset, null));
  641. } catch (UnsupportedEncodingException uex) {
  642. throw new MessagingException("Encoding error", uex);
  643. }
  644. }
  645. /**
  646. * Returns the value of the RFC 822 "Date" field. This is the date
  647. * on which this message was sent. Returns null if this field is
  648. * unavailable or its value is absent. <p>
  649. *
  650. * This implementation uses the <code>getHeader</code> method
  651. * to obtain the requisite header field.
  652. *
  653. * @return The sent Date
  654. * @exception MessagingException
  655. */
  656. public Date getSentDate() throws MessagingException {
  657. String s = getHeader("Date", null);
  658. if (s != null) {
  659. try {
  660. synchronized (mailDateFormat) {
  661. return mailDateFormat.parse(s);
  662. }
  663. } catch (ParseException pex) {
  664. return null;
  665. }
  666. }
  667. return null;
  668. }
  669. /**
  670. * Set the RFC 822 "Date" header field. This is the date on which the
  671. * creator of the message indicates that the message is complete
  672. * and ready for delivery. If the date parameter is
  673. * <code>null</code>, the existing "Date" field is removed.
  674. *
  675. * @exception IllegalWriteException if the underlying
  676. * implementation does not support modification
  677. * @exception IllegalStateException if this message is
  678. * obtained from a READ_ONLY folder.
  679. * @exception MessagingException
  680. */
  681. public void setSentDate(Date d) throws MessagingException {
  682. if (d == null)
  683. removeHeader("Date");
  684. else {
  685. synchronized (mailDateFormat) {
  686. setHeader("Date", mailDateFormat.format(d));
  687. }
  688. }
  689. }
  690. /**
  691. * Returns the Date on this message was received. Returns
  692. * <code>null</code> if this date cannot be obtained. <p>
  693. *
  694. * Note that RFC 822 does not define a field for the received
  695. * date. Hence only implementations that can provide this date
  696. * need return a valid value. <p>
  697. *
  698. * This implementation returns <code>null</code>.
  699. *
  700. * @return the date this message was received
  701. * @exception MessagingException
  702. */
  703. public Date getReceivedDate() throws MessagingException {
  704. return null;
  705. }
  706. /**
  707. * Return the size of the content of this message in bytes.
  708. * Return -1 if the size cannot be determined. <p>
  709. *
  710. * Note that this number may not be an exact measure of the
  711. * content size and may or may not account for any transfer
  712. * encoding of the content. <p>
  713. *
  714. * This implementation returns the size of the <code>content</code>
  715. * array (if not null), or, if <code>contentStream</code> is not
  716. * null, and the <code>available</code> method returns a positive
  717. * number, it returns that number as the size. Otherwise, it returns
  718. * -1.
  719. *
  720. * @return size of content in bytes
  721. * @exception MessagingException
  722. */
  723. public int getSize() throws MessagingException {
  724. if (content != null)
  725. return content.length;
  726. if (contentStream != null) {
  727. try {
  728. int size = contentStream.available();
  729. // only believe the size if it's greate than zero, since zero
  730. // is the default returned by the InputStream class itself
  731. if (size > 0)
  732. return size;
  733. } catch (IOException ex) {
  734. // ignore it
  735. }
  736. }
  737. return -1;
  738. }
  739. /**
  740. * Return the number of lines for the content of this message.
  741. * Return -1 if this number cannot be determined. <p>
  742. *
  743. * Note that this number may not be an exact measure of the
  744. * content length and may or may not account for any transfer
  745. * encoding of the content. <p>
  746. *
  747. * This implementation returns -1.
  748. *
  749. * @return number of lines in the content.
  750. * @exception MessagingException
  751. */
  752. public int getLineCount() throws MessagingException {
  753. return -1;
  754. }
  755. /**
  756. * Returns the value of the RFC 822 "Content-Type" header field.
  757. * This represents the content-type of the content of this
  758. * message. This value must not be null. If this field is
  759. * unavailable, "text/plain" should be returned. <p>
  760. *
  761. * This implementation uses the <code>getHeader</code> method
  762. * to obtain the requisite header field.
  763. *
  764. * @return The ContentType of this part
  765. * @exception MessagingException
  766. * @see javax.activation.DataHandler
  767. */
  768. public String getContentType() throws MessagingException {
  769. String s = getHeader("Content-Type", null);
  770. if (s == null)
  771. return "text/plain";
  772. return s;
  773. }
  774. /**
  775. * Is this Part of the specified MIME type? This method
  776. * compares <strong>only the <code>primaryType</code> and
  777. * <code>subType</code></strong>.
  778. * The parameters of the content types are ignored. <p>
  779. *
  780. * For example, this method will return <code>true</code> when
  781. * comparing a Part of content type <strong>"text/plain"</strong>
  782. * with <strong>"text/plain; charset=foobar"</strong>. <p>
  783. *
  784. * If the <code>subType</code> of <code>mimeType</code> is the
  785. * special character '*', then the subtype is ignored during the
  786. * comparison.
  787. */
  788. public boolean isMimeType(String mimeType) throws MessagingException {
  789. return MimeBodyPart.isMimeType(this, mimeType);
  790. }
  791. /**
  792. * Returns the value of the "Content-Disposition" header field.
  793. * This represents the disposition of this part. The disposition
  794. * describes how the part should be presented to the user. <p>
  795. *
  796. * If the Content-Disposition field is unavailable,
  797. * <code>null</code> is returned. <p>
  798. *
  799. * This implementation uses the <code>getHeader</code> method
  800. * to obtain the requisite header field.
  801. *
  802. * @return disposition of this part, or null if unknown
  803. * @exception MessagingException
  804. */
  805. public String getDisposition() throws MessagingException {
  806. return MimeBodyPart.getDisposition(this);
  807. }
  808. /**
  809. * Set the "Content-Disposition" header field of this Message.
  810. * If <code>disposition</code> is null, any existing "Content-Disposition"
  811. * header field is removed.
  812. *
  813. * @exception IllegalWriteException if the underlying
  814. * implementation does not support modification
  815. * @exception IllegalStateException if this message is
  816. * obtained from a READ_ONLY folder.
  817. * @exception MessagingException
  818. */
  819. public void setDisposition(String disposition) throws MessagingException {
  820. MimeBodyPart.setDisposition(this, disposition);
  821. }
  822. /**
  823. * Returns the content transfer encoding from the
  824. * "Content-Transfer-Encoding" header
  825. * field. Returns <code>null</code> if the header is unavailable
  826. * or its value is absent. <p>
  827. *
  828. * This implementation uses the <code>getHeader</code> method
  829. * to obtain the requisite header field.
  830. *
  831. * @return content-transfer-encoding
  832. * @exception MessagingException
  833. */
  834. public String getEncoding() throws MessagingException {
  835. return MimeBodyPart.getEncoding(this);
  836. }
  837. /**
  838. * Returns the value of the "Content-ID" header field. Returns
  839. * <code>null</code> if the field is unavailable or its value is
  840. * absent. <p>
  841. *
  842. * This implementation uses the <code>getHeader</code> method
  843. * to obtain the requisite header field.
  844. *
  845. * @return content-ID
  846. * @exception MessagingException
  847. */
  848. public String getContentID() throws MessagingException {
  849. return getHeader("Content-Id", null);
  850. }
  851. /**
  852. * Set the "Content-ID" header field of this Message.
  853. * If the <code>cid</code> parameter is null, any existing
  854. * "Content-ID" is removed.
  855. *
  856. * @exception IllegalWriteException if the underlying
  857. * implementation does not support modification
  858. * @exception IllegalStateException if this message is
  859. * obtained from a READ_ONLY folder.
  860. * @exception MessagingException
  861. */
  862. public void setContentID(String cid) throws MessagingException {
  863. if (cid == null)
  864. removeHeader("Content-ID");
  865. else
  866. setHeader("Content-ID", cid);
  867. }
  868. /**
  869. * Return the value of the "Content-MD5" header field. Returns
  870. * <code>null</code> if this field is unavailable or its value
  871. * is absent. <p>
  872. *
  873. * This implementation uses the <code>getHeader</code> method
  874. * to obtain the requisite header field.
  875. *
  876. * @return content-MD5
  877. * @exception MessagingException
  878. */
  879. public String getContentMD5() throws MessagingException {
  880. return getHeader("Content-MD5", null);
  881. }
  882. /**
  883. * Set the "Content-MD5" header field of this Message.
  884. *
  885. * @exception IllegalWriteException if the underlying
  886. * implementation does not support modification
  887. * @exception IllegalStateException if this message is
  888. * obtained from a READ_ONLY folder.
  889. * @exception MessagingException
  890. */
  891. public void setContentMD5(String md5) throws MessagingException {
  892. setHeader("Content-MD5", md5);
  893. }
  894. /**
  895. * Returns the "Content-Description" header field of this Message.
  896. * This typically associates some descriptive information with
  897. * this part. Returns null if this field is unavailable or its
  898. * value is absent. <p>
  899. *
  900. * If the Content-Description field is encoded as per RFC 2047,
  901. * it is decoded and converted into Unicode. If the decoding or
  902. * conversion fails, the raw data is returned as-is <p>
  903. *
  904. * This implementation uses the <code>getHeader</code> method
  905. * to obtain the requisite header field.
  906. *
  907. * @return content-description
  908. * @exception MessagingException
  909. */
  910. public String getDescription() throws MessagingException {
  911. return MimeBodyPart.getDescription(this);
  912. }
  913. /**
  914. * Set the "Content-Description" header field for this Message.
  915. * If the description parameter is <code>null</code>, then any
  916. * existing "Content-Description" fields are removed. <p>
  917. *
  918. * If the description contains non US-ASCII characters, it will
  919. * be encoded using the platform's default charset. If the
  920. * description contains only US-ASCII characters, no encoding
  921. * is done and it is used as-is. <p>
  922. *
  923. * Note that if the charset encoding process fails, a
  924. * MessagingException is thrown, and an UnsupportedEncodingException
  925. * is included in the chain of nested exceptions within the
  926. * MessagingException.
  927. *
  928. * @param description content-description
  929. * @exception IllegalWriteException if the underlying
  930. * implementation does not support modification
  931. * @exception IllegalStateException if this message is
  932. * obtained from a READ_ONLY folder.
  933. * @exception MessagingException. An
  934. * UnsupportedEncodingException may be included
  935. * in the exception chain if the charset
  936. * conversion fails.
  937. */
  938. public void setDescription(String description) throws MessagingException {
  939. setDescription(description, null);
  940. }
  941. /**
  942. * Set the "Content-Description" header field for this Message.
  943. * If the description parameter is <code>null</code>, then any
  944. * existing "Content-Description" fields are removed. <p>
  945. *
  946. * If the description contains non US-ASCII characters, it will
  947. * be encoded using the specified charset. If the description
  948. * contains only US-ASCII characters, no encoding is done and
  949. * it is used as-is. <p>
  950. *
  951. * Note that if the charset encoding process fails, a
  952. * MessagingException is thrown, and an UnsupportedEncodingException
  953. * is included in the chain of nested exceptions within the
  954. * MessagingException.
  955. *
  956. * @param description Description
  957. * @param charset Charset for encoding
  958. * @exception IllegalWriteException if the underlying
  959. * implementation does not support modification
  960. * @exception IllegalStateException if this message is
  961. * obtained from a READ_ONLY folder.
  962. * @exception MessagingException. An
  963. * UnsupportedEncodingException may be included
  964. * in the exception chain if the charset
  965. * conversion fails.
  966. */
  967. public void setDescription(String description, String charset)
  968. throws MessagingException {
  969. MimeBodyPart.setDescription(this, description, charset);
  970. }
  971. /**
  972. * Get the languages specified in the "Content-Language" header
  973. * field of this message. The Content-Language header is defined by
  974. * RFC 1766. Returns <code>null</code> if this field is unavailable
  975. * or its value is absent. <p>
  976. *
  977. * This implementation uses the <code>getHeader</code> method
  978. * to obtain the requisite header field.
  979. *
  980. * @return value of content-language header.
  981. * @exception MessagingException
  982. */
  983. public String[] getContentLanguage() throws MessagingException {
  984. return MimeBodyPart.getContentLanguage(this);
  985. }
  986. /**
  987. * Set the "Content-Language" header of this MimePart. The
  988. * Content-Language header is defined by RFC 1766.
  989. *
  990. * @param languages array of language tags
  991. * @exception IllegalWriteException if the underlying
  992. * implementation does not support modification
  993. * @exception IllegalStateException if this message is
  994. * obtained from a READ_ONLY folder.
  995. * @exception MessagingException
  996. */
  997. public void setContentLanguage(String[] languages)
  998. throws MessagingException {
  999. MimeBodyPart.setContentLanguage(this, languages);
  1000. }
  1001. /**
  1002. * Returns the value of the "Message-ID" header field. Returns
  1003. * null if this field is unavailable or its value is absent. <p>
  1004. *
  1005. * The default implementation provided here uses the
  1006. * <code>getHeader</code> method to return the value of the
  1007. * "Message-ID" field.
  1008. *
  1009. * @return Message-ID
  1010. * @exception MessagingException if the retrieval of this field
  1011. * causes any exception.
  1012. * @see javax.mail.search.MessageIDTerm
  1013. * @since JavaMail 1.1
  1014. */
  1015. public String getMessageID() throws MessagingException {
  1016. return getHeader("Message-ID", null);
  1017. }
  1018. /**
  1019. * Get the filename associated with this Message. <p>
  1020. *
  1021. * Returns the value of the "filename" parameter from the
  1022. * "Content-Disposition" header field of this message. If it's
  1023. * not available, returns the value of the "name" parameter from
  1024. * the "Content-Type" header field of this BodyPart.
  1025. * Returns <code>null</code> if both are absent.
  1026. *
  1027. * @return filename
  1028. * @exception MessagingException
  1029. */
  1030. public String getFileName() throws MessagingException {
  1031. return MimeBodyPart.getFileName(this);
  1032. }
  1033. /**
  1034. * Set the filename associated with this part, if possible. <p>
  1035. *
  1036. * Sets the "filename" parameter of the "Content-Disposition"
  1037. * header field of this message.
  1038. *
  1039. * @exception IllegalWriteException if the underlying
  1040. * implementation does not support modification
  1041. * @exception IllegalStateException if this message is
  1042. * obtained from a READ_ONLY folder.
  1043. * @exception MessagingException
  1044. */
  1045. public void setFileName(String filename) throws MessagingException {
  1046. MimeBodyPart.setFileName(this, filename);
  1047. }
  1048. private String getHeaderName(Message.RecipientType type)
  1049. throws MessagingException {
  1050. String headerName;
  1051. if (type == Message.RecipientType.TO)
  1052. headerName = "To";
  1053. else if (type == Message.RecipientType.CC)
  1054. headerName = "Cc";
  1055. else if (type == Message.RecipientType.BCC)
  1056. headerName = "Bcc";
  1057. else if (type == MimeMessage.RecipientType.NEWSGROUPS)
  1058. headerName = "Newsgroups";
  1059. else
  1060. throw new MessagingException("Invalid Recipient Type");
  1061. return headerName;
  1062. }
  1063. /**
  1064. * Return a decoded input stream for this Message's "content". <p>
  1065. *
  1066. * This implementation obtains the input stream from the DataHandler,
  1067. * that is, it invokes <code>getDataHandler().getInputStream()</code>.
  1068. *
  1069. * @return an InputStream
  1070. * @exception MessagingException
  1071. * @exception IOException this is typically thrown by the
  1072. * DataHandler. Refer to the documentation for
  1073. * javax.activation.DataHandler for more details.
  1074. *
  1075. * @see #getContentStream
  1076. * @see javax.activation.DataHandler#getInputStream
  1077. */
  1078. public InputStream getInputStream()
  1079. throws IOException, MessagingException {
  1080. return getDataHandler().getInputStream();
  1081. }
  1082. /**
  1083. * Produce the raw bytes of the content. This method is used during
  1084. * parsing, to create a DataHandler object for the content. Subclasses
  1085. * that can provide a separate input stream for just the message
  1086. * content might want to override this method. <p>
  1087. *
  1088. * This implementation just returns a ByteArrayInputStream constructed
  1089. * out of the <code>content</code> byte array.
  1090. *
  1091. * @see #content
  1092. */
  1093. protected InputStream getContentStream() throws MessagingException {
  1094. if (contentStream != null)
  1095. return ((SharedInputStream)contentStream).newStream(0, -1);
  1096. if (content != null)
  1097. return new SharedByteArrayInputStream(content);
  1098. throw new MessagingException("No content");
  1099. }
  1100. /**
  1101. * Return an InputStream to the raw data with any Content-Transfer-Encoding
  1102. * intact. This method is useful if the "Content-Transfer-Encoding"
  1103. * header is incorrect or corrupt, which would prevent the
  1104. * <code>getInputStream</code> method or <code>getContent</code> method
  1105. * from returning the correct data. In such a case the application may
  1106. * use this method and attempt to decode the raw data itself. <p>
  1107. *
  1108. * This implementation simply calls the <code>getContentStream</code>
  1109. * method.
  1110. *
  1111. * @see #getInputStream
  1112. * @see #getContentStream
  1113. * @since JavaMail 1.2
  1114. */
  1115. public InputStream getRawInputStream() throws MessagingException {
  1116. return getContentStream();
  1117. }
  1118. /**
  1119. * Return a DataHandler for this Message's content. <p>
  1120. *
  1121. * The implementation provided here works as follows. Note the use of
  1122. * the <code>getContentStream</code> method to
  1123. * generate the byte stream for the content. Also note that
  1124. * any transfer-decoding is done automatically within this method.<p>
  1125. *
  1126. * <blockquote><pre>
  1127. * getDataHandler() {
  1128. * if (dh == null) {
  1129. * dh = new DataHandler(new MimePartDataSource(this));
  1130. * }
  1131. * return dh;
  1132. * }
  1133. * <p>
  1134. * class MimePartDataSource implements DataSource {
  1135. * public getInputStream() {
  1136. * return MimeUtility.decode(
  1137. * getContentStream(), getEncoding());
  1138. * }
  1139. *
  1140. * .... <other DataSource methods>
  1141. * }
  1142. * </pre></blockquote><p>
  1143. *
  1144. * @exception MessagingException
  1145. */
  1146. public synchronized DataHandler getDataHandler()
  1147. throws MessagingException {
  1148. if (dh == null)
  1149. dh = new DataHandler(new MimePartDataSource(this));
  1150. return dh;
  1151. }
  1152. /**
  1153. * Return the content as a Java object. The type of this
  1154. * object is dependent on the content itself. For
  1155. * example, the native format of a "text/plain" content
  1156. * is usually a String object. The native format for a "multipart"
  1157. * message is always a Multipart subclass. For content types that are
  1158. * unknown to the DataHandler system, an input stream is returned
  1159. * as the content. <p>
  1160. *
  1161. * This implementation obtains the content from the DataHandler,
  1162. * that is, it invokes <code>getDataHandler().getContent()</code>.
  1163. *
  1164. * @return Object
  1165. * @see javax.mail.Part
  1166. * @see javax.activation.DataHandler#getContent
  1167. * @exception MessagingException
  1168. * @exception IOException this is typically thrown by the
  1169. * DataHandler. Refer to the documentation for
  1170. * javax.activation.DataHandler for more details.
  1171. */
  1172. public Object getContent() throws IOException, MessagingException {
  1173. return getDataHandler().getContent();
  1174. }
  1175. /**
  1176. * This method provides the mechanism to set this part's content.
  1177. * The given DataHandler object should wrap the actual content.
  1178. *
  1179. * @param dh The DataHandler for the content.
  1180. * @exception IllegalWriteException if the underlying
  1181. * implementation does not support modification
  1182. * @exception IllegalStateException if this message is
  1183. * obtained from a READ_ONLY folder.
  1184. * @exception MessagingException
  1185. */
  1186. public void setDataHandler(DataHandler dh)
  1187. throws MessagingException {
  1188. this.dh = dh;
  1189. MimeBodyPart.invalidateContentHeaders(this);
  1190. }
  1191. /**
  1192. * A convenience method for setting this Message's content. <p>
  1193. *
  1194. * The content is wrapped in a DataHandler object. Note that a
  1195. * DataContentHandler class for the specified type should be
  1196. * available to the JavaMail implementation for this to work right.
  1197. * i.e., to do <code>setContent(foobar, "application/x-foobar")</code>,
  1198. * a DataContentHandler for "application/x-foobar" should be installed.
  1199. * Refer to the Java Activation Framework for more information.
  1200. *
  1201. * @param o the content object
  1202. * @param type Mime type of the object
  1203. * @exception IllegalWriteException if the underlying
  1204. * implementation does not support modification of
  1205. * existing values
  1206. * @exception IllegalStateException if this message is
  1207. * obtained from a READ_ONLY folder.
  1208. * @exception MessagingException
  1209. */
  1210. public void setContent(Object o, String type)
  1211. throws MessagingException {
  1212. setDataHandler(new DataHandler(o, type));
  1213. }
  1214. /**
  1215. * Convenience method that sets the given String as this
  1216. * part's content, with a MIME type of "text/plain". If the
  1217. * string contains non US-ASCII characters. it will be encoded
  1218. * using the platform's default charset. The charset is also
  1219. * used to set the "charset" parameter.<p>
  1220. *
  1221. * Note that there may be a performance penalty if
  1222. * <code>text</code> is large, since this method may have
  1223. * to scan all the characters to determine what charset to
  1224. * use. <p>
  1225. *
  1226. * If the charset is already known, use the
  1227. * <code>setText</code> method that takes the charset parameter.
  1228. *
  1229. * @see #setText(String text, String charset)
  1230. */
  1231. public void setText(String text) throws MessagingException {
  1232. setText(text, null);
  1233. }
  1234. /**
  1235. * Convenience method that sets the given String as this part's
  1236. * content, with a MIME type of "text/plain" and the specified
  1237. * charset. The given Unicode string will be charset-encoded
  1238. * using the specified charset. The charset is also used to set
  1239. * the "charset" parameter.
  1240. */
  1241. public void setText(String text, String charset)
  1242. throws MessagingException {
  1243. MimeBodyPart.setText(this, text, charset);
  1244. }
  1245. /**
  1246. * This method sets the Message's content to a Multipart object.
  1247. *
  1248. * @param mp The multipart object that is the Message's content
  1249. * @exception IllegalWriteException if the underlying
  1250. * implementation does not support modification of
  1251. * existing values
  1252. * @exception IllegalStateException if this message is
  1253. * obtained from a READ_ONLY folder.
  1254. * @exception MessagingException
  1255. */
  1256. public void setContent(Multipart mp) throws MessagingException {
  1257. setDataHandler(new DataHandler(mp, mp.getContentType()));
  1258. mp.setParent(this);
  1259. }
  1260. /**
  1261. * Get a new Message suitable for a reply to this message.
  1262. * The new Message will have its attributes and headers
  1263. * set up appropriately. Note that this new message object
  1264. * will be empty, i.e., it will <strong>not</strong> have a "content".
  1265. * These will have to be suitably filled in by the client. <p>
  1266. *
  1267. * If <code>replyToAll</code> is set, the new Message will be addressed
  1268. * to all recipients of this message. Otherwise, the reply will be
  1269. * addressed to only the sender of this message (using the value
  1270. * of the <code>getReplyTo</code> method). <p>
  1271. *
  1272. * The "Subject" field is filled in with the original subject
  1273. * prefixed with "Re:" (unless it already starts with "Re:").
  1274. * The "In-Reply-To" header is set in the new message if this
  1275. * message has a "Message-Id" header. The <code>ANSWERED</code>
  1276. * flag is set in this message.
  1277. *
  1278. * @param replyToAll reply should be sent to all recipients
  1279. * of this message
  1280. * @return the reply Message
  1281. * @exception MessagingException
  1282. */
  1283. public Message reply(boolean replyToAll) throws MessagingException {
  1284. MimeMessage reply = new MimeMessage(session);
  1285. /*
  1286. * Have to manipulate the raw Subject header so that we don't lose
  1287. * any encoding information. This is safe because "Re:" isn't
  1288. * internationalized and (generally) isn't encoded. If the entire
  1289. * Subject header is encoded, prefixing it with "Re: " still leaves
  1290. * a valid and correct encoded header.
  1291. */
  1292. String subject = getHeader("Subject", null);
  1293. if (subject != null) {
  1294. if (!subject.regionMatches(true, 0, "Re: ", 0, 4))
  1295. subject = "Re: " + subject;
  1296. reply.setHeader("Subject", subject);
  1297. }
  1298. Address a[] = getReplyTo();
  1299. reply.setRecipients(Message.RecipientType.TO, a);
  1300. if (replyToAll) {
  1301. Vector v = new Vector();
  1302. // add my own address to list
  1303. InternetAddress me = InternetAddress.getLocalAddress(session);
  1304. if (me != null)
  1305. v.addElement(me);
  1306. // add any alternate names I'm known by
  1307. String alternates = session.getProperty("mail.alternates");
  1308. if (alternates != null)
  1309. eliminateDuplicates(v,
  1310. InternetAddress.parse(alternates, false));
  1311. // should we Cc all other original recipients?
  1312. String replyallccStr = session.getProperty("mail.replyallcc");
  1313. boolean replyallcc =
  1314. replyallccStr != null && replyallccStr.equalsIgnoreCase("true");
  1315. // add the recipients from the To field so far
  1316. eliminateDuplicates(v, a);
  1317. a = getRecipients(Message.RecipientType.TO);
  1318. a = eliminateDuplicates(v, a);
  1319. if (a != null && a.length > 0) {
  1320. if (replyallcc)
  1321. reply.addRecipients(Message.RecipientType.CC, a);
  1322. else
  1323. reply.addRecipients(Message.RecipientType.TO, a);
  1324. }
  1325. a = getRecipients(Message.RecipientType.CC);
  1326. a = eliminateDuplicates(v, a);
  1327. if (a != null && a.length > 0)
  1328. reply.addRecipients(Message.RecipientType.CC, a);
  1329. // don't eliminate duplicate newsgroups
  1330. a = getRecipients(RecipientType.NEWSGROUPS);
  1331. if (a != null && a.length > 0)
  1332. reply.setRecipients(RecipientType.NEWSGROUPS, a);
  1333. }
  1334. String msgId = getHeader("Message-Id", null);
  1335. if (msgId != null)
  1336. reply.setHeader("In-Reply-To", msgId);
  1337. try {
  1338. setFlags(answeredFlag, true);
  1339. } catch (MessagingException mex) {
  1340. // ignore it
  1341. }
  1342. return reply;
  1343. }
  1344. // used above in reply()
  1345. private static final Flags answeredFlag = new Flags(Flags.Flag.ANSWERED);
  1346. /**
  1347. * Check addrs for any duplicates that may already be in v.
  1348. * Return a new array without the duplicates. Add any new
  1349. * addresses to v. Note that the input array may be modified.
  1350. */
  1351. private Address[] eliminateDuplicates(Vector v, Address[] addrs) {
  1352. if (addrs == null)
  1353. return null;
  1354. int gone = 0;
  1355. for (int i = 0; i < addrs.length; i++) {
  1356. boolean found = false;
  1357. // search the vector for this address
  1358. for (int j = 0; j < v.size(); j++) {
  1359. if (((InternetAddress)v.elementAt(j)).equals(addrs[i])) {
  1360. // found it; count it and remove it from the input array
  1361. found = true;
  1362. gone++;
  1363. addrs[i] = null;
  1364. break;
  1365. }
  1366. }
  1367. if (!found)
  1368. v.addElement(addrs[i]); // add new address to vector
  1369. }
  1370. // if we found any duplicates, squish the array
  1371. if (gone != 0) {
  1372. Address[] a;
  1373. // new array should be same type as original array
  1374. // XXX - there must be a better way, perhaps reflection?
  1375. if (addrs instanceof InternetAddress[])
  1376. a = new InternetAddress[addrs.length - gone];
  1377. else
  1378. a = new Address[addrs.length - gone];
  1379. for (int i = 0, j = 0; i < addrs.length; i++)
  1380. if (addrs[i] != null)
  1381. a[j++] = addrs[i];
  1382. addrs = a;
  1383. }
  1384. return addrs;
  1385. }
  1386. /**
  1387. * Output the message as an RFC 822 format stream. <p>
  1388. *
  1389. * Note that, depending on how the messag was constructed, it may
  1390. * use a variety of line termination conventions. Generally the
  1391. * output should be sent through an appropriate FilterOutputStream
  1392. * that converts the line terminators to the desired form, either
  1393. * CRLF for MIME compatibility and for use in Internet protocols,
  1394. * or the local platform's line terminator for storage in a local
  1395. * text file. <p>
  1396. *
  1397. * This implementation calls the <code>writeTo(OutputStream,
  1398. * String[])</code> method with a null ignore list.
  1399. *
  1400. * @exception IOException if an error occurs writing to the stream
  1401. * or if an error is generated by the
  1402. * javax.activation layer.
  1403. * @exception MessagingException
  1404. * @see javax.activation.DataHandler#writeTo
  1405. */
  1406. public void writeTo(OutputStream os)
  1407. throws IOException, MessagingException {
  1408. writeTo(os, null);
  1409. }
  1410. /**
  1411. * Output the message as an RFC 822 format stream, without
  1412. * specified headers. If the <code>saved</code> flag is not set,
  1413. * the <code>saveChanges</code> method is called.
  1414. * If the <code>modified</code> flag is not
  1415. * set and the <code>content</code> array is not null, the
  1416. * <code>content</code> array is written directly, after
  1417. * writing the appropriate message headers.
  1418. *
  1419. * @exception javax.mail.MessagingException
  1420. * @exception IOException if an error occurs writing to the stream
  1421. * or if an error is generated by the
  1422. * javax.activation layer.
  1423. * @see javax.activation.DataHandler#writeTo
  1424. */
  1425. public void writeTo(OutputStream os, String[] ignoreList)
  1426. throws IOException, MessagingException {
  1427. if (!saved)
  1428. saveChanges();
  1429. if (modified || (content == null && contentStream == null)) {
  1430. MimeBodyPart.writeTo(this, os, ignoreList);
  1431. return;
  1432. }
  1433. // Else, the content is untouched, so we can just output it
  1434. // First, write out the header
  1435. Enumeration hdrLines = getNonMatchingHeaderLines(ignoreList);
  1436. LineOutputStream los = new LineOutputStream(os);
  1437. while (hdrLines.hasMoreElements())
  1438. los.writeln((String)hdrLines.nextElement());
  1439. // The CRLF separator between header and content
  1440. los.writeln();
  1441. // Finally, the content.
  1442. if (contentStream != null) {
  1443. // copy the data to the output stream
  1444. InputStream is =
  1445. ((SharedInputStream)contentStream).newStream(0, -1);
  1446. byte[] buf = new byte[8192];
  1447. int len;
  1448. while ((len = is.read(buf)) > 0)
  1449. os.write(buf, 0, len);
  1450. is.close();
  1451. buf = null;
  1452. } else {
  1453. os.write(content);
  1454. }
  1455. os.flush();
  1456. }
  1457. /**
  1458. * Get all the headers for this header_name. Note that certain
  1459. * headers may be encoded as per RFC 2047 if they contain
  1460. * non US-ASCII characters and these should be decoded. <p>
  1461. *
  1462. * This implementation obtains the headers from the
  1463. * <code>headers</code> InternetHeaders object.
  1464. *
  1465. * @param name name of header
  1466. * @return array of headers
  1467. * @exception MessagingException
  1468. * @see javax.mail.internet.MimeUtility
  1469. */
  1470. public String[] getHeader(String name)
  1471. throws MessagingException {
  1472. return headers.getHeader(name);
  1473. }
  1474. /**
  1475. * Get all the headers for this header name, returned as a single
  1476. * String, with headers separated by the delimiter. If the
  1477. * delimiter is <code>null</code>, only the first header is
  1478. * returned.
  1479. *
  1480. * @param header_name the name of this header
  1481. * @return the value fields for all headers with
  1482. * this name
  1483. * @exception MessagingException
  1484. */
  1485. public String getHeader(String name, String delimiter)
  1486. throws MessagingException {
  1487. return headers.getHeader(name, delimiter);
  1488. }
  1489. /**
  1490. * Set the value for this header_name. Replaces all existing
  1491. * header values with this new value. Note that RFC 822 headers
  1492. * must contain only US-ASCII characters, so a header that
  1493. * contains non US-ASCII characters must have been encoded by the
  1494. * caller as per the rules of RFC 2047.
  1495. *
  1496. * @param name header name
  1497. * @param value header value
  1498. * @see javax.mail.internet.MimeUtility
  1499. * @exception IllegalWriteException if the underlying
  1500. * implementation does not support modification
  1501. * @exception IllegalStateException if this message is
  1502. * obtained from a READ_ONLY folder.
  1503. * @exception MessagingException
  1504. */
  1505. public void setHeader(String name, String value)
  1506. throws MessagingException {
  1507. headers.setHeader(name, value);
  1508. }
  1509. /**
  1510. * Add this value to the existing values for this header_name.
  1511. * Note that RFC 822 headers must contain only US-ASCII
  1512. * characters, so a header that contains non US-ASCII characters
  1513. * must have been encoded as per the rules of RFC 2047.
  1514. *
  1515. * @param name header name
  1516. * @param value header value
  1517. * @see javax.mail.internet.MimeUtility
  1518. * @exception IllegalWriteException if the underlying
  1519. * implementation does not support modification
  1520. * @exception IllegalStateException if this message is
  1521. * obtained from a READ_ONLY folder.
  1522. * @exception MessagingException
  1523. */
  1524. public void addHeader(String name, String value)
  1525. throws MessagingException {
  1526. headers.addHeader(name, value);
  1527. }
  1528. /**
  1529. * Remove all headers with this name.
  1530. * @exception IllegalWriteException if the underlying
  1531. * implementation does not support modification
  1532. * @exception IllegalStateException if this message is
  1533. * obtained from a READ_ONLY folder.
  1534. * @exception MessagingException
  1535. */
  1536. public void removeHeader(String name)
  1537. throws MessagingException {
  1538. headers.removeHeader(name);
  1539. }
  1540. /**
  1541. * Return all the headers from this Message as an enumeration
  1542. * of Header objects. <p>
  1543. *
  1544. * Note that certain headers may be encoded as per RFC 2047
  1545. * if they contain non US-ASCII characters and these should
  1546. * be decoded. <p>
  1547. *
  1548. * This implementation obtains the headers from the
  1549. * <code>headers</code> InternetHeaders object.
  1550. *
  1551. * @return array of header objects
  1552. * @exception MessagingException
  1553. * @see javax.mail.internet.MimeUtility
  1554. */
  1555. public Enumeration getAllHeaders() throws MessagingException {
  1556. return headers.getAllHeaders();
  1557. }
  1558. /**
  1559. * Return matching headers from this Message as an Enumeration of
  1560. * Header objects. This implementation obtains the headers from
  1561. * the <code>headers</code> InternetHeaders object.
  1562. *
  1563. * @exception MessagingException
  1564. */
  1565. public Enumeration getMatchingHeaders(String[] names)
  1566. throws MessagingException {
  1567. return headers.getMatchingHeaders(names);
  1568. }
  1569. /**
  1570. * Return non-matching headers from this Message as an
  1571. * Enumeration of Header objects. This implementation
  1572. * obtains the header from the <code>headers</code> InternetHeaders object.
  1573. *
  1574. * @exception MessagingException
  1575. */
  1576. public Enumeration getNonMatchingHeaders(String[] names)
  1577. throws MessagingException {
  1578. return headers.getNonMatchingHeaders(names);
  1579. }
  1580. /**
  1581. * Add a raw RFC 822 header-line.
  1582. *
  1583. * @exception IllegalWriteException if the underlying
  1584. * implementation does not support modification
  1585. * @exception IllegalStateException if this message is
  1586. * obtained from a READ_ONLY folder.
  1587. * @exception MessagingException
  1588. */
  1589. public void addHeaderLine(String line) throws MessagingException {
  1590. headers.addHeaderLine(line);
  1591. }
  1592. /**
  1593. * Get all header lines as an Enumeration of Strings. A Header
  1594. * line is a raw RFC 822 header-line, containing both the "name"
  1595. * and "value" field.
  1596. *
  1597. * @exception MessagingException
  1598. */
  1599. public Enumeration getAllHeaderLines() throws MessagingException {
  1600. return headers.getAllHeaderLines();
  1601. }
  1602. /**
  1603. * Get matching header lines as an Enumeration of Strings.
  1604. * A Header line is a raw RFC 822 header-line, containing both
  1605. * the "name" and "value" field.
  1606. *
  1607. * @exception MessagingException
  1608. */
  1609. public Enumeration getMatchingHeaderLines(String[] names)
  1610. throws MessagingException {
  1611. return headers.getMatchingHeaderLines(names);
  1612. }
  1613. /**
  1614. * Get non-matching header lines as an Enumeration of Strings.
  1615. * A Header line is a raw RFC 822 header-line, containing both
  1616. * the "name" and "value" field.
  1617. *
  1618. * @exception MessagingException
  1619. */
  1620. public Enumeration getNonMatchingHeaderLines(String[] names)
  1621. throws MessagingException {
  1622. return headers.getNonMatchingHeaderLines(names);
  1623. }
  1624. /**
  1625. * Return a <code>Flags</code> object containing the flags for
  1626. * this message. <p>
  1627. *
  1628. * Note that a clone of the internal Flags object is returned, so
  1629. * modifying the returned Flags object will not affect the flags
  1630. * of this message.
  1631. *
  1632. * @return Flags object containing the flags for this message
  1633. * @exception MessagingException
  1634. * @see javax.mail.Flags
  1635. */
  1636. public synchronized Flags getFlags() throws MessagingException {
  1637. return (Flags)flags.clone();
  1638. }
  1639. /**
  1640. * Check whether the flag specified in the <code>flag</code>
  1641. * argument is set in this message. <p>
  1642. *
  1643. * This implementation checks this message's internal
  1644. * <code>flags</code> object.
  1645. *
  1646. * @param flag the flag
  1647. * @return value of the specified flag for this message
  1648. * @see javax.mail.Flags.Flag
  1649. * @see javax.mail.Flags.Flag#ANSWERED
  1650. * @see javax.mail.Flags.Flag#DELETED
  1651. * @see javax.mail.Flags.Flag#DRAFT
  1652. * @see javax.mail.Flags.Flag#FLAGGED
  1653. * @see javax.mail.Flags.Flag#RECENT
  1654. * @see javax.mail.Flags.Flag#SEEN
  1655. * @exception MessagingException
  1656. */
  1657. public synchronized boolean isSet(Flags.Flag flag)
  1658. throws MessagingException {
  1659. return (flags.contains(flag));
  1660. }
  1661. /**
  1662. * Set the flags for this message. <p>
  1663. *
  1664. * This implementation modifies the <code>flags</code> field.
  1665. *
  1666. * @exception IllegalWriteException if the underlying
  1667. * implementation does not support modification
  1668. * @exception IllegalStateException if this message is
  1669. * obtained from a READ_ONLY folder.
  1670. * @exception MessagingException
  1671. */
  1672. public synchronized void setFlags(Flags flag, boolean set)
  1673. throws MessagingException {
  1674. if (set)
  1675. flags.add(flag);
  1676. else
  1677. flags.remove(flag);
  1678. }
  1679. /**
  1680. * Updates the appropriate header fields of this message to be
  1681. * consistent with the message's contents. If this message is
  1682. * contained in a Folder, any changes made to this message are
  1683. * committed to the containing folder. <p>
  1684. *
  1685. * If any part of a message's headers or contents are changed,
  1686. * <code>saveChanges</code> must be called to ensure that those
  1687. * changes are permanent. Otherwise, any such modifications may or
  1688. * may not be saved, depending on the folder implementation. <p>
  1689. *
  1690. * Messages obtained from folders opened READ_ONLY should not be
  1691. * modified and saveChanges should not be called on such messages. <p>
  1692. *
  1693. * This method sets the <code>modified</code> flag to true, the
  1694. * <code>save</code> flag to true, and then calls the
  1695. * <code>updateHeaders<code> method.
  1696. *
  1697. * @exception IllegalWriteException if the underlying
  1698. * implementation does not support modification
  1699. * @exception IllegalStateException if this message is
  1700. * obtained from a READ_ONLY folder.
  1701. * @exception MessagingException
  1702. */
  1703. public void saveChanges() throws MessagingException {
  1704. modified = true;
  1705. saved = true;
  1706. updateHeaders();
  1707. }
  1708. /**
  1709. * Called by the <code>saveChanges</code> method to actually
  1710. * update the MIME headers. The implementation here sets the
  1711. * <code>Content-Transfer-Encoding</code> header (if needed
  1712. * and not already set), the <code>Mime-Version</code> header
  1713. * and the <code>Message-ID</code> header. Also, if the content
  1714. * of this message is a <code>MimeMultipart</code>, it's
  1715. * <code>updateHeaders</code> method is called.
  1716. *
  1717. * @exception IllegalWriteException if the underlying
  1718. * implementation does not support modification
  1719. * @exception IllegalStateException if this message is
  1720. * obtained from a READ_ONLY folder.
  1721. * @exception MessagingException
  1722. */
  1723. protected void updateHeaders() throws MessagingException {
  1724. MimeBodyPart.updateHeaders(this);
  1725. setHeader("Mime-Version", "1.0");
  1726. setHeader("Message-ID",
  1727. "<" + UniqueValue.getUniqueMessageIDValue(session) + ">");
  1728. }
  1729. /**
  1730. * Create and return an InternetHeaders object that loads the
  1731. * headers from the given InputStream. Subclasses can override
  1732. * this method to return a subclass of InternetHeaders, if
  1733. * necessary. This implementation simply constructs and returns
  1734. * an InternetHeaders object.
  1735. *
  1736. * @param is the InputStream to read the headers from
  1737. * @exception MessagingException
  1738. * @since JavaMail 1.2
  1739. */
  1740. protected InternetHeaders createInternetHeaders(InputStream is)
  1741. throws MessagingException {
  1742. return new InternetHeaders(is);
  1743. }
  1744. }