1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/share/jaxws_classes/com/sun/xml/internal/messaging/saaj/packaging/mime/internet/BMMimeMultipart.java Wed Apr 27 01:27:09 2016 +0800 1.3 @@ -0,0 +1,762 @@ 1.4 +/* 1.5 + * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. Oracle designates this 1.11 + * particular file as subject to the "Classpath" exception as provided 1.12 + * by Oracle in the LICENSE file that accompanied this code. 1.13 + * 1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.17 + * version 2 for more details (a copy is included in the LICENSE file that 1.18 + * accompanied this code). 1.19 + * 1.20 + * You should have received a copy of the GNU General Public License version 1.21 + * 2 along with this work; if not, write to the Free Software Foundation, 1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.23 + * 1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 1.25 + * or visit www.oracle.com if you need additional information or have any 1.26 + * questions. 1.27 + */ 1.28 + 1.29 +/* 1.30 + * @(#)MimeMultipart.java 1.31 03/01/29 1.31 + */ 1.32 + 1.33 + 1.34 + 1.35 +package com.sun.xml.internal.messaging.saaj.packaging.mime.internet; 1.36 + 1.37 +import java.io.*; 1.38 +import java.util.BitSet; 1.39 + 1.40 +import javax.activation.DataSource; 1.41 + 1.42 +import com.sun.xml.internal.messaging.saaj.packaging.mime.*; 1.43 +import com.sun.xml.internal.messaging.saaj.packaging.mime.util.*; 1.44 + 1.45 +import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream; 1.46 +import com.sun.xml.internal.messaging.saaj.util.FinalArrayList; 1.47 + 1.48 +/** 1.49 + * The MimeMultipart class is an implementation of the abstract Multipart 1.50 + * class that uses MIME conventions for the multipart data. <p> 1.51 + * 1.52 + * A MimeMultipart is obtained from a MimePart whose primary type 1.53 + * is "multipart" (by invoking the part's <code>getContent()</code> method) 1.54 + * or it can be created by a client as part of creating a new MimeMessage. <p> 1.55 + * 1.56 + * The default multipart subtype is "mixed". The other multipart 1.57 + * subtypes, such as "alternative", "related", and so on, can be 1.58 + * implemented as subclasses of MimeMultipart with additional methods 1.59 + * to implement the additional semantics of that type of multipart 1.60 + * content. The intent is that service providers, mail JavaBean writers 1.61 + * and mail clients will write many such subclasses and their Command 1.62 + * Beans, and will install them into the JavaBeans Activation 1.63 + * Framework, so that any JavaMail implementation and its clients can 1.64 + * transparently find and use these classes. Thus, a MIME multipart 1.65 + * handler is treated just like any other type handler, thereby 1.66 + * decoupling the process of providing multipart handlers from the 1.67 + * JavaMail API. Lacking these additional MimeMultipart subclasses, 1.68 + * all subtypes of MIME multipart data appear as MimeMultipart objects. <p> 1.69 + * 1.70 + * An application can directly construct a MIME multipart object of any 1.71 + * subtype by using the <code>MimeMultipart(String subtype)</code> 1.72 + * constructor. For example, to create a "multipart/alternative" object, 1.73 + * use <code>new MimeMultipart("alternative")</code>. 1.74 + * 1.75 + */ 1.76 + 1.77 +//TODO: cleanup the SharedInputStream handling 1.78 +public class BMMimeMultipart extends MimeMultipart { 1.79 + 1.80 + /* 1.81 + * When true it indicates parsing hasnt been done at all 1.82 + */ 1.83 + private boolean begining = true; 1.84 + 1.85 + int[] bcs = new int[256]; 1.86 + int[] gss = null; 1.87 + private static final int BUFFER_SIZE = 4096; 1.88 + private byte[] buffer = new byte[BUFFER_SIZE]; 1.89 + private byte[] prevBuffer = new byte[BUFFER_SIZE]; 1.90 + private BitSet lastPartFound = new BitSet(1); 1.91 + 1.92 + // cached inputstream which is possibly partially consumed 1.93 + private InputStream in = null; 1.94 + private String boundary = null; 1.95 + // current stream position, set to -1 on EOF 1.96 + int b = 0; 1.97 + 1.98 + // property to indicate if lazyAttachments is ON 1.99 + private boolean lazyAttachments = false; 1.100 + 1.101 + /** 1.102 + * Default constructor. An empty MimeMultipart object 1.103 + * is created. Its content type is set to "multipart/mixed". 1.104 + * A unique boundary string is generated and this string is 1.105 + * setup as the "boundary" parameter for the 1.106 + * <code>contentType</code> field. <p> 1.107 + * 1.108 + * MimeBodyParts may be added later. 1.109 + */ 1.110 + public BMMimeMultipart() { 1.111 + super(); 1.112 + //this("mixed"); 1.113 + } 1.114 + 1.115 + /** 1.116 + * Construct a MimeMultipart object of the given subtype. 1.117 + * A unique boundary string is generated and this string is 1.118 + * setup as the "boundary" parameter for the 1.119 + * <code>contentType</code> field. <p> 1.120 + * 1.121 + * MimeBodyParts may be added later. 1.122 + */ 1.123 + public BMMimeMultipart(String subtype) { 1.124 + super(subtype); 1.125 + /* 1.126 + * Compute a boundary string. 1.127 + String boundary = UniqueValue.getUniqueBoundaryValue(); 1.128 + ContentType cType = new ContentType("multipart", subtype, null); 1.129 + contentType.setParameter("boundary", boundary); 1.130 + */ 1.131 + } 1.132 + 1.133 + /** 1.134 + * Constructs a MimeMultipart object and its bodyparts from the 1.135 + * given DataSource. <p> 1.136 + * 1.137 + * This constructor handles as a special case the situation where the 1.138 + * given DataSource is a MultipartDataSource object. In this case, this 1.139 + * method just invokes the superclass (i.e., Multipart) constructor 1.140 + * that takes a MultipartDataSource object. <p> 1.141 + * 1.142 + * Otherwise, the DataSource is assumed to provide a MIME multipart 1.143 + * byte stream. The <code>parsed</code> flag is set to false. When 1.144 + * the data for the body parts are needed, the parser extracts the 1.145 + * "boundary" parameter from the content type of this DataSource, 1.146 + * skips the 'preamble' and reads bytes till the terminating 1.147 + * boundary and creates MimeBodyParts for each part of the stream. 1.148 + * 1.149 + * @param ds DataSource, can be a MultipartDataSource 1.150 + */ 1.151 + public BMMimeMultipart(DataSource ds, ContentType ct) 1.152 + throws MessagingException { 1.153 + super(ds,ct); 1.154 + boundary = ct.getParameter("boundary"); 1.155 + /* 1.156 + if (ds instanceof MultipartDataSource) { 1.157 + // ask super to do this for us. 1.158 + setMultipartDataSource((MultipartDataSource)ds); 1.159 + return; 1.160 + } 1.161 + 1.162 + // 'ds' was not a MultipartDataSource, we have 1.163 + // to parse this ourself. 1.164 + parsed = false; 1.165 + this.ds = ds; 1.166 + if (ct==null) 1.167 + contentType = new ContentType(ds.getContentType()); 1.168 + else 1.169 + contentType = ct; 1.170 + */ 1.171 + 1.172 + } 1.173 + 1.174 + public InputStream initStream() throws MessagingException { 1.175 + 1.176 + if (in == null) { 1.177 + try { 1.178 + in = ds.getInputStream(); 1.179 + if (!(in instanceof ByteArrayInputStream) && 1.180 + !(in instanceof BufferedInputStream) && 1.181 + !(in instanceof SharedInputStream)) 1.182 + in = new BufferedInputStream(in); 1.183 + } catch (Exception ex) { 1.184 + throw new MessagingException("No inputstream from datasource"); 1.185 + } 1.186 + 1.187 + if (!in.markSupported()) { 1.188 + throw new MessagingException( 1.189 + "InputStream does not support Marking"); 1.190 + } 1.191 + } 1.192 + return in; 1.193 + } 1.194 + 1.195 + /** 1.196 + * Parse the InputStream from our DataSource, constructing the 1.197 + * appropriate MimeBodyParts. The <code>parsed</code> flag is 1.198 + * set to true, and if true on entry nothing is done. This 1.199 + * method is called by all other methods that need data for 1.200 + * the body parts, to make sure the data has been parsed. 1.201 + * 1.202 + * @since JavaMail 1.2 1.203 + */ 1.204 + protected void parse() throws MessagingException { 1.205 + if (parsed) 1.206 + return; 1.207 + 1.208 + initStream(); 1.209 + 1.210 + SharedInputStream sin = null; 1.211 + if (in instanceof SharedInputStream) { 1.212 + sin = (SharedInputStream)in; 1.213 + } 1.214 + 1.215 + String bnd = "--" + boundary; 1.216 + byte[] bndbytes = ASCIIUtility.getBytes(bnd); 1.217 + try { 1.218 + parse(in, bndbytes, sin); 1.219 + } catch (IOException ioex) { 1.220 + throw new MessagingException("IO Error", ioex); 1.221 + } catch (Exception ex) { 1.222 + throw new MessagingException("Error", ex); 1.223 + } 1.224 + 1.225 + parsed = true; 1.226 + } 1.227 + 1.228 + public boolean lastBodyPartFound() { 1.229 + return lastPartFound.get(0); 1.230 + } 1.231 + 1.232 + public MimeBodyPart getNextPart( 1.233 + InputStream stream, byte[] pattern, SharedInputStream sin) 1.234 + throws Exception { 1.235 + 1.236 + if (!stream.markSupported()) { 1.237 + throw new Exception("InputStream does not support Marking"); 1.238 + } 1.239 + 1.240 + if (begining) { 1.241 + compile(pattern); 1.242 + if (!skipPreamble(stream, pattern, sin)) { 1.243 + throw new Exception( 1.244 + "Missing Start Boundary, or boundary does not start on a new line"); 1.245 + } 1.246 + begining = false; 1.247 + } 1.248 + 1.249 + if (lastBodyPartFound()) { 1.250 + throw new Exception("No parts found in Multipart InputStream"); 1.251 + } 1.252 + 1.253 + if (sin != null) { 1.254 + long start = sin.getPosition(); 1.255 + b = readHeaders(stream); 1.256 + if (b == -1) { 1.257 + throw new Exception( 1.258 + "End of Stream encountered while reading part headers"); 1.259 + } 1.260 + long[] v = new long[1]; 1.261 + v[0] = -1; // just to ensure the code later sets it correctly 1.262 + b = readBody(stream, pattern, v, null, sin); 1.263 + // looks like this check has to be disabled 1.264 + // it is allowed to have Mime Package without closing boundary 1.265 + if (!ignoreMissingEndBoundary) { 1.266 + if ((b == -1) && !lastBodyPartFound()) { 1.267 + throw new MessagingException("Missing End Boundary for Mime Package : EOF while skipping headers"); 1.268 + } 1.269 + } 1.270 + long end = v[0]; 1.271 + MimeBodyPart mbp = createMimeBodyPart(sin.newStream(start, end)); 1.272 + addBodyPart(mbp); 1.273 + return mbp; 1.274 + 1.275 + } else { 1.276 + InternetHeaders headers = createInternetHeaders(stream); 1.277 + ByteOutputStream baos = new ByteOutputStream(); 1.278 + b = readBody(stream, pattern, null,baos, null); 1.279 + // looks like this check has to be disabled 1.280 + // in the old impl it is allowed to have Mime Package 1.281 + // without closing boundary 1.282 + if (!ignoreMissingEndBoundary) { 1.283 + if ((b == -1) && !lastBodyPartFound()) { 1.284 + throw new MessagingException("Missing End Boundary for Mime Package : EOF while skipping headers"); 1.285 + } 1.286 + } 1.287 + MimeBodyPart mbp = createMimeBodyPart( 1.288 + headers, baos.getBytes(), baos.getCount()); 1.289 + addBodyPart(mbp); 1.290 + return mbp; 1.291 + } 1.292 + 1.293 + } 1.294 + 1.295 + public boolean parse( 1.296 + InputStream stream, byte[] pattern, SharedInputStream sin) 1.297 + throws Exception { 1.298 + 1.299 + while (!lastPartFound.get(0) && (b != -1)) { 1.300 + getNextPart(stream, pattern, sin); 1.301 + } 1.302 + return true; 1.303 + } 1.304 + 1.305 + private int readHeaders(InputStream is) throws Exception { 1.306 + // if the headers are to end properly then there has to be CRLF 1.307 + // actually we just need to mark the start and end positions 1.308 + int b = is.read(); 1.309 + while(b != -1) { 1.310 + // when it is a shared input stream no need to copy 1.311 + if (b == '\r') { 1.312 + b = is.read(); 1.313 + if (b == '\n') { 1.314 + b = is.read(); 1.315 + if (b == '\r') { 1.316 + b = is.read(); 1.317 + if (b == '\n') { 1.318 + return b; 1.319 + } else { 1.320 + continue; 1.321 + } 1.322 + } else { 1.323 + continue; 1.324 + } 1.325 + } else { 1.326 + continue; 1.327 + } 1.328 + } 1.329 + b = is.read(); 1.330 + } 1.331 + if (b == -1) { 1.332 + throw new Exception( 1.333 + "End of inputstream while reading Mime-Part Headers"); 1.334 + } 1.335 + return b; 1.336 + } 1.337 + 1.338 + private int readBody( 1.339 + InputStream is, byte[] pattern, long[] posVector, 1.340 + ByteOutputStream baos, SharedInputStream sin) 1.341 + throws Exception { 1.342 + if (!find(is, pattern, posVector, baos, sin)) { 1.343 + throw new Exception( 1.344 + "Missing boundary delimitier while reading Body Part"); 1.345 + } 1.346 + return b; 1.347 + } 1.348 + 1.349 + private boolean skipPreamble( 1.350 + InputStream is, byte[] pattern, SharedInputStream sin) 1.351 + throws Exception { 1.352 + if (!find(is, pattern, sin)) { 1.353 + return false; 1.354 + } 1.355 + if (lastPartFound.get(0)) { 1.356 + throw new Exception( 1.357 + "Found closing boundary delimiter while trying to skip preamble"); 1.358 + } 1.359 + return true; 1.360 + } 1.361 + 1.362 + 1.363 + public int readNext(InputStream is, byte[] buff, int patternLength, 1.364 + BitSet eof, long[] posVector, SharedInputStream sin) 1.365 + throws Exception { 1.366 + 1.367 + int bufferLength = is.read(buffer, 0, patternLength); 1.368 + if (bufferLength == -1) { 1.369 + eof.flip(0); 1.370 + } else if (bufferLength < patternLength) { 1.371 + //repeatedly read patternLength - bufferLength 1.372 + int temp = 0; 1.373 + long pos = 0; 1.374 + int i = bufferLength; 1.375 + for (; i < patternLength; i++) { 1.376 + if (sin != null) { 1.377 + pos = sin.getPosition(); 1.378 + } 1.379 + temp = is.read(); 1.380 + if (temp == -1) { 1.381 + eof.flip(0); 1.382 + if (sin != null) { 1.383 + posVector[0] = pos; 1.384 + } 1.385 + break; 1.386 + } 1.387 + buffer[i] = (byte)temp; 1.388 + } 1.389 + bufferLength=i; 1.390 + } 1.391 + return bufferLength; 1.392 + } 1.393 + 1.394 + public boolean find(InputStream is, byte[] pattern, SharedInputStream sin) 1.395 + throws Exception { 1.396 + int i; 1.397 + int l = pattern.length; 1.398 + int lx = l -1; 1.399 + int bufferLength = 0; 1.400 + BitSet eof = new BitSet(1); 1.401 + long[] posVector = new long[1]; 1.402 + 1.403 + while (true) { 1.404 + is.mark(l); 1.405 + bufferLength = readNext(is, buffer, l, eof, posVector, sin); 1.406 + if (eof.get(0)) { 1.407 + // End of stream 1.408 + return false; 1.409 + } 1.410 + 1.411 + /* 1.412 + if (bufferLength < l) { 1.413 + //is.reset(); 1.414 + return false; 1.415 + }*/ 1.416 + 1.417 + for(i = lx; i >= 0; i--) { 1.418 + if (buffer[i] != pattern[i]) { 1.419 + break; 1.420 + } 1.421 + } 1.422 + 1.423 + if (i < 0) { 1.424 + // found the boundary, skip *LWSP-char and CRLF 1.425 + if (!skipLWSPAndCRLF(is)) { 1.426 + throw new Exception("Boundary does not terminate with CRLF"); 1.427 + } 1.428 + return true; 1.429 + } 1.430 + 1.431 + int s = Math.max(i + 1 - bcs[buffer[i] & 0x7f], gss[i]); 1.432 + is.reset(); 1.433 + is.skip(s); 1.434 + } 1.435 + } 1.436 + 1.437 + public boolean find( 1.438 + InputStream is, byte[] pattern, long[] posVector, 1.439 + ByteOutputStream out, SharedInputStream sin) throws Exception { 1.440 + int i; 1.441 + int l = pattern.length; 1.442 + int lx = l -1; 1.443 + int bufferLength = 0; 1.444 + int s = 0; 1.445 + long endPos = -1; 1.446 + byte[] tmp = null; 1.447 + 1.448 + boolean first = true; 1.449 + BitSet eof = new BitSet(1); 1.450 + 1.451 + while (true) { 1.452 + is.mark(l); 1.453 + if (!first) { 1.454 + tmp = prevBuffer; 1.455 + prevBuffer = buffer; 1.456 + buffer = tmp; 1.457 + } 1.458 + if (sin != null) { 1.459 + endPos = sin.getPosition(); 1.460 + } 1.461 + 1.462 + bufferLength = readNext(is, buffer, l, eof, posVector, sin); 1.463 + 1.464 + if (bufferLength == -1) { 1.465 + // End of stream 1.466 + // looks like it is allowed to not have a closing boundary 1.467 + //return false; 1.468 + //if (sin != null) { 1.469 + // posVector[0] = endPos; 1.470 + //} 1.471 + b = -1; 1.472 + if ((s == l) && (sin == null)) { 1.473 + out.write(prevBuffer, 0, s); 1.474 + } 1.475 + return true; 1.476 + } 1.477 + 1.478 + if (bufferLength < l) { 1.479 + if (sin != null) { 1.480 + //endPos = sin.getPosition(); 1.481 + //posVector[0] = endPos; 1.482 + } else { 1.483 + // looks like it is allowed to not have a closing boundary 1.484 + // in the old implementation 1.485 + out.write(buffer, 0, bufferLength); 1.486 + } 1.487 + // looks like it is allowed to not have a closing boundary 1.488 + // in the old implementation 1.489 + //return false; 1.490 + b = -1; 1.491 + return true; 1.492 + } 1.493 + 1.494 + for(i = lx; i >= 0; i--) { 1.495 + if (buffer[i] != pattern[i]) { 1.496 + break; 1.497 + } 1.498 + } 1.499 + 1.500 + if (i < 0) { 1.501 + if (s > 0) { 1.502 + //looks like the earlier impl allowed just an LF 1.503 + // so if s == 1 : it must be an LF 1.504 + // if s == 2 : it must be a CR LF 1.505 + if (s <= 2) { 1.506 + //it could be "some-char\n" so write some-char 1.507 + if (s == 2) { 1.508 + if (prevBuffer[1] == '\n') { 1.509 + if (prevBuffer[0] != '\r' && prevBuffer[0] != '\n') { 1.510 + out.write(prevBuffer,0,1); 1.511 + } 1.512 + if (sin != null) { 1.513 + posVector[0] = endPos; 1.514 + } 1.515 + 1.516 + } else { 1.517 + throw new Exception( 1.518 + "Boundary characters encountered in part Body " + 1.519 + "without a preceeding CRLF"); 1.520 + } 1.521 + 1.522 + } else if (s==1) { 1.523 + if (prevBuffer[0] != '\n') { 1.524 + throw new Exception( 1.525 + "Boundary characters encountered in part Body " + 1.526 + "without a preceeding CRLF"); 1.527 + }else { 1.528 + if (sin != null) { 1.529 + posVector[0] = endPos; 1.530 + } 1.531 + } 1.532 + } 1.533 + 1.534 + } else if (s > 2) { 1.535 + if ((prevBuffer[s-2] == '\r') && (prevBuffer[s-1] == '\n')) { 1.536 + if (sin != null) { 1.537 + posVector[0] = endPos - 2; 1.538 + } else { 1.539 + out.write(prevBuffer, 0, s - 2); 1.540 + } 1.541 + } else if (prevBuffer[s-1] == '\n') { 1.542 + //old impl allowed just a \n 1.543 + if (sin != null) { 1.544 + posVector[0] = endPos - 1; 1.545 + } else { 1.546 + out.write(prevBuffer, 0, s - 1); 1.547 + } 1.548 + } else { 1.549 + throw new Exception( 1.550 + "Boundary characters encountered in part Body " + 1.551 + "without a preceeding CRLF"); 1.552 + } 1.553 + } 1.554 + } 1.555 + // found the boundary, skip *LWSP-char and CRLF 1.556 + if (!skipLWSPAndCRLF(is)) { 1.557 + //throw new Exception( 1.558 + // "Boundary does not terminate with CRLF"); 1.559 + } 1.560 + return true; 1.561 + } 1.562 + 1.563 + if ((s > 0) && (sin == null)) { 1.564 + if (prevBuffer[s-1] == (byte)13) { 1.565 + // if buffer[0] == (byte)10 1.566 + if (buffer[0] == (byte)10) { 1.567 + int j=lx-1; 1.568 + for(j = lx-1; j > 0; j--) { 1.569 + if (buffer[j+1] != pattern[j]) { 1.570 + break; 1.571 + } 1.572 + } 1.573 + if (j == 0) { 1.574 + // matched the pattern excluding the last char of the pattern 1.575 + // so dont write the CR into stream 1.576 + out.write(prevBuffer,0,s-1); 1.577 + } else { 1.578 + out.write(prevBuffer,0,s); 1.579 + } 1.580 + } else { 1.581 + out.write(prevBuffer, 0, s); 1.582 + } 1.583 + } else { 1.584 + out.write(prevBuffer, 0, s); 1.585 + } 1.586 + } 1.587 + 1.588 + s = Math.max(i + 1 - bcs[buffer[i] & 0x7f], gss[i]); 1.589 + is.reset(); 1.590 + is.skip(s); 1.591 + if (first) { 1.592 + first = false; 1.593 + } 1.594 + } 1.595 + } 1.596 + 1.597 + private boolean skipLWSPAndCRLF(InputStream is) throws Exception { 1.598 + 1.599 + b = is.read(); 1.600 + //looks like old impl allowed just a \n as well 1.601 + if (b == '\n') { 1.602 + return true; 1.603 + } 1.604 + 1.605 + if (b == '\r') { 1.606 + b = is.read(); 1.607 + //skip any multiple '\r' "\r\n" --> "\r\r\n" on Win2k 1.608 + if (b == '\r') { 1.609 + b = is.read(); 1.610 + } 1.611 + if (b == '\n') { 1.612 + return true; 1.613 + } else { 1.614 + throw new Exception( 1.615 + "transport padding after a Mime Boundary should end in a CRLF, found CR only"); 1.616 + } 1.617 + } 1.618 + 1.619 + if (b == '-') { 1.620 + b = is.read(); 1.621 + if (b != '-') { 1.622 + throw new Exception( 1.623 + "Unexpected singular '-' character after Mime Boundary"); 1.624 + } else { 1.625 + //System.out.println("Last Part Found"); 1.626 + lastPartFound.flip(0); 1.627 + // read the next char 1.628 + b = is.read(); 1.629 + } 1.630 + } 1.631 + 1.632 + while ((b != -1) && ((b == ' ') || (b == '\t'))) { 1.633 + b = is.read(); 1.634 + if (b == '\n') { 1.635 + return true; 1.636 + } 1.637 + if (b == '\r') { 1.638 + b = is.read(); 1.639 + //skip any multiple '\r': "\r\n" --> "\r\r\n" on Win2k 1.640 + if (b == '\r') { 1.641 + b = is.read(); 1.642 + } 1.643 + if (b == '\n') { 1.644 + return true; 1.645 + } 1.646 + } 1.647 + } 1.648 + 1.649 + if (b == -1) { 1.650 + // the last boundary need not have CRLF 1.651 + if (!lastPartFound.get(0)) { 1.652 + throw new Exception( 1.653 + "End of Multipart Stream before encountering closing boundary delimiter"); 1.654 + } 1.655 + return true; 1.656 + } 1.657 + return false; 1.658 + } 1.659 + 1.660 + private void compile(byte[] pattern) { 1.661 + int l = pattern.length; 1.662 + 1.663 + int i; 1.664 + int j; 1.665 + 1.666 + // Copied from J2SE 1.4 regex code 1.667 + // java.util.regex.Pattern.java 1.668 + 1.669 + // Initialise Bad Character Shift table 1.670 + for (i = 0; i < l; i++) { 1.671 + bcs[pattern[i]] = i + 1; 1.672 + } 1.673 + 1.674 + // Initialise Good Suffix Shift table 1.675 + gss = new int[l]; 1.676 + NEXT: for (i = l; i > 0; i--) { 1.677 + // j is the beginning index of suffix being considered 1.678 + for (j = l - 1; j >= i; j--) { 1.679 + // Testing for good suffix 1.680 + if (pattern[j] == pattern[j - i]) { 1.681 + // pattern[j..len] is a good suffix 1.682 + gss[j - 1] = i; 1.683 + } else { 1.684 + // No match. The array has already been 1.685 + // filled up with correct values before. 1.686 + continue NEXT; 1.687 + } 1.688 + } 1.689 + while (j > 0) { 1.690 + gss[--j] = i; 1.691 + } 1.692 + } 1.693 + gss[l - 1] = 1; 1.694 + } 1.695 + 1.696 + 1.697 + /** 1.698 + * Iterates through all the parts and outputs each Mime part 1.699 + * separated by a boundary. 1.700 + */ 1.701 + byte[] buf = new byte[1024]; 1.702 + 1.703 + public void writeTo(OutputStream os) 1.704 + throws IOException, MessagingException { 1.705 + 1.706 + // inputStream was not null 1.707 + if (in != null) { 1.708 + contentType.setParameter("boundary", this.boundary); 1.709 + } 1.710 + 1.711 + String bnd = "--" + contentType.getParameter("boundary"); 1.712 + for (int i = 0; i < parts.size(); i++) { 1.713 + OutputUtil.writeln(bnd, os); // put out boundary 1.714 + ((MimeBodyPart)parts.get(i)).writeTo(os); 1.715 + OutputUtil.writeln(os); // put out empty line 1.716 + } 1.717 + 1.718 + if (in != null) { 1.719 + OutputUtil.writeln(bnd, os); // put out boundary 1.720 + if ((os instanceof ByteOutputStream) && lazyAttachments) { 1.721 + ((ByteOutputStream)os).write(in); 1.722 + } else { 1.723 + ByteOutputStream baos = new ByteOutputStream(in.available()); 1.724 + baos.write(in); 1.725 + baos.writeTo(os); 1.726 + // reset the inputstream so that we can support a 1.727 + //getAttachment later 1.728 + in = baos.newInputStream(); 1.729 + } 1.730 + 1.731 + // this will endup writing the end boundary 1.732 + } else { 1.733 + // put out last boundary 1.734 + OutputUtil.writeAsAscii(bnd, os); 1.735 + OutputUtil.writeAsAscii("--", os); 1.736 + } 1.737 + } 1.738 + 1.739 + public void setInputStream(InputStream is) { 1.740 + this.in = is; 1.741 + } 1.742 + 1.743 + public InputStream getInputStream() { 1.744 + return this.in; 1.745 + } 1.746 + 1.747 + public void setBoundary(String bnd) { 1.748 + this.boundary = bnd; 1.749 + if (this.contentType != null) { 1.750 + this.contentType.setParameter("boundary", bnd); 1.751 + } 1.752 + } 1.753 + public String getBoundary() { 1.754 + return this.boundary; 1.755 + } 1.756 + 1.757 + public boolean isEndOfStream() { 1.758 + return (b == -1); 1.759 + } 1.760 + 1.761 + public void setLazyAttachments(boolean flag) { 1.762 + lazyAttachments = flag; 1.763 + } 1.764 + 1.765 +}