You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by rm...@apache.org on 2014/08/26 20:17:09 UTC
svn commit: r1620683 [9/17] - in /geronimo/specs/trunk: ./
geronimo-javamail_1.5_spec/ geronimo-javamail_1.5_spec/src/
geronimo-javamail_1.5_spec/src/main/
geronimo-javamail_1.5_spec/src/main/java/
geronimo-javamail_1.5_spec/src/main/java/javax/ geroni...
Added: geronimo/specs/trunk/geronimo-javamail_1.5_spec/src/main/java/javax/mail/internet/MimeMultipart.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-javamail_1.5_spec/src/main/java/javax/mail/internet/MimeMultipart.java?rev=1620683&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-javamail_1.5_spec/src/main/java/javax/mail/internet/MimeMultipart.java (added)
+++ geronimo/specs/trunk/geronimo-javamail_1.5_spec/src/main/java/javax/mail/internet/MimeMultipart.java Tue Aug 26 18:17:06 2014
@@ -0,0 +1,778 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package javax.mail.internet;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+
+import javax.activation.DataSource;
+import javax.mail.BodyPart;
+import javax.mail.MessagingException;
+import javax.mail.Multipart;
+import javax.mail.MultipartDataSource;
+
+import org.apache.geronimo.mail.util.SessionUtil;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class MimeMultipart extends Multipart {
+ private static final String MIME_IGNORE_MISSING_ENDBOUNDARY = "mail.mime.multipart.ignoremissingendboundary";
+ private static final String MIME_IGNORE_MISSING_BOUNDARY_PARAMETER = "mail.mime.multipart.ignoremissingboundaryparameter";
+ private static final String MIME_IGNORE_EXISTING_BOUNDARY_PARAMETER = "mail.mime.multipart.ignoreexistingboundaryparameter";
+ private static final String MIME_ALLOWEMPTY = "mail.mime.multipart.allowempty";
+
+ /**
+ * DataSource that provides our InputStream.
+ */
+ protected DataSource ds;
+
+ /**
+ * Indicates if the data has been parsed.
+ */
+ protected boolean parsed = true;
+
+ // the content type information
+ private transient ContentType type;
+
+ /** Have we seen the final bounary line?
+ *
+ * @since JavaMail 1.5
+ */
+ protected boolean complete = true;
+
+ /**
+ * The MIME multipart preamble text, the text that
+ * occurs before the first boundary line.
+ *
+ * @since JavaMail 1.5
+ */
+ protected String preamble = null;
+
+
+ /**
+ * Flag corresponding to the "mail.mime.multipart.ignoremissingendboundary"
+ * property, set in the {@link #initializeProperties} method called from
+ * constructors and the parse method.
+ *
+ * @since JavaMail 1.5
+ */
+ protected boolean ignoreMissingEndBoundary = true;
+
+ /**
+ * Flag corresponding to the
+ * "mail.mime.multipart.ignoremissingboundaryparameter"
+ * property, set in the {@link #initializeProperties} method called from
+ * constructors and the parse method.
+ *
+ * @since JavaMail 1.5
+ */
+ protected boolean ignoreMissingBoundaryParameter = true;
+
+ /**
+ * Flag corresponding to the
+ * "mail.mime.multipart.ignoreexistingboundaryparameter"
+ * property, set in the {@link #initializeProperties} method called from
+ * constructors and the parse method.
+ *
+ * @since JavaMail 1.5
+ */
+ protected boolean ignoreExistingBoundaryParameter = false;
+
+ /**
+ * Flag corresponding to the "mail.mime.multipart.allowempty"
+ * property, set in the {@link #initializeProperties} method called from
+ * constructors and the parse method.
+ *
+ * @since JavaMail 1.5
+ */
+ protected boolean allowEmpty = false;
+
+ /**
+ * Initialize flags that control parsing behavior,
+ * based on System properties described above in
+ * the class documentation.
+ *
+ * @since JavaMail 1.5
+ */
+ protected void initializeProperties() {
+
+ ignoreMissingEndBoundary = SessionUtil.getBooleanProperty(MIME_IGNORE_MISSING_ENDBOUNDARY, true);
+ ignoreMissingBoundaryParameter = SessionUtil.getBooleanProperty(MIME_IGNORE_MISSING_BOUNDARY_PARAMETER, true);
+ ignoreExistingBoundaryParameter = SessionUtil.getBooleanProperty(MIME_IGNORE_EXISTING_BOUNDARY_PARAMETER, false);
+ allowEmpty = SessionUtil.getBooleanProperty(MIME_ALLOWEMPTY, false);
+
+ }
+
+ /**
+ * Create an empty MimeMultipart with content type "multipart/mixed"
+ */
+ public MimeMultipart() {
+ this("mixed");
+ }
+
+ /**
+ * Create an empty MimeMultipart with the subtype supplied.
+ *
+ * @param subtype the subtype
+ */
+ public MimeMultipart(final String subtype) {
+ type = new ContentType("multipart", subtype, null);
+ type.setParameter("boundary", getBoundary());
+ contentType = type.toString();
+ initializeProperties();
+ }
+
+ /**
+ * Create a MimeMultipart from the supplied DataSource.
+ *
+ * @param dataSource the DataSource to use
+ * @throws MessagingException
+ */
+ public MimeMultipart(final DataSource dataSource) throws MessagingException {
+ ds = dataSource;
+ if (dataSource instanceof MultipartDataSource) {
+ super.setMultipartDataSource((MultipartDataSource) dataSource);
+ parsed = true;
+ } else {
+ // We keep the original, provided content type string so that we
+ // don't end up changing quoting/formatting of the header unless
+ // changes are made to the content type. James is somewhat dependent
+ // on that behavior.
+ contentType = ds.getContentType();
+ type = new ContentType(contentType);
+ parsed = false;
+ }
+ }
+
+
+ /**
+ * Construct a MimeMultipart object of the default "mixed" subtype,
+ * and with the given body parts. More body parts may be added later.
+ *
+ * @since JavaMail 1.5
+ */
+ public MimeMultipart(final BodyPart... parts) throws MessagingException {
+ this("mixed");
+ this.parts.addAll(Arrays.asList(parts));
+ }
+
+ /**
+ * Construct a MimeMultipart object of the given subtype
+ * and with the given body parts. More body parts may be added later.
+ *
+ * @since JavaMail 1.5
+ */
+ public MimeMultipart(final String subtype, final BodyPart... parts)
+ throws MessagingException {
+ this(subtype);
+ this.parts.addAll(Arrays.asList(parts));
+ }
+
+ public void setSubType(final String subtype) throws MessagingException {
+ type.setSubType(subtype);
+ contentType = type.toString();
+ }
+
+ @Override
+ public int getCount() throws MessagingException {
+ parse();
+ return super.getCount();
+ }
+
+ @Override
+ public synchronized BodyPart getBodyPart(final int part) throws MessagingException {
+ parse();
+ return super.getBodyPart(part);
+ }
+
+ public BodyPart getBodyPart(final String cid) throws MessagingException {
+ parse();
+ for (int i = 0; i < parts.size(); i++) {
+ final MimeBodyPart bodyPart = (MimeBodyPart) parts.get(i);
+ if (cid.equals(bodyPart.getContentID())) {
+ return bodyPart;
+ }
+ }
+ return null;
+ }
+
+ protected void updateHeaders() throws MessagingException {
+ parse();
+ for (int i = 0; i < parts.size(); i++) {
+ final MimeBodyPart bodyPart = (MimeBodyPart) parts.get(i);
+ bodyPart.updateHeaders();
+ }
+ }
+
+ private static byte[] dash = { '-', '-' };
+ private static byte[] crlf = { 13, 10 };
+
+ @Override
+ public void writeTo(final OutputStream out) throws IOException, MessagingException {
+ parse();
+ final String boundary = type.getParameter("boundary");
+ final byte[] bytes = boundary.getBytes("ISO8859-1");
+
+ if(!allowEmpty && parts.size() == 0) {
+ throw new MessagingException("Multipart content with no body parts is not allowed");
+ }
+
+ if (preamble != null) {
+ final byte[] preambleBytes = preamble.getBytes("ISO8859-1");
+ // write this out, followed by a line break.
+ out.write(preambleBytes);
+ out.write(crlf);
+ }
+
+ for (int i = 0; i < parts.size(); i++) {
+ final BodyPart bodyPart = (BodyPart) parts.get(i);
+ out.write(dash);
+ out.write(bytes);
+ out.write(crlf);
+ bodyPart.writeTo(out);
+ out.write(crlf);
+ }
+ out.write(dash);
+ out.write(bytes);
+ out.write(dash);
+ out.write(crlf);
+ out.flush();
+ }
+
+ protected void parse() throws MessagingException {
+ if (parsed) {
+ return;
+ }
+
+ initializeProperties();
+
+ try {
+ final ContentType cType = new ContentType(contentType);
+ final String boundaryString = cType.getParameter("boundary");
+
+ if(!ignoreMissingBoundaryParameter && boundaryString == null) {
+ throw new MessagingException("Missing boundary parameter in content-type");
+ }
+
+ final InputStream is = new BufferedInputStream(ds.getInputStream());
+ BufferedInputStream pushbackInStream = null;
+ boolean boundaryFound = false;
+
+ byte[] boundary = null;
+ if (boundaryString == null || ignoreExistingBoundaryParameter) {
+ pushbackInStream = new BufferedInputStream(is, 1200);
+ // read until we find something that looks like a boundary string
+ boundary = readTillFirstBoundary(pushbackInStream);
+ boundaryFound = boundary != null;
+ }
+ else {
+ boundary = ("--" + boundaryString).getBytes("ISO8859-1");
+ pushbackInStream = new BufferedInputStream(is, boundary.length + 1000);
+ boundaryFound = readTillFirstBoundary(pushbackInStream, boundary);
+ }
+
+
+
+ if(allowEmpty && !boundaryFound) {
+ parsed = true;
+ return;
+ }
+
+ if(!allowEmpty && !boundaryFound) {
+ throw new MessagingException("Multipart content with no body parts is not allowed");
+ }
+
+
+ while (true) {
+ MimeBodyPartInputStream partStream;
+ partStream = new MimeBodyPartInputStream(pushbackInStream, boundary);
+ addBodyPart(new MimeBodyPart(partStream));
+
+ // terminated by an EOF rather than a proper boundary?
+ if (!partStream.boundaryFound) {
+
+ if (!ignoreMissingEndBoundary) {
+ throw new MessagingException("Missing Multi-part end boundary");
+ }
+ complete = false;
+ break;
+ }
+ // if we hit the final boundary, stop processing this
+ if (partStream.finalBoundaryFound) {
+ break;
+ }
+ }
+
+
+
+ } catch (final Exception e){
+ throw new MessagingException(e.toString(),e);
+ }
+ parsed = true;
+ }
+
+ /**
+ * Move the read pointer to the beginning of the first part
+ * read till the end of first boundary. Any data read before this point are
+ * saved as the preamble.
+ *
+ * @param pushbackInStream
+ * @param boundary
+ * @throws MessagingException
+ */
+ private byte[] readTillFirstBoundary(final BufferedInputStream pushbackInStream) throws MessagingException {
+ final ByteArrayOutputStream preambleStream = new ByteArrayOutputStream();
+
+ try {
+ while (true) {
+ // read the next line
+ final byte[] line = readLine(pushbackInStream);
+ // hit an EOF?
+ if (line == null || line.length==0) {
+ return null;//throw new MessagingException("Unexpected End of Stream while searching for first Mime Boundary");
+ }
+ // if this looks like a boundary, then make it so
+ if (line.length > 2 && line[0] == '-' && line[1] == '-') {
+ // save the preamble, if there is one.
+ final byte[] preambleBytes = preambleStream.toByteArray();
+ if (preambleBytes.length > 0) {
+ preamble = new String(preambleBytes, "ISO8859-1");
+ }
+ return stripLinearWhiteSpace(line);
+ }
+ else {
+ // this is part of the preamble.
+ preambleStream.write(line);
+ preambleStream.write('\r');
+ preambleStream.write('\n');
+ }
+ }
+ } catch (final IOException ioe) {
+ throw new MessagingException(ioe.toString(), ioe);
+ }
+ }
+
+
+ /**
+ * Scan a line buffer stripping off linear whitespace
+ * characters, returning a new array without the
+ * characters, if possible.
+ *
+ * @param line The source line buffer.
+ *
+ * @return A byte array with white space characters removed,
+ * if necessary.
+ */
+ private byte[] stripLinearWhiteSpace(final byte[] line) {
+ int index = line.length - 1;
+ // if the last character is not a space or tab, we
+ // can use this unchanged
+ if (line[index] != ' ' && line[index] != '\t') {
+ return line;
+ }
+ // scan backwards for the first non-white space
+ for (; index > 0; index--) {
+ if (line[index] != ' ' && line[index] != '\t') {
+ break;
+ }
+ }
+ // make a shorter copy of this
+ final byte[] newLine = new byte[index + 1];
+ System.arraycopy(line, 0, newLine, 0, index + 1);
+ return newLine;
+ }
+
+ /**
+ * Move the read pointer to the beginning of the first part
+ * read till the end of first boundary. Any data read before this point are
+ * saved as the preamble.
+ *
+ * @param pushbackInStream
+ * @param boundary
+ * @throws MessagingException
+ */
+ private boolean readTillFirstBoundary(final BufferedInputStream pushbackInStream, final byte[] boundary) throws MessagingException {
+ final ByteArrayOutputStream preambleStream = new ByteArrayOutputStream();
+
+ try {
+ while (true) {
+ // read the next line
+ final byte[] line = readLine(pushbackInStream);
+ // hit an EOF?
+ if (line == null || line.length==0) {
+ return false;//throw new MessagingException("Unexpected End of Stream while searching for first Mime Boundary");
+ }
+
+ // apply the boundary comparison rules to this
+ if (compareBoundary(line, boundary)) {
+ // save the preamble, if there is one.
+ final byte[] preambleBytes = preambleStream.toByteArray();
+ if (preambleBytes.length > 0) {
+ preamble = new String(preambleBytes, "ISO8859-1");
+ }
+ return true;
+ }
+
+ // this is part of the preamble.
+ preambleStream.write(line);
+ preambleStream.write('\r');
+ preambleStream.write('\n');
+ }
+ } catch (final IOException ioe) {
+ throw new MessagingException(ioe.toString(), ioe);
+ }
+ }
+
+
+ /**
+ * Perform a boundary comparison, taking into account
+ * potential linear white space
+ *
+ * @param line The line to compare.
+ * @param boundary The boundary we're searching for
+ *
+ * @return true if this is a valid boundary line, false for
+ * any mismatches.
+ */
+ private boolean compareBoundary(final byte[] line, final byte[] boundary) {
+ // if the line is too short, this is an easy failure
+ if (line.length < boundary.length) {
+ return false;
+ }
+
+ // this is the most common situation
+ if (line.length == boundary.length) {
+ return Arrays.equals(line, boundary);
+ }
+ // the line might have linear white space after the boundary portions
+ for (int i = 0; i < boundary.length; i++) {
+ // fail on any mismatch
+ if (line[i] != boundary[i]) {
+ return false;
+ }
+ }
+ // everything after the boundary portion must be linear whitespace
+ for (int i = boundary.length; i < line.length; i++) {
+ // fail on any mismatch
+ if (line[i] != ' ' && line[i] != '\t') {
+ return false;
+ }
+ }
+ // these are equivalent
+ return true;
+ }
+
+ /**
+ * Read a single line of data from the input stream,
+ * returning it as an array of bytes.
+ *
+ * @param in The source input stream.
+ *
+ * @return A byte array containing the line data. Returns
+ * null if there's nothing left in the stream.
+ * @exception MessagingException
+ */
+ private byte[] readLine(final BufferedInputStream in) throws IOException
+ {
+ final ByteArrayOutputStream line = new ByteArrayOutputStream();
+
+ while (in.available() > 0) {
+ int value = in.read();
+ if (value == -1) {
+ // if we have nothing in the accumulator, signal an EOF back
+ if (line.size() == 0) {
+ return null;
+ }
+ break;
+ }
+ else if (value == '\r') {
+ in.mark(10);
+ value = in.read();
+ // we expect to find a linefeed after the carriage return, but
+ // some things play loose with the rules.
+ if (value != '\n') {
+ in.reset();
+ }
+ break;
+ }
+ else if (value == '\n') {
+ // naked linefeed, allow that
+ break;
+ }
+ else {
+ // write this to the line
+ line.write((byte)value);
+ }
+ }
+ // return this as an array of bytes
+ return line.toByteArray();
+ }
+
+
+ protected InternetHeaders createInternetHeaders(final InputStream in) throws MessagingException {
+ return new InternetHeaders(in);
+ }
+
+ protected MimeBodyPart createMimeBodyPart(final InternetHeaders headers, final byte[] data) throws MessagingException {
+ return new MimeBodyPart(headers, data);
+ }
+
+ protected MimeBodyPart createMimeBodyPart(final InputStream in) throws MessagingException {
+ return new MimeBodyPart(in);
+ }
+
+ // static used to track boundary value allocations to help ensure uniqueness.
+ private static int part;
+
+ private synchronized static String getBoundary() {
+ int i;
+ synchronized(MimeMultipart.class) {
+ i = part++;
+ }
+ final StringBuffer buf = new StringBuffer(64);
+ buf.append("----=_Part_").append(i).append('_').append((new Object()).hashCode()).append('.').append(System.currentTimeMillis());
+ return buf.toString();
+ }
+
+ private class MimeBodyPartInputStream extends InputStream {
+ BufferedInputStream inStream;
+ public boolean boundaryFound = false;
+ byte[] boundary;
+ public boolean finalBoundaryFound = false;
+
+ public MimeBodyPartInputStream(final BufferedInputStream inStream, final byte[] boundary) {
+ super();
+ this.inStream = inStream;
+ this.boundary = boundary;
+ }
+
+ /**
+ * The base reading method for reading one character
+ * at a time.
+ *
+ * @return The read character, or -1 if an EOF was encountered.
+ * @exception IOException
+ */
+ @Override
+ public int read() throws IOException {
+ if (boundaryFound) {
+ return -1;
+ }
+
+ // read the next value from stream
+ final int firstChar = inStream.read();
+ // premature end? Handle it like a boundary located
+ if (firstChar == -1) {
+ //DO NOT treat this a a boundary because if we do so we have no chance to detect missing end boundaries
+ return -1;
+ }
+
+ // we first need to look for a line boundary. If we find a boundary, it can be followed by the
+ // boundary marker, so we need to remember what sort of thing we found, then read ahead looking
+ // for the part boundary.
+
+ // NB:, we only handle [\r]\n--boundary marker[--]
+ // we need to at least accept what most mail servers would consider an
+ // invalid format using just '\n'
+ if (firstChar != '\r' && firstChar != '\n') {
+ // not a \r, just return the byte as is
+ return firstChar;
+ }
+ // we might need to rewind to this point. The padding is to allow for
+ // line terminators and linear whitespace on the boundary lines
+ inStream.mark(boundary.length + 1000);
+ // we need to keep track of the first read character in case we need to
+ // rewind back to the mark point
+ int value = firstChar;
+ // if this is a '\r', then we require the '\n'
+ if (value == '\r') {
+ // now scan ahead for the second character
+ value = inStream.read();
+ if (value != '\n') {
+ // only a \r, so this can't be a boundary. Return the
+ // \r as if it was data, after first resetting
+ inStream.reset();
+ return '\r';
+ }
+ }
+
+ value = inStream.read();
+ // if the next character is not a boundary start, we
+ // need to handle this as a normal line end
+ if ((byte) value != boundary[0]) {
+ // just reset and return the first character as data
+ inStream.reset();
+ return firstChar;
+ }
+
+ // we're here because we found a "\r\n-" sequence, which is a potential
+ // boundary marker. Read the individual characters of the next line until
+ // we have a mismatch
+
+ // read value is the first byte of the boundary. Start matching the
+ // next characters to find a boundary
+ int boundaryIndex = 0;
+ while ((boundaryIndex < boundary.length) && ((byte) value == boundary[boundaryIndex])) {
+ value = inStream.read();
+ boundaryIndex++;
+ }
+ // if we didn't match all the way, we need to push back what we've read and
+ // return the EOL character
+ if (boundaryIndex != boundary.length) {
+ // Boundary not found. Restoring bytes skipped.
+ // just reset and return the first character as data
+ inStream.reset();
+ return firstChar;
+ }
+
+ // The full boundary sequence should be \r\n--boundary string[--]\r\n
+ // if the last character we read was a '-', check for the end terminator
+ if (value == '-') {
+ value = inStream.read();
+ // crud, we have a bad boundary terminator. We need to unwind this all the way
+ // back to the lineend and pretend none of this ever happened
+ if (value != '-') {
+ // Boundary not found. Restoring bytes skipped.
+ // just reset and return the first character as data
+ inStream.reset();
+ return firstChar;
+ }
+ // on the home stretch, but we need to verify the LWSP/EOL sequence
+ value = inStream.read();
+ // first skip over the linear whitespace
+ while (value == ' ' || value == '\t') {
+ value = inStream.read();
+ }
+
+ // We've matched the final boundary, skipped any whitespace, but
+ // we've hit the end of the stream. This is highly likely when
+ // we have nested multiparts, since the linend terminator for the
+ // final boundary marker is eated up as the start of the outer
+ // boundary marker. No CRLF sequence here is ok.
+ if (value == -1) {
+ // we've hit the end of times...
+ finalBoundaryFound = true;
+ // we have a boundary, so return this as an EOF condition
+ boundaryFound = true;
+ return -1;
+ }
+
+ // this must be a CR or a LF...which leaves us even more to push back and forget
+ if (value != '\r' && value != '\n') {
+ // Boundary not found. Restoring bytes skipped.
+ // just reset and return the first character as data
+ inStream.reset();
+ return firstChar;
+ }
+
+ // if this is carriage return, check for a linefeed
+ if (value == '\r') {
+ // last check, this must be a line feed
+ value = inStream.read();
+ if (value != '\n') {
+ // SO CLOSE!
+ // Boundary not found. Restoring bytes skipped.
+ // just reset and return the first character as data
+ inStream.reset();
+ return firstChar;
+ }
+ }
+
+ // we've hit the end of times...
+ finalBoundaryFound = true;
+ }
+ else {
+ // first skip over the linear whitespace
+ while (value == ' ' || value == '\t') {
+ value = inStream.read();
+ }
+ // this must be a CR or a LF...which leaves us even more to push back and forget
+ if (value != '\r' && value != '\n') {
+ // Boundary not found. Restoring bytes skipped.
+ // just reset and return the first character as data
+ inStream.reset();
+ return firstChar;
+ }
+
+ // if this is carriage return, check for a linefeed
+ if (value == '\r') {
+ // last check, this must be a line feed
+ value = inStream.read();
+ if (value != '\n') {
+ // SO CLOSE!
+ // Boundary not found. Restoring bytes skipped.
+ // just reset and return the first character as data
+ inStream.reset();
+ return firstChar;
+ }
+ }
+ }
+ // we have a boundary, so return this as an EOF condition
+ boundaryFound = true;
+ return -1;
+ }
+ }
+
+
+ /**
+ * Return true if the final boundary line for this multipart was
+ * seen when parsing the data.
+ *
+ * @return
+ * @exception MessagingException
+ */
+ public boolean isComplete() throws MessagingException {
+ // make sure we've parsed this
+ parse();
+ return complete;
+ }
+
+
+ /**
+ * Returns the preamble text that appears before the first bady
+ * part of a MIME multi part. The preamble is optional, so this
+ * might be null.
+ *
+ * @return The preamble text string.
+ * @exception MessagingException
+ */
+ public String getPreamble() throws MessagingException {
+ parse();
+ return preamble;
+ }
+
+ /**
+ * Set the message preamble text. This will be written before
+ * the first boundary of a multi-part message.
+ *
+ * @param preamble The new boundary text. This is complete lines of text, including
+ * new lines.
+ *
+ * @exception MessagingException
+ */
+ public void setPreamble(final String preamble) throws MessagingException {
+ this.preamble = preamble;
+ }
+}
Added: geronimo/specs/trunk/geronimo-javamail_1.5_spec/src/main/java/javax/mail/internet/MimePart.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-javamail_1.5_spec/src/main/java/javax/mail/internet/MimePart.java?rev=1620683&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-javamail_1.5_spec/src/main/java/javax/mail/internet/MimePart.java (added)
+++ geronimo/specs/trunk/geronimo-javamail_1.5_spec/src/main/java/javax/mail/internet/MimePart.java Tue Aug 26 18:17:06 2014
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package javax.mail.internet;
+
+import java.util.Enumeration;
+
+import javax.mail.MessagingException;
+import javax.mail.Part;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public interface MimePart extends Part {
+ public abstract void addHeaderLine(String line) throws MessagingException;
+
+ public abstract Enumeration getAllHeaderLines() throws MessagingException;
+
+ public abstract String getContentID() throws MessagingException;
+
+ public abstract String[] getContentLanguage() throws MessagingException;
+
+ public abstract String getContentMD5() throws MessagingException;
+
+ public abstract String getEncoding() throws MessagingException;
+
+ public abstract String getHeader(String header, String delimiter)
+ throws MessagingException;
+
+ public abstract Enumeration getMatchingHeaderLines(String[] names)
+ throws MessagingException;
+
+ public abstract Enumeration getNonMatchingHeaderLines(String[] names)
+ throws MessagingException;
+
+ public abstract void setContentLanguage(String[] languages)
+ throws MessagingException;
+
+ public abstract void setContentMD5(String content)
+ throws MessagingException;
+
+ public abstract void setText(String text) throws MessagingException;
+
+ public abstract void setText(String text, String charset)
+ throws MessagingException;
+
+ public abstract void setText(String text, String charset, String subType)
+ throws MessagingException;
+}
Added: geronimo/specs/trunk/geronimo-javamail_1.5_spec/src/main/java/javax/mail/internet/MimePartDataSource.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-javamail_1.5_spec/src/main/java/javax/mail/internet/MimePartDataSource.java?rev=1620683&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-javamail_1.5_spec/src/main/java/javax/mail/internet/MimePartDataSource.java (added)
+++ geronimo/specs/trunk/geronimo-javamail_1.5_spec/src/main/java/javax/mail/internet/MimePartDataSource.java Tue Aug 26 18:17:06 2014
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package javax.mail.internet;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.UnknownServiceException;
+
+import javax.activation.DataSource;
+import javax.mail.MessageAware;
+import javax.mail.MessageContext;
+import javax.mail.MessagingException;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class MimePartDataSource implements DataSource, MessageAware {
+ // the part that provides the data form this data source.
+ protected MimePart part;
+
+ public MimePartDataSource(final MimePart part) {
+ this.part = part;
+ }
+
+ public InputStream getInputStream() throws IOException {
+ try {
+ InputStream stream;
+ if (part instanceof MimeMessage) {
+ stream = ((MimeMessage) part).getContentStream();
+ } else if (part instanceof MimeBodyPart) {
+ stream = ((MimeBodyPart) part).getContentStream();
+ } else {
+ throw new MessagingException("Unknown part");
+ }
+ return checkPartEncoding(part, stream);
+ } catch (final MessagingException e) {
+ throw (IOException) new IOException(e.getMessage()).initCause(e);
+ }
+ }
+
+
+ /**
+ * For a given part, decide it the data stream requires
+ * wrappering with a stream for decoding a particular
+ * encoding.
+ *
+ * @param part The part we're extracting.
+ * @param stream The raw input stream for the part.
+ *
+ * @return An input stream configured for reading the
+ * source part and decoding it into raw bytes.
+ */
+ private InputStream checkPartEncoding(final MimePart part, final InputStream stream) throws MessagingException {
+ String encoding = part.getEncoding();
+ // if nothing is specified, there's nothing to do
+ if (encoding == null) {
+ return stream;
+ }
+ // now screen out the ones that never need decoding
+ encoding = encoding.toLowerCase();
+ if (encoding.equals("7bit") || encoding.equals("8bit") || encoding.equals("binary")) {
+ return stream;
+ }
+ // now we need to check the content type to prevent
+ // MultiPart types from getting decoded, since the part is just an envelope around other
+ // parts
+ final String contentType = part.getContentType();
+ if (contentType != null) {
+ try {
+ final ContentType type = new ContentType(contentType);
+ // no decoding done here
+ if (type.match("multipart/*")) {
+ return stream;
+ }
+ } catch (final ParseException e) {
+ // ignored....bad content type means we handle as a normal part
+ }
+ }
+ // ok, wrap this is a decoding stream if required
+ return MimeUtility.decode(stream, encoding);
+ }
+
+
+ public OutputStream getOutputStream() throws IOException {
+ throw new UnknownServiceException();
+ }
+
+ public String getContentType() {
+ try {
+ return part.getContentType();
+ } catch (final MessagingException e) {
+ return null;
+ }
+ }
+
+ public String getName() {
+ try {
+ if (part instanceof MimeBodyPart) {
+ return ((MimeBodyPart) part).getFileName();
+ }
+ } catch (final MessagingException mex) {
+ // ignore it
+ }
+ return "";
+ }
+
+ public synchronized MessageContext getMessageContext() {
+ return new MessageContext(part);
+ }
+}