jaxws/src/java.xml.soap/share/classes/com/sun/xml/internal/messaging/saaj/packaging/mime/internet/BMMimeMultipart.java
changeset 28641 6b05689b7445
parent 28640 01e4ca94fb0d
parent 28505 7574ac3bb6c1
child 28642 a42fefc69922
equal deleted inserted replaced
28640:01e4ca94fb0d 28641:6b05689b7445
     1 /*
       
     2  * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    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 import java.util.BitSet;
       
    36 
       
    37 import javax.activation.DataSource;
       
    38 
       
    39 import com.sun.xml.internal.messaging.saaj.packaging.mime.*;
       
    40 import com.sun.xml.internal.messaging.saaj.packaging.mime.util.*;
       
    41 
       
    42 import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
       
    43 
       
    44 /**
       
    45  * The MimeMultipart class is an implementation of the abstract Multipart
       
    46  * class that uses MIME conventions for the multipart data. <p>
       
    47  *
       
    48  * A MimeMultipart is obtained from a MimePart 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,
       
    64  * all subtypes of MIME multipart data appear as MimeMultipart objects. <p>
       
    65  *
       
    66  * An application can directly construct a MIME multipart object of any
       
    67  * subtype by using the <code>MimeMultipart(String subtype)</code>
       
    68  * constructor.  For example, to create a "multipart/alternative" object,
       
    69  * use <code>new MimeMultipart("alternative")</code>.
       
    70  *
       
    71  */
       
    72 
       
    73 //TODO: cleanup the SharedInputStream handling
       
    74 public  class BMMimeMultipart extends MimeMultipart {
       
    75 
       
    76     /*
       
    77      * When true it indicates parsing hasnt been done at all
       
    78      */
       
    79     private boolean begining = true;
       
    80 
       
    81     int[] bcs = new int[256];
       
    82     int[] gss = null;
       
    83     private static final int BUFFER_SIZE = 4096;
       
    84     private byte[] buffer = new byte[BUFFER_SIZE];
       
    85     private byte[] prevBuffer = new byte[BUFFER_SIZE];
       
    86     private BitSet lastPartFound = new BitSet(1);
       
    87 
       
    88     // cached inputstream which is possibly partially consumed
       
    89     private InputStream in = null;
       
    90     private String boundary = null;
       
    91     // current stream position, set to -1 on EOF
       
    92     int b = 0;
       
    93 
       
    94     // property to indicate if lazyAttachments is ON
       
    95     private boolean lazyAttachments = false;
       
    96 
       
    97     /**
       
    98      * Default constructor. An empty MimeMultipart object
       
    99      * is created. Its content type is set to "multipart/mixed".
       
   100      * A unique boundary string is generated and this string is
       
   101      * setup as the "boundary" parameter for the
       
   102      * <code>contentType</code> field. <p>
       
   103      *
       
   104      * MimeBodyParts may be added later.
       
   105      */
       
   106     public BMMimeMultipart() {
       
   107         super();
       
   108         //this("mixed");
       
   109     }
       
   110 
       
   111     /**
       
   112      * Construct a MimeMultipart object of the given subtype.
       
   113      * A unique boundary string is generated and this string is
       
   114      * setup as the "boundary" parameter for the
       
   115      * <code>contentType</code> field. <p>
       
   116      *
       
   117      * MimeBodyParts may be added later.
       
   118      */
       
   119     public BMMimeMultipart(String subtype) {
       
   120         super(subtype);
       
   121         /*
       
   122          * Compute a boundary string.
       
   123         String boundary = UniqueValue.getUniqueBoundaryValue();
       
   124         ContentType cType = new ContentType("multipart", subtype, null);
       
   125         contentType.setParameter("boundary", boundary);
       
   126          */
       
   127     }
       
   128 
       
   129     /**
       
   130      * Constructs a MimeMultipart object and its bodyparts from the
       
   131      * given DataSource. <p>
       
   132      *
       
   133      * This constructor handles as a special case the situation where the
       
   134      * given DataSource is a MultipartDataSource object.  In this case, this
       
   135      * method just invokes the superclass (i.e., Multipart) constructor
       
   136      * that takes a MultipartDataSource object. <p>
       
   137      *
       
   138      * Otherwise, the DataSource is assumed to provide a MIME multipart
       
   139      * byte stream.  The <code>parsed</code> flag is set to false.  When
       
   140      * the data for the body parts are needed, the parser extracts the
       
   141      * "boundary" parameter from the content type of this DataSource,
       
   142      * skips the 'preamble' and reads bytes till the terminating
       
   143      * boundary and creates MimeBodyParts for each part of the stream.
       
   144      *
       
   145      * @param   ds      DataSource, can be a MultipartDataSource
       
   146      */
       
   147     public BMMimeMultipart(DataSource ds, ContentType ct)
       
   148         throws MessagingException {
       
   149         super(ds,ct);
       
   150         boundary = ct.getParameter("boundary");
       
   151         /*
       
   152         if (ds instanceof MultipartDataSource) {
       
   153             // ask super to do this for us.
       
   154             setMultipartDataSource((MultipartDataSource)ds);
       
   155             return;
       
   156         }
       
   157 
       
   158         // 'ds' was not a MultipartDataSource, we have
       
   159         // to parse this ourself.
       
   160         parsed = false;
       
   161         this.ds = ds;
       
   162         if (ct==null)
       
   163             contentType = new ContentType(ds.getContentType());
       
   164         else
       
   165             contentType = ct;
       
   166        */
       
   167 
       
   168     }
       
   169 
       
   170     public InputStream initStream() throws MessagingException {
       
   171 
       
   172         if (in == null) {
       
   173             try {
       
   174                 in = ds.getInputStream();
       
   175                 if (!(in instanceof ByteArrayInputStream) &&
       
   176                     !(in instanceof BufferedInputStream) &&
       
   177                     !(in instanceof SharedInputStream))
       
   178                     in = new BufferedInputStream(in);
       
   179             } catch (Exception ex) {
       
   180                 throw new MessagingException("No inputstream from datasource");
       
   181             }
       
   182 
       
   183             if (!in.markSupported()) {
       
   184                 throw new MessagingException(
       
   185                     "InputStream does not support Marking");
       
   186             }
       
   187         }
       
   188         return in;
       
   189     }
       
   190 
       
   191     /**
       
   192      * Parse the InputStream from our DataSource, constructing the
       
   193      * appropriate MimeBodyParts.  The <code>parsed</code> flag is
       
   194      * set to true, and if true on entry nothing is done.  This
       
   195      * method is called by all other methods that need data for
       
   196      * the body parts, to make sure the data has been parsed.
       
   197      *
       
   198      * @since   JavaMail 1.2
       
   199      */
       
   200     protected  void parse() throws  MessagingException {
       
   201         if (parsed)
       
   202             return;
       
   203 
       
   204         initStream();
       
   205 
       
   206         SharedInputStream sin = null;
       
   207         if (in instanceof SharedInputStream) {
       
   208             sin = (SharedInputStream)in;
       
   209         }
       
   210 
       
   211         String bnd = "--" + boundary;
       
   212         byte[] bndbytes = ASCIIUtility.getBytes(bnd);
       
   213         try {
       
   214             parse(in, bndbytes, sin);
       
   215         } catch (IOException ioex) {
       
   216             throw new MessagingException("IO Error", ioex);
       
   217         } catch (Exception ex) {
       
   218             throw new MessagingException("Error", ex);
       
   219         }
       
   220 
       
   221         parsed = true;
       
   222     }
       
   223 
       
   224     public boolean lastBodyPartFound() {
       
   225         return lastPartFound.get(0);
       
   226     }
       
   227 
       
   228     public MimeBodyPart getNextPart(
       
   229         InputStream stream, byte[] pattern, SharedInputStream sin)
       
   230         throws Exception {
       
   231 
       
   232         if (!stream.markSupported()) {
       
   233             throw new Exception("InputStream does not support Marking");
       
   234         }
       
   235 
       
   236         if (begining) {
       
   237             compile(pattern);
       
   238             if (!skipPreamble(stream, pattern, sin)) {
       
   239                 throw new Exception(
       
   240                     "Missing Start Boundary, or boundary does not start on a new line");
       
   241             }
       
   242             begining = false;
       
   243         }
       
   244 
       
   245         if (lastBodyPartFound()) {
       
   246             throw new Exception("No parts found in Multipart InputStream");
       
   247         }
       
   248 
       
   249         if (sin != null) {
       
   250             long start = sin.getPosition();
       
   251             b = readHeaders(stream);
       
   252             if (b == -1) {
       
   253                 throw new Exception(
       
   254                     "End of Stream encountered while reading part headers");
       
   255             }
       
   256             long[] v = new long[1];
       
   257             v[0] = -1; // just to ensure the code later sets it correctly
       
   258             b = readBody(stream, pattern, v, null, sin);
       
   259             // looks like this check has to be disabled
       
   260             // it is allowed to have Mime Package without closing boundary
       
   261             if (!ignoreMissingEndBoundary) {
       
   262                 if ((b == -1) && !lastBodyPartFound()) {
       
   263                     throw new MessagingException("Missing End Boundary for Mime Package : EOF while skipping headers");
       
   264                 }
       
   265             }
       
   266             long end = v[0];
       
   267             MimeBodyPart mbp = createMimeBodyPart(sin.newStream(start, end));
       
   268             addBodyPart(mbp);
       
   269             return mbp;
       
   270 
       
   271         } else {
       
   272             InternetHeaders headers = createInternetHeaders(stream);
       
   273             ByteOutputStream baos = new ByteOutputStream();
       
   274             b = readBody(stream, pattern, null,baos, null);
       
   275             // looks like this check has to be disabled
       
   276             // in the old impl it is allowed to have Mime Package
       
   277             // without closing boundary
       
   278             if (!ignoreMissingEndBoundary) {
       
   279                 if ((b == -1) && !lastBodyPartFound()) {
       
   280                     throw new MessagingException("Missing End Boundary for Mime Package : EOF while skipping headers");
       
   281                 }
       
   282             }
       
   283             MimeBodyPart mbp = createMimeBodyPart(
       
   284                 headers, baos.getBytes(), baos.getCount());
       
   285             addBodyPart(mbp);
       
   286             return mbp;
       
   287         }
       
   288 
       
   289     }
       
   290 
       
   291     public boolean parse(
       
   292         InputStream stream, byte[] pattern, SharedInputStream sin)
       
   293         throws Exception {
       
   294 
       
   295         while (!lastPartFound.get(0) && (b != -1)) {
       
   296            getNextPart(stream, pattern, sin);
       
   297         }
       
   298         return true;
       
   299     }
       
   300 
       
   301     private int readHeaders(InputStream is) throws Exception {
       
   302         // if the headers are to end properly then there has to be CRLF
       
   303         // actually we just need to mark the start and end positions
       
   304         int b = is.read();
       
   305         while(b != -1) {
       
   306             // when it is a shared input stream no need to copy
       
   307             if (b == '\r') {
       
   308                 b = is.read();
       
   309                 if (b == '\n') {
       
   310                     b = is.read();
       
   311                     if (b == '\r') {
       
   312                         b = is.read();
       
   313                         if (b == '\n') {
       
   314                            return b;
       
   315                         } else {
       
   316                             continue;
       
   317                         }
       
   318                     } else {
       
   319                         continue;
       
   320                     }
       
   321                 } else {
       
   322                     continue;
       
   323                 }
       
   324             }
       
   325             b = is.read();
       
   326         }
       
   327         if (b == -1) {
       
   328             throw new Exception(
       
   329             "End of inputstream while reading Mime-Part Headers");
       
   330         }
       
   331         return b;
       
   332     }
       
   333 
       
   334     private int readBody(
       
   335         InputStream is, byte[] pattern, long[] posVector,
       
   336         ByteOutputStream baos, SharedInputStream sin)
       
   337         throws Exception {
       
   338         if (!find(is, pattern, posVector, baos, sin)) {
       
   339             throw new Exception(
       
   340             "Missing boundary delimitier while reading Body Part");
       
   341         }
       
   342         return b;
       
   343     }
       
   344 
       
   345     private boolean skipPreamble(
       
   346         InputStream is, byte[] pattern, SharedInputStream sin)
       
   347         throws Exception {
       
   348         if (!find(is, pattern, sin)) {
       
   349             return false;
       
   350         }
       
   351         if (lastPartFound.get(0)) {
       
   352             throw new Exception(
       
   353             "Found closing boundary delimiter while trying to skip preamble");
       
   354         }
       
   355         return true;
       
   356     }
       
   357 
       
   358 
       
   359     public int  readNext(InputStream is, byte[] buff, int patternLength,
       
   360         BitSet eof, long[] posVector, SharedInputStream sin)
       
   361         throws Exception {
       
   362 
       
   363         int bufferLength = is.read(buffer, 0, patternLength);
       
   364         if (bufferLength == -1) {
       
   365            eof.flip(0);
       
   366         } else if (bufferLength < patternLength) {
       
   367             //repeatedly read patternLength - bufferLength
       
   368             int temp = 0;
       
   369             long pos = 0;
       
   370             int i = bufferLength;
       
   371             for (; i < patternLength; i++) {
       
   372                 if (sin != null) {
       
   373                     pos = sin.getPosition();
       
   374                 }
       
   375                 temp = is.read();
       
   376                 if (temp == -1) {
       
   377                     eof.flip(0);
       
   378                     if (sin != null) {
       
   379                         posVector[0] = pos;
       
   380                     }
       
   381                     break;
       
   382                 }
       
   383                 buffer[i] = (byte)temp;
       
   384             }
       
   385             bufferLength=i;
       
   386         }
       
   387         return bufferLength;
       
   388     }
       
   389 
       
   390     public boolean find(InputStream is, byte[] pattern, SharedInputStream sin)
       
   391         throws Exception {
       
   392         int i;
       
   393         int l = pattern.length;
       
   394         int lx = l -1;
       
   395         BitSet eof = new BitSet(1);
       
   396         long[] posVector = new long[1];
       
   397 
       
   398         while (true) {
       
   399             is.mark(l);
       
   400             readNext(is, buffer, l, eof, posVector, sin);
       
   401             if (eof.get(0)) {
       
   402                 // End of stream
       
   403                 return false;
       
   404             }
       
   405 
       
   406             /*
       
   407             if (bufferLength < l) {
       
   408                 //is.reset();
       
   409                 return false;
       
   410             }*/
       
   411 
       
   412             for(i = lx; i >= 0; i--) {
       
   413                 if (buffer[i] != pattern[i]) {
       
   414                     break;
       
   415                 }
       
   416             }
       
   417 
       
   418             if (i < 0) {
       
   419                 // found the boundary, skip *LWSP-char and CRLF
       
   420                 if (!skipLWSPAndCRLF(is)) {
       
   421                     throw new Exception("Boundary does not terminate with CRLF");
       
   422                 }
       
   423                 return true;
       
   424             }
       
   425 
       
   426             int s = Math.max(i + 1 - bcs[buffer[i] & 0x7f], gss[i]);
       
   427             is.reset();
       
   428             is.skip(s);
       
   429         }
       
   430     }
       
   431 
       
   432     public boolean find(
       
   433         InputStream is, byte[] pattern, long[] posVector,
       
   434         ByteOutputStream out, SharedInputStream sin) throws Exception {
       
   435         int i;
       
   436         int l = pattern.length;
       
   437         int lx = l -1;
       
   438         int bufferLength = 0;
       
   439         int s = 0;
       
   440         long endPos = -1;
       
   441         byte[] tmp = null;
       
   442 
       
   443         boolean first = true;
       
   444         BitSet eof = new BitSet(1);
       
   445 
       
   446         while (true) {
       
   447             is.mark(l);
       
   448             if (!first) {
       
   449                 tmp = prevBuffer;
       
   450                 prevBuffer = buffer;
       
   451                 buffer = tmp;
       
   452             }
       
   453             if (sin != null) {
       
   454                 endPos = sin.getPosition();
       
   455             }
       
   456 
       
   457             bufferLength = readNext(is, buffer, l, eof, posVector, sin);
       
   458 
       
   459             if (bufferLength == -1) {
       
   460                 // End of stream
       
   461                 // looks like it is allowed to not have a closing boundary
       
   462                 //return false;
       
   463                 //if (sin != null) {
       
   464                  //   posVector[0] = endPos;
       
   465                 //}
       
   466                 b = -1;
       
   467                 if ((s == l) && (sin == null)) {
       
   468                     out.write(prevBuffer, 0, s);
       
   469                 }
       
   470                 return true;
       
   471             }
       
   472 
       
   473             if (bufferLength < l) {
       
   474                 if (sin != null) {
       
   475                     //endPos = sin.getPosition();
       
   476                     //posVector[0] = endPos;
       
   477                 } else {
       
   478                     // looks like it is allowed to not have a closing boundary
       
   479                     // in the old implementation
       
   480                         out.write(buffer, 0, bufferLength);
       
   481                 }
       
   482                 // looks like it is allowed to not have a closing boundary
       
   483                 // in the old implementation
       
   484                 //return false;
       
   485                 b = -1;
       
   486                 return true;
       
   487             }
       
   488 
       
   489             for(i = lx; i >= 0; i--) {
       
   490                 if (buffer[i] != pattern[i]) {
       
   491                     break;
       
   492                 }
       
   493             }
       
   494 
       
   495             if (i < 0) {
       
   496                 if (s > 0) {
       
   497                     //looks like the earlier impl allowed just an LF
       
   498                     // so if s == 1 : it must be an LF
       
   499                     // if s == 2 : it must be a CR LF
       
   500                     if (s <= 2) {
       
   501                         //it could be "some-char\n" so write some-char
       
   502                         if (s == 2) {
       
   503                             if (prevBuffer[1] == '\n') {
       
   504                                 if (prevBuffer[0] != '\r' && prevBuffer[0] != '\n') {
       
   505                                     out.write(prevBuffer,0,1);
       
   506                                 }
       
   507                                 if (sin != null) {
       
   508                                     posVector[0] = endPos;
       
   509                                 }
       
   510 
       
   511                             } else {
       
   512                                 throw new Exception(
       
   513                                         "Boundary characters encountered in part Body " +
       
   514                                         "without a preceeding CRLF");
       
   515                             }
       
   516 
       
   517                         } else if (s==1) {
       
   518                             if (prevBuffer[0] != '\n') {
       
   519                                 throw new Exception(
       
   520                                         "Boundary characters encountered in part Body " +
       
   521                                         "without a preceeding CRLF");
       
   522                             }else {
       
   523                                 if (sin != null) {
       
   524                                     posVector[0] = endPos;
       
   525                                 }
       
   526                             }
       
   527                         }
       
   528 
       
   529                     } else if (s > 2) {
       
   530                         if ((prevBuffer[s-2] == '\r') && (prevBuffer[s-1] == '\n')) {
       
   531                             if (sin != null) {
       
   532                                 posVector[0] = endPos - 2;
       
   533                             } else {
       
   534                                 out.write(prevBuffer, 0, s - 2);
       
   535                             }
       
   536                         } else if (prevBuffer[s-1] == '\n') {
       
   537                             //old impl allowed just a \n
       
   538                             if (sin != null) {
       
   539                                 posVector[0] = endPos - 1;
       
   540                             } else {
       
   541                                 out.write(prevBuffer, 0, s - 1);
       
   542                             }
       
   543                         } else {
       
   544                             throw new Exception(
       
   545                                 "Boundary characters encountered in part Body " +
       
   546                                 "without a preceeding CRLF");
       
   547                         }
       
   548                     }
       
   549                 }
       
   550                 // found the boundary, skip *LWSP-char and CRLF
       
   551                 if (!skipLWSPAndCRLF(is)) {
       
   552                     //throw new Exception(
       
   553                     //   "Boundary does not terminate with CRLF");
       
   554                 }
       
   555                 return true;
       
   556             }
       
   557 
       
   558             if ((s > 0) && (sin == null)) {
       
   559                 if (prevBuffer[s-1] == (byte)13) {
       
   560                     // if buffer[0] == (byte)10
       
   561                     if (buffer[0] == (byte)10) {
       
   562                         int j;
       
   563                         for(j = lx-1; j > 0; j--) {
       
   564                             if (buffer[j+1] != pattern[j]) {
       
   565                                 break;
       
   566                              }
       
   567                          }
       
   568                          if (j == 0) {
       
   569                              // matched the pattern excluding the last char of the pattern
       
   570                              // so dont write the CR into stream
       
   571                              out.write(prevBuffer,0,s-1);
       
   572                          } else {
       
   573                              out.write(prevBuffer,0,s);
       
   574                          }
       
   575                     } else {
       
   576                         out.write(prevBuffer, 0, s);
       
   577                     }
       
   578                 } else {
       
   579                     out.write(prevBuffer, 0, s);
       
   580                 }
       
   581             }
       
   582 
       
   583             s = Math.max(i + 1 - bcs[buffer[i] & 0x7f], gss[i]);
       
   584             is.reset();
       
   585             is.skip(s);
       
   586             if (first) {
       
   587                 first = false;
       
   588             }
       
   589         }
       
   590     }
       
   591 
       
   592     private boolean skipLWSPAndCRLF(InputStream is) throws Exception {
       
   593 
       
   594         b = is.read();
       
   595         //looks like old impl allowed just a \n as well
       
   596         if (b == '\n') {
       
   597             return true;
       
   598         }
       
   599 
       
   600         if (b == '\r') {
       
   601             b = is.read();
       
   602             //skip any multiple '\r' "\r\n" --> "\r\r\n" on Win2k
       
   603             if (b == '\r') {
       
   604                 b = is.read();
       
   605             }
       
   606             if (b == '\n') {
       
   607                 return true;
       
   608             } else {
       
   609                 throw new Exception(
       
   610                     "transport padding after a Mime Boundary  should end in a CRLF, found CR only");
       
   611             }
       
   612         }
       
   613 
       
   614         if (b == '-') {
       
   615             b = is.read();
       
   616             if (b != '-') {
       
   617                throw new Exception(
       
   618                    "Unexpected singular '-' character after Mime Boundary");
       
   619             } else {
       
   620                 //System.out.println("Last Part Found");
       
   621                 lastPartFound.flip(0);
       
   622                 // read the next char
       
   623                 b  = is.read();
       
   624             }
       
   625         }
       
   626 
       
   627         while ((b != -1) && ((b == ' ') || (b == '\t'))) {
       
   628             b = is.read();
       
   629             if (b == '\n') {
       
   630                 return true;
       
   631             }
       
   632             if (b == '\r') {
       
   633                 b = is.read();
       
   634                 //skip any multiple '\r': "\r\n" --> "\r\r\n" on Win2k
       
   635                 if (b == '\r') {
       
   636                     b = is.read();
       
   637                 }
       
   638                 if (b == '\n') {
       
   639                    return true;
       
   640                 }
       
   641             }
       
   642         }
       
   643 
       
   644         if (b == -1) {
       
   645             // the last boundary need not have CRLF
       
   646             if (!lastPartFound.get(0)) {
       
   647                 throw new Exception(
       
   648                         "End of Multipart Stream before encountering  closing boundary delimiter");
       
   649             }
       
   650             return true;
       
   651         }
       
   652         return false;
       
   653     }
       
   654 
       
   655     private void compile(byte[] pattern) {
       
   656         int l = pattern.length;
       
   657 
       
   658         int i;
       
   659         int j;
       
   660 
       
   661         // Copied from J2SE 1.4 regex code
       
   662         // java.util.regex.Pattern.java
       
   663 
       
   664         // Initialise Bad Character Shift table
       
   665         for (i = 0; i < l; i++) {
       
   666             bcs[pattern[i]] = i + 1;
       
   667         }
       
   668 
       
   669         // Initialise Good Suffix Shift table
       
   670         gss = new int[l];
       
   671   NEXT: for (i = l; i > 0; i--) {
       
   672             // j is the beginning index of suffix being considered
       
   673             for (j = l - 1; j >= i; j--) {
       
   674                 // Testing for good suffix
       
   675                 if (pattern[j] == pattern[j - i]) {
       
   676                     // pattern[j..len] is a good suffix
       
   677                     gss[j - 1] = i;
       
   678                 } else {
       
   679                    // No match. The array has already been
       
   680                    // filled up with correct values before.
       
   681                    continue NEXT;
       
   682                 }
       
   683             }
       
   684             while (j > 0) {
       
   685                 gss[--j] = i;
       
   686             }
       
   687         }
       
   688         gss[l - 1] = 1;
       
   689     }
       
   690 
       
   691 
       
   692     /**
       
   693      * Iterates through all the parts and outputs each Mime part
       
   694      * separated by a boundary.
       
   695      */
       
   696 
       
   697     public void writeTo(OutputStream os)
       
   698             throws IOException, MessagingException {
       
   699 
       
   700         // inputStream was not null
       
   701         if (in != null) {
       
   702             contentType.setParameter("boundary", this.boundary);
       
   703         }
       
   704 
       
   705         String bnd = "--" + contentType.getParameter("boundary");
       
   706         for (int i = 0; i < parts.size(); i++) {
       
   707             OutputUtil.writeln(bnd, os); // put out boundary
       
   708             ((MimeBodyPart)parts.get(i)).writeTo(os);
       
   709             OutputUtil.writeln(os); // put out empty line
       
   710         }
       
   711 
       
   712         if (in != null) {
       
   713             OutputUtil.writeln(bnd, os); // put out boundary
       
   714             if ((os instanceof ByteOutputStream) && lazyAttachments) {
       
   715                 ((ByteOutputStream) os).write(in);
       
   716             } else {
       
   717                 ByteOutputStream baos = null;
       
   718                 try {
       
   719                     baos = new ByteOutputStream(in.available());
       
   720                     baos.write(in);
       
   721                     baos.writeTo(os);
       
   722                     // reset the inputstream so that we can support a
       
   723                     // getAttachment later
       
   724                     in = baos.newInputStream();
       
   725                 } finally {
       
   726                     if (baos != null)
       
   727                         baos.close();
       
   728                 }
       
   729             }
       
   730 
       
   731             // this will endup writing the end boundary
       
   732         } else {
       
   733             // put out last boundary
       
   734             OutputUtil.writeAsAscii(bnd, os);
       
   735             OutputUtil.writeAsAscii("--", os);
       
   736         }
       
   737     }
       
   738 
       
   739     public void setInputStream(InputStream is) {
       
   740         this.in = is;
       
   741     }
       
   742 
       
   743     public InputStream getInputStream() {
       
   744         return this.in;
       
   745     }
       
   746 
       
   747     public void setBoundary(String bnd) {
       
   748         this.boundary = bnd;
       
   749         if (this.contentType != null) {
       
   750             this.contentType.setParameter("boundary", bnd);
       
   751         }
       
   752     }
       
   753     public String getBoundary() {
       
   754         return this.boundary;
       
   755     }
       
   756 
       
   757     public boolean isEndOfStream() {
       
   758         return (b == -1);
       
   759     }
       
   760 
       
   761     public void setLazyAttachments(boolean flag) {
       
   762         lazyAttachments = flag;
       
   763     }
       
   764 
       
   765 }