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