0N/A/*
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0N/A *
412N/A * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved.
0N/A *
0N/A * The contents of this file are subject to the terms of either the GNU
0N/A * General Public License Version 2 only ("GPL") or the Common Development
0N/A * and Distribution License("CDDL") (collectively, the "License"). You
292N/A * may not use this file except in compliance with the License. You can
292N/A * obtain a copy of the License at
292N/A * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
292N/A * or packager/legal/LICENSE.txt. See the License for the specific
0N/A * language governing permissions and limitations under the License.
0N/A *
0N/A * When distributing the software, include this License Header Notice in each
292N/A * file and include the License file at packager/legal/LICENSE.txt.
292N/A *
292N/A * GPL Classpath Exception:
292N/A * Oracle designates this particular file as subject to the "Classpath"
292N/A * exception as provided by Oracle in the GPL Version 2 section of the License
292N/A * file that accompanied this code.
292N/A *
292N/A * Modifications:
292N/A * If applicable, add the following below the License Header, with the fields
292N/A * enclosed by brackets [] replaced by your own identifying information:
292N/A * "Portions Copyright [year] [name of copyright owner]"
0N/A *
0N/A * Contributor(s):
0N/A * If you wish your version of this file to be governed by only the CDDL or
0N/A * only the GPL Version 2, indicate your decision by adding "[Contributor]
0N/A * elects to include this software in this distribution under the [CDDL or GPL
0N/A * Version 2] license." If you don't indicate a single choice of license, a
0N/A * recipient has the option to distribute your version of this file under
0N/A * either the CDDL, the GPL Version 2 or to extend the choice of license to
0N/A * its licensees as provided above. However, if you add GPL Version 2 code
0N/A * and therefore, elected the GPL Version 2 license, then the option applies
0N/A * only if the new code is made subject to such option by the copyright
0N/A * holder.
0N/A */
0N/A
0N/Apackage com.sun.mail.imap;
0N/A
0N/Aimport java.io.*;
0N/A
0N/Aimport java.util.Enumeration;
0N/Aimport javax.mail.*;
0N/Aimport javax.mail.internet.*;
0N/Aimport javax.activation.*;
0N/A
0N/Aimport com.sun.mail.util.*;
0N/Aimport com.sun.mail.iap.*;
0N/Aimport com.sun.mail.imap.protocol.*;
0N/A
0N/A/**
264N/A * An IMAP body part.
0N/A *
0N/A * @author John Mani
412N/A * @author Bill Shannon
0N/A */
0N/A
412N/Apublic class IMAPBodyPart extends MimeBodyPart implements ReadableMime {
0N/A private IMAPMessage message;
0N/A private BODYSTRUCTURE bs;
0N/A private String sectionId;
0N/A
0N/A // processed values ..
0N/A private String type;
0N/A private String description;
0N/A
0N/A private boolean headersLoaded = false;
0N/A
264N/A private static final boolean decodeFileName =
264N/A PropUtil.getBooleanSystemProperty("mail.mime.decodefilename", false);
264N/A
0N/A protected IMAPBodyPart(BODYSTRUCTURE bs, String sid, IMAPMessage message) {
0N/A super();
0N/A this.bs = bs;
0N/A this.sectionId = sid;
0N/A this.message = message;
0N/A // generate content-type
0N/A ContentType ct = new ContentType(bs.type, bs.subtype, bs.cParams);
0N/A type = ct.toString();
0N/A }
0N/A
0N/A /* Override this method to make it a no-op, rather than throw
0N/A * an IllegalWriteException. This will permit IMAPBodyParts to
0N/A * be inserted in newly crafted MimeMessages, especially when
0N/A * forwarding or replying to messages.
0N/A */
0N/A protected void updateHeaders() {
0N/A return;
0N/A }
0N/A
0N/A public int getSize() throws MessagingException {
0N/A return bs.size;
0N/A }
0N/A
0N/A public int getLineCount() throws MessagingException {
0N/A return bs.lines;
0N/A }
0N/A
0N/A public String getContentType() throws MessagingException {
0N/A return type;
0N/A }
0N/A
0N/A public String getDisposition() throws MessagingException {
0N/A return bs.disposition;
0N/A }
0N/A
0N/A public void setDisposition(String disposition) throws MessagingException {
0N/A throw new IllegalWriteException("IMAPBodyPart is read-only");
0N/A }
0N/A
0N/A public String getEncoding() throws MessagingException {
0N/A return bs.encoding;
0N/A }
0N/A
0N/A public String getContentID() throws MessagingException {
0N/A return bs.id;
0N/A }
0N/A
0N/A public String getContentMD5() throws MessagingException {
0N/A return bs.md5;
0N/A }
0N/A
0N/A public void setContentMD5(String md5) throws MessagingException {
0N/A throw new IllegalWriteException("IMAPBodyPart is read-only");
0N/A }
0N/A
0N/A public String getDescription() throws MessagingException {
0N/A if (description != null) // cached value ?
0N/A return description;
0N/A
0N/A if (bs.description == null)
0N/A return null;
0N/A
0N/A try {
0N/A description = MimeUtility.decodeText(bs.description);
0N/A } catch (UnsupportedEncodingException ex) {
0N/A description = bs.description;
0N/A }
0N/A
0N/A return description;
0N/A }
0N/A
0N/A public void setDescription(String description, String charset)
0N/A throws MessagingException {
0N/A throw new IllegalWriteException("IMAPBodyPart is read-only");
0N/A }
0N/A
0N/A public String getFileName() throws MessagingException {
0N/A String filename = null;
0N/A if (bs.dParams != null)
0N/A filename = bs.dParams.get("filename");
0N/A if (filename == null && bs.cParams != null)
0N/A filename = bs.cParams.get("name");
264N/A if (decodeFileName && filename != null) {
264N/A try {
264N/A filename = MimeUtility.decodeText(filename);
264N/A } catch (UnsupportedEncodingException ex) {
264N/A throw new MessagingException("Can't decode filename", ex);
264N/A }
264N/A }
0N/A return filename;
0N/A }
0N/A
0N/A public void setFileName(String filename) throws MessagingException {
0N/A throw new IllegalWriteException("IMAPBodyPart is read-only");
0N/A }
0N/A
0N/A protected InputStream getContentStream() throws MessagingException {
0N/A InputStream is = null;
412N/A boolean pk = message.getPeek(); // acquire outside of message cache lock
0N/A
0N/A // Acquire MessageCacheLock, to freeze seqnum.
0N/A synchronized(message.getMessageCacheLock()) {
0N/A try {
0N/A IMAPProtocol p = message.getProtocol();
0N/A
0N/A // Check whether this message is expunged
0N/A message.checkExpunged();
0N/A
0N/A if (p.isREV1() && (message.getFetchBlockSize() != -1))
444N/A return new IMAPInputStream(message, sectionId,
444N/A message.ignoreBodyStructureSize() ? -1 : bs.size, pk);
0N/A
0N/A // Else, vanila IMAP4, no partial fetch
0N/A
0N/A int seqnum = message.getSequenceNumber();
0N/A BODY b;
0N/A if (pk)
0N/A b = p.peekBody(seqnum, sectionId);
0N/A else
0N/A b = p.fetchBody(seqnum, sectionId);
0N/A if (b != null)
0N/A is = b.getByteArrayInputStream();
0N/A } catch (ConnectionException cex) {
0N/A throw new FolderClosedException(
0N/A message.getFolder(), cex.getMessage());
0N/A } catch (ProtocolException pex) {
0N/A throw new MessagingException(pex.getMessage(), pex);
0N/A }
0N/A }
0N/A
0N/A if (is == null)
0N/A throw new MessagingException("No content");
0N/A else
0N/A return is;
0N/A }
412N/A
412N/A /**
412N/A * Return the MIME format stream of headers for this body part.
412N/A */
412N/A private InputStream getHeaderStream() throws MessagingException {
412N/A if (!message.isREV1())
412N/A loadHeaders(); // will be needed below
412N/A
412N/A // Acquire MessageCacheLock, to freeze seqnum.
412N/A synchronized(message.getMessageCacheLock()) {
412N/A try {
412N/A IMAPProtocol p = message.getProtocol();
412N/A
412N/A // Check whether this message got expunged
412N/A message.checkExpunged();
412N/A
412N/A if (p.isREV1()) {
412N/A int seqnum = message.getSequenceNumber();
412N/A BODY b = p.peekBody(seqnum, sectionId + ".MIME");
412N/A
412N/A if (b == null)
412N/A throw new MessagingException("Failed to fetch headers");
412N/A
412N/A ByteArrayInputStream bis = b.getByteArrayInputStream();
412N/A if (bis == null)
412N/A throw new MessagingException("Failed to fetch headers");
412N/A return bis;
412N/A
412N/A } else {
412N/A // Can't read it from server, have to fake it
412N/A SharedByteArrayOutputStream bos =
412N/A new SharedByteArrayOutputStream(0);
412N/A LineOutputStream los = new LineOutputStream(bos);
412N/A
412N/A try {
412N/A // Write out the header
787N/A @SuppressWarnings("unchecked")
787N/A Enumeration<String> hdrLines
787N/A = super.getAllHeaderLines();
412N/A while (hdrLines.hasMoreElements())
787N/A los.writeln(hdrLines.nextElement());
412N/A
412N/A // The CRLF separator between header and content
412N/A los.writeln();
412N/A } catch (IOException ioex) {
412N/A // should never happen
412N/A } finally {
412N/A try {
412N/A los.close();
412N/A } catch (IOException cex) { }
412N/A }
412N/A return bos.toStream();
412N/A }
412N/A } catch (ConnectionException cex) {
412N/A throw new FolderClosedException(
412N/A message.getFolder(), cex.getMessage());
412N/A } catch (ProtocolException pex) {
412N/A throw new MessagingException(pex.getMessage(), pex);
412N/A }
412N/A }
412N/A }
412N/A
412N/A /**
412N/A * Return the MIME format stream corresponding to this message part.
412N/A *
412N/A * @return the MIME format stream
412N/A * @since JavaMail 1.4.5
412N/A */
412N/A public InputStream getMimeStream() throws MessagingException {
412N/A /*
412N/A * The IMAP protocol doesn't support returning the entire
412N/A * part content in one operation so we have to fake it by
412N/A * concatenating the header stream and the content stream.
412N/A */
412N/A return new SequenceInputStream(getHeaderStream(), getContentStream());
412N/A }
0N/A
0N/A public synchronized DataHandler getDataHandler()
0N/A throws MessagingException {
0N/A if (dh == null) {
0N/A if (bs.isMulti())
0N/A dh = new DataHandler(
0N/A new IMAPMultipartDataSource(
0N/A this, bs.bodies, sectionId, message)
0N/A );
421N/A else if (bs.isNested() && message.isREV1() && bs.envelope != null)
0N/A dh = new DataHandler(
0N/A new IMAPNestedMessage(message,
0N/A bs.bodies[0],
0N/A bs.envelope,
0N/A sectionId),
0N/A type
0N/A );
0N/A }
0N/A
0N/A return super.getDataHandler();
0N/A }
0N/A
0N/A public void setDataHandler(DataHandler content) throws MessagingException {
0N/A throw new IllegalWriteException("IMAPBodyPart is read-only");
0N/A }
0N/A
0N/A public void setContent(Object o, String type) throws MessagingException {
0N/A throw new IllegalWriteException("IMAPBodyPart is read-only");
0N/A }
0N/A
0N/A public void setContent(Multipart mp) throws MessagingException {
0N/A throw new IllegalWriteException("IMAPBodyPart is read-only");
0N/A }
0N/A
0N/A public String[] getHeader(String name) throws MessagingException {
0N/A loadHeaders();
0N/A return super.getHeader(name);
0N/A }
0N/A
0N/A public void setHeader(String name, String value)
0N/A throws MessagingException {
0N/A throw new IllegalWriteException("IMAPBodyPart is read-only");
0N/A }
0N/A
0N/A public void addHeader(String name, String value)
0N/A throws MessagingException {
0N/A throw new IllegalWriteException("IMAPBodyPart is read-only");
0N/A }
0N/A
0N/A public void removeHeader(String name) throws MessagingException {
0N/A throw new IllegalWriteException("IMAPBodyPart is read-only");
0N/A }
0N/A
787N/A @SuppressWarnings("unchecked")
787N/A public Enumeration<Header> getAllHeaders() throws MessagingException {
0N/A loadHeaders();
0N/A return super.getAllHeaders();
0N/A }
0N/A
787N/A @SuppressWarnings("unchecked")
787N/A public Enumeration<Header> getMatchingHeaders(String[] names)
0N/A throws MessagingException {
0N/A loadHeaders();
0N/A return super.getMatchingHeaders(names);
0N/A }
0N/A
787N/A @SuppressWarnings("unchecked")
787N/A public Enumeration<Header> getNonMatchingHeaders(String[] names)
0N/A throws MessagingException {
0N/A loadHeaders();
0N/A return super.getNonMatchingHeaders(names);
0N/A }
0N/A
0N/A public void addHeaderLine(String line) throws MessagingException {
0N/A throw new IllegalWriteException("IMAPBodyPart is read-only");
0N/A }
0N/A
787N/A @SuppressWarnings("unchecked")
787N/A public Enumeration<String> getAllHeaderLines() throws MessagingException {
0N/A loadHeaders();
0N/A return super.getAllHeaderLines();
0N/A }
0N/A
787N/A @SuppressWarnings("unchecked")
787N/A public Enumeration<String> getMatchingHeaderLines(String[] names)
0N/A throws MessagingException {
0N/A loadHeaders();
0N/A return super.getMatchingHeaderLines(names);
0N/A }
0N/A
787N/A @SuppressWarnings("unchecked")
787N/A public Enumeration<String> getNonMatchingHeaderLines(String[] names)
0N/A throws MessagingException {
0N/A loadHeaders();
0N/A return super.getNonMatchingHeaderLines(names);
0N/A }
0N/A
0N/A private synchronized void loadHeaders() throws MessagingException {
0N/A if (headersLoaded)
0N/A return;
0N/A
466N/A // "headers" should never be null since it's set in the constructor.
466N/A // If something did go wrong this will fix it, but is an unsynchronized
466N/A // assignment of "headers".
0N/A if (headers == null)
0N/A headers = new InternetHeaders();
0N/A
0N/A // load headers
0N/A
0N/A // Acquire MessageCacheLock, to freeze seqnum.
0N/A synchronized(message.getMessageCacheLock()) {
0N/A try {
0N/A IMAPProtocol p = message.getProtocol();
0N/A
0N/A // Check whether this message got expunged
0N/A message.checkExpunged();
0N/A
0N/A if (p.isREV1()) {
0N/A int seqnum = message.getSequenceNumber();
0N/A BODY b = p.peekBody(seqnum, sectionId + ".MIME");
0N/A
0N/A if (b == null)
0N/A throw new MessagingException("Failed to fetch headers");
0N/A
0N/A ByteArrayInputStream bis = b.getByteArrayInputStream();
0N/A if (bis == null)
0N/A throw new MessagingException("Failed to fetch headers");
0N/A
0N/A headers.load(bis);
0N/A
0N/A } else {
0N/A
0N/A // RFC 1730 does not provide for fetching BodyPart headers
0N/A // So, just dump the RFC1730 BODYSTRUCTURE into the
0N/A // headerStore
0N/A
0N/A // Content-Type
0N/A headers.addHeader("Content-Type", type);
0N/A // Content-Transfer-Encoding
0N/A headers.addHeader("Content-Transfer-Encoding", bs.encoding);
0N/A // Content-Description
0N/A if (bs.description != null)
0N/A headers.addHeader("Content-Description",
0N/A bs.description);
0N/A // Content-ID
0N/A if (bs.id != null)
0N/A headers.addHeader("Content-ID", bs.id);
0N/A // Content-MD5
0N/A if (bs.md5 != null)
0N/A headers.addHeader("Content-MD5", bs.md5);
0N/A }
0N/A } catch (ConnectionException cex) {
0N/A throw new FolderClosedException(
0N/A message.getFolder(), cex.getMessage());
0N/A } catch (ProtocolException pex) {
0N/A throw new MessagingException(pex.getMessage(), pex);
0N/A }
0N/A }
0N/A headersLoaded = true;
0N/A }
0N/A}