14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 /*
27 * @(#)MimeMultipart.java 1.31 03/01/29
28 */
29
30
31
32 package com.sun.xml.internal.messaging.saaj.packaging.mime.internet;
33
34 import java.io.*;
35
36 import javax.activation.DataSource;
37
38 import com.sun.xml.internal.messaging.saaj.packaging.mime.*;
39 import com.sun.xml.internal.messaging.saaj.packaging.mime.util.*;
40 import com.sun.xml.internal.messaging.saaj.util.FinalArrayList;
41 import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
42 import com.sun.xml.internal.messaging.saaj.util.SAAJUtil;
43
44 /**
45 * The MimeMultipart class is an implementation
46 * that uses MIME conventions for the multipart data. <p>
47 *
48 * A MimeMultipart is obtained from a MimeBodyPart whose primary type
49 * is "multipart" (by invoking the part's <code>getContent()</code> method)
50 * or it can be created by a client as part of creating a new MimeMessage. <p>
51 *
52 * The default multipart subtype is "mixed". The other multipart
53 * subtypes, such as "alternative", "related", and so on, can be
54 * implemented as subclasses of MimeMultipart with additional methods
55 * to implement the additional semantics of that type of multipart
56 * content. The intent is that service providers, mail JavaBean writers
57 * and mail clients will write many such subclasses and their Command
58 * Beans, and will install them into the JavaBeans Activation
59 * Framework, so that any JavaMail implementation and its clients can
60 * transparently find and use these classes. Thus, a MIME multipart
61 * handler is treated just like any other type handler, thereby
62 * decoupling the process of providing multipart handlers from the
63 * JavaMail API. Lacking these additional MimeMultipart subclasses,
309 */
310 protected void parse() throws MessagingException {
311 if (parsed)
312 return;
313
314 InputStream in;
315 SharedInputStream sin = null;
316 long start = 0, end = 0;
317 boolean foundClosingBoundary = false;
318
319 try {
320 in = ds.getInputStream();
321 if (!(in instanceof ByteArrayInputStream) &&
322 !(in instanceof BufferedInputStream) &&
323 !(in instanceof SharedInputStream))
324 in = new BufferedInputStream(in);
325 } catch (Exception ex) {
326 throw new MessagingException("No inputstream from datasource");
327 }
328 if (in instanceof SharedInputStream)
329 sin = (SharedInputStream)in;
330
331 String boundary = "--" + contentType.getParameter("boundary");
332 byte[] bndbytes = ASCIIUtility.getBytes(boundary);
333 int bl = bndbytes.length;
334
335 ByteOutputStream buf = null;
336 try {
337 // Skip the preamble
338 LineInputStream lin = new LineInputStream(in);
339 String line;
340 while ((line = lin.readLine()) != null) {
341 /*
342 * Strip trailing whitespace. Can't use trim method
343 * because it's too aggressive. Some bogus MIME
344 * messages will include control characters in the
345 * boundary string.
346 */
347 int i;
348 for (i = line.length() - 1; i >= 0; i--) {
349 char c = line.charAt(i);
380 } else {
381 // collect the headers for this body part
382 headers = createInternetHeaders(in);
383 }
384
385 if (!in.markSupported())
386 throw new MessagingException("Stream doesn't support mark");
387
388 buf = null;
389 // if we don't have a shared input stream, we copy the data
390 if (sin == null)
391 buf = new ByteOutputStream();
392 int b;
393 boolean bol = true; // beginning of line flag
394 // the two possible end of line characters
395 int eol1 = -1, eol2 = -1;
396
397 /*
398 * Read and save the content bytes in buf.
399 */
400 for (;;) {
401 if (bol) {
402 /*
403 * At the beginning of a line, check whether the
404 * next line is a boundary.
405 */
406 int i;
407 in.mark(bl + 4 + 1000); // bnd + "--\r\n" + lots of LWSP
408 // read bytes, matching against the boundary
409 for (i = 0; i < bl; i++)
410 if (in.read() != bndbytes[i])
411 break;
412 if (i == bl) {
413 // matched the boundary, check for last boundary
414 int b2 = in.read();
415 if (b2 == '-') {
416 if (in.read() == '-') {
417 done = true;
418 foundClosingBoundary = true;
419 break; // ignore trailing text
420 }
474 }
475 }
476
477 /*
478 * Create a MimeBody element to represent this body part.
479 */
480 MimeBodyPart part;
481 if (sin != null)
482 part = createMimeBodyPart(sin.newStream(start, end));
483 else
484 part = createMimeBodyPart(headers, buf.getBytes(), buf.getCount());
485 addBodyPart(part);
486 }
487 } catch (IOException ioex) {
488 throw new MessagingException("IO Error", ioex);
489 } finally {
490 if (buf != null)
491 buf.close();
492 }
493
494 if (!ignoreMissingEndBoundary && !foundClosingBoundary && sin== null) {
495 throw new MessagingException("Missing End Boundary for Mime Package : EOF while skipping headers");
496 }
497 parsed = true;
498 }
499
500 /**
501 * Create and return an InternetHeaders object that loads the
502 * headers from the given InputStream. Subclasses can override
503 * this method to return a subclass of InternetHeaders, if
504 * necessary. This implementation simply constructs and returns
505 * an InternetHeaders object.
506 *
507 * @param is the InputStream to read the headers from.
508 * @return headers.
509 * @exception MessagingException in case of error.
510 * @since JavaMail 1.2
511 */
512 protected InternetHeaders createInternetHeaders(InputStream is)
513 throws MessagingException {
514 return new InternetHeaders(is);
|
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 /*
27 * @(#)MimeMultipart.java 1.31 03/01/29
28 */
29
30
31
32 package com.sun.xml.internal.messaging.saaj.packaging.mime.internet;
33
34 import com.sun.xml.internal.messaging.saaj.packaging.mime.MessagingException;
35 import com.sun.xml.internal.messaging.saaj.packaging.mime.MultipartDataSource;
36 import com.sun.xml.internal.messaging.saaj.packaging.mime.util.ASCIIUtility;
37 import com.sun.xml.internal.messaging.saaj.packaging.mime.util.LineInputStream;
38 import com.sun.xml.internal.messaging.saaj.packaging.mime.util.OutputUtil;
39 import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
40 import com.sun.xml.internal.messaging.saaj.util.FinalArrayList;
41 import com.sun.xml.internal.messaging.saaj.util.SAAJUtil;
42
43 import javax.activation.DataSource;
44 import java.io.BufferedInputStream;
45 import java.io.ByteArrayInputStream;
46 import java.io.IOException;
47 import java.io.InputStream;
48 import java.io.OutputStream;
49
50 /**
51 * The MimeMultipart class is an implementation
52 * that uses MIME conventions for the multipart data. <p>
53 *
54 * A MimeMultipart is obtained from a MimeBodyPart whose primary type
55 * is "multipart" (by invoking the part's <code>getContent()</code> method)
56 * or it can be created by a client as part of creating a new MimeMessage. <p>
57 *
58 * The default multipart subtype is "mixed". The other multipart
59 * subtypes, such as "alternative", "related", and so on, can be
60 * implemented as subclasses of MimeMultipart with additional methods
61 * to implement the additional semantics of that type of multipart
62 * content. The intent is that service providers, mail JavaBean writers
63 * and mail clients will write many such subclasses and their Command
64 * Beans, and will install them into the JavaBeans Activation
65 * Framework, so that any JavaMail implementation and its clients can
66 * transparently find and use these classes. Thus, a MIME multipart
67 * handler is treated just like any other type handler, thereby
68 * decoupling the process of providing multipart handlers from the
69 * JavaMail API. Lacking these additional MimeMultipart subclasses,
315 */
316 protected void parse() throws MessagingException {
317 if (parsed)
318 return;
319
320 InputStream in;
321 SharedInputStream sin = null;
322 long start = 0, end = 0;
323 boolean foundClosingBoundary = false;
324
325 try {
326 in = ds.getInputStream();
327 if (!(in instanceof ByteArrayInputStream) &&
328 !(in instanceof BufferedInputStream) &&
329 !(in instanceof SharedInputStream))
330 in = new BufferedInputStream(in);
331 } catch (Exception ex) {
332 throw new MessagingException("No inputstream from datasource");
333 }
334 if (in instanceof SharedInputStream)
335 sin = (SharedInputStream) in;
336
337 String boundary = "--" + contentType.getParameter("boundary");
338 byte[] bndbytes = ASCIIUtility.getBytes(boundary);
339 int bl = bndbytes.length;
340
341 ByteOutputStream buf = null;
342 try {
343 // Skip the preamble
344 LineInputStream lin = new LineInputStream(in);
345 String line;
346 while ((line = lin.readLine()) != null) {
347 /*
348 * Strip trailing whitespace. Can't use trim method
349 * because it's too aggressive. Some bogus MIME
350 * messages will include control characters in the
351 * boundary string.
352 */
353 int i;
354 for (i = line.length() - 1; i >= 0; i--) {
355 char c = line.charAt(i);
386 } else {
387 // collect the headers for this body part
388 headers = createInternetHeaders(in);
389 }
390
391 if (!in.markSupported())
392 throw new MessagingException("Stream doesn't support mark");
393
394 buf = null;
395 // if we don't have a shared input stream, we copy the data
396 if (sin == null)
397 buf = new ByteOutputStream();
398 int b;
399 boolean bol = true; // beginning of line flag
400 // the two possible end of line characters
401 int eol1 = -1, eol2 = -1;
402
403 /*
404 * Read and save the content bytes in buf.
405 */
406 for (; ; ) {
407 if (bol) {
408 /*
409 * At the beginning of a line, check whether the
410 * next line is a boundary.
411 */
412 int i;
413 in.mark(bl + 4 + 1000); // bnd + "--\r\n" + lots of LWSP
414 // read bytes, matching against the boundary
415 for (i = 0; i < bl; i++)
416 if (in.read() != bndbytes[i])
417 break;
418 if (i == bl) {
419 // matched the boundary, check for last boundary
420 int b2 = in.read();
421 if (b2 == '-') {
422 if (in.read() == '-') {
423 done = true;
424 foundClosingBoundary = true;
425 break; // ignore trailing text
426 }
480 }
481 }
482
483 /*
484 * Create a MimeBody element to represent this body part.
485 */
486 MimeBodyPart part;
487 if (sin != null)
488 part = createMimeBodyPart(sin.newStream(start, end));
489 else
490 part = createMimeBodyPart(headers, buf.getBytes(), buf.getCount());
491 addBodyPart(part);
492 }
493 } catch (IOException ioex) {
494 throw new MessagingException("IO Error", ioex);
495 } finally {
496 if (buf != null)
497 buf.close();
498 }
499
500 if (!ignoreMissingEndBoundary && !foundClosingBoundary && sin == null) {
501 throw new MessagingException("Missing End Boundary for Mime Package : EOF while skipping headers");
502 }
503 parsed = true;
504 }
505
506 /**
507 * Create and return an InternetHeaders object that loads the
508 * headers from the given InputStream. Subclasses can override
509 * this method to return a subclass of InternetHeaders, if
510 * necessary. This implementation simply constructs and returns
511 * an InternetHeaders object.
512 *
513 * @param is the InputStream to read the headers from.
514 * @return headers.
515 * @exception MessagingException in case of error.
516 * @since JavaMail 1.2
517 */
518 protected InternetHeaders createInternetHeaders(InputStream is)
519 throws MessagingException {
520 return new InternetHeaders(is);
|