src/share/jaxws_classes/com/sun/xml/internal/messaging/saaj/packaging/mime/internet/BMMimeMultipart.java

Thu, 31 Aug 2017 15:18:52 +0800

author
aoqi
date
Thu, 31 Aug 2017 15:18:52 +0800
changeset 637
9c07ef4934dd
parent 368
0989ad8c0860
parent 0
373ffda63c9a
permissions
-rw-r--r--

merge

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

mercurial