Fri, 04 Oct 2013 16:21:34 +0100
8025054: Update JAX-WS RI integration to 2.2.9-b130926.1035
Reviewed-by: chegar
ohair@286 | 1 | /* |
alanb@368 | 2 | * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. |
ohair@286 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
ohair@286 | 4 | * |
ohair@286 | 5 | * This code is free software; you can redistribute it and/or modify it |
ohair@286 | 6 | * under the terms of the GNU General Public License version 2 only, as |
ohair@286 | 7 | * published by the Free Software Foundation. Oracle designates this |
ohair@286 | 8 | * particular file as subject to the "Classpath" exception as provided |
ohair@286 | 9 | * by Oracle in the LICENSE file that accompanied this code. |
ohair@286 | 10 | * |
ohair@286 | 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
ohair@286 | 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
ohair@286 | 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
ohair@286 | 14 | * version 2 for more details (a copy is included in the LICENSE file that |
ohair@286 | 15 | * accompanied this code). |
ohair@286 | 16 | * |
ohair@286 | 17 | * You should have received a copy of the GNU General Public License version |
ohair@286 | 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
ohair@286 | 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
ohair@286 | 20 | * |
ohair@286 | 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
ohair@286 | 22 | * or visit www.oracle.com if you need additional information or have any |
ohair@286 | 23 | * questions. |
ohair@286 | 24 | */ |
ohair@286 | 25 | |
ohair@286 | 26 | package com.sun.xml.internal.org.jvnet.mimepull; |
ohair@286 | 27 | |
ohair@286 | 28 | import java.io.InputStream; |
ohair@286 | 29 | import java.io.IOException; |
ohair@286 | 30 | import java.util.*; |
ohair@286 | 31 | import java.util.logging.Logger; |
ohair@286 | 32 | import java.nio.ByteBuffer; |
alanb@368 | 33 | import java.util.logging.Level; |
ohair@286 | 34 | |
ohair@286 | 35 | /** |
ohair@286 | 36 | * Pull parser for the MIME messages. Applications can use pull API to continue |
ohair@286 | 37 | * the parsing MIME messages lazily. |
ohair@286 | 38 | * |
ohair@286 | 39 | * <pre> |
ohair@286 | 40 | * for e.g.: |
ohair@286 | 41 | * <p> |
ohair@286 | 42 | * |
ohair@286 | 43 | * MIMEParser parser = ... |
ohair@286 | 44 | * Iterator<MIMEEvent> it = parser.iterator(); |
ohair@286 | 45 | * while(it.hasNext()) { |
ohair@286 | 46 | * MIMEEvent event = it.next(); |
ohair@286 | 47 | * ... |
ohair@286 | 48 | * } |
ohair@286 | 49 | * </pre> |
ohair@286 | 50 | * |
ohair@286 | 51 | * @author Jitendra Kotamraju |
ohair@286 | 52 | */ |
ohair@286 | 53 | class MIMEParser implements Iterable<MIMEEvent> { |
ohair@286 | 54 | |
ohair@286 | 55 | private static final Logger LOGGER = Logger.getLogger(MIMEParser.class.getName()); |
ohair@286 | 56 | |
alanb@368 | 57 | private static final String HEADER_ENCODING = "ISO8859-1"; |
alanb@368 | 58 | |
ohair@286 | 59 | // Actually, the grammar doesn't support whitespace characters |
ohair@286 | 60 | // after boundary. But the mail implementation checks for it. |
ohair@286 | 61 | // We will only check for these many whitespace characters after boundary |
ohair@286 | 62 | private static final int NO_LWSP = 1000; |
ohair@286 | 63 | private enum STATE {START_MESSAGE, SKIP_PREAMBLE, START_PART, HEADERS, BODY, END_PART, END_MESSAGE} |
ohair@286 | 64 | private STATE state = STATE.START_MESSAGE; |
ohair@286 | 65 | |
ohair@286 | 66 | private final InputStream in; |
ohair@286 | 67 | private final byte[] bndbytes; |
ohair@286 | 68 | private final int bl; |
ohair@286 | 69 | private final MIMEConfig config; |
ohair@286 | 70 | private final int[] bcs = new int[128]; // BnM algo: Bad Character Shift table |
ohair@286 | 71 | private final int[] gss; // BnM algo : Good Suffix Shift table |
ohair@286 | 72 | |
ohair@286 | 73 | /** |
ohair@286 | 74 | * Have we parsed the data from our InputStream yet? |
ohair@286 | 75 | */ |
ohair@286 | 76 | private boolean parsed; |
ohair@286 | 77 | |
ohair@286 | 78 | /* |
ohair@286 | 79 | * Read and process body partsList until we see the |
ohair@286 | 80 | * terminating boundary line (or EOF). |
ohair@286 | 81 | */ |
ohair@286 | 82 | private boolean done = false; |
ohair@286 | 83 | |
ohair@286 | 84 | private boolean eof; |
ohair@286 | 85 | private final int capacity; |
ohair@286 | 86 | private byte[] buf; |
ohair@286 | 87 | private int len; |
ohair@286 | 88 | private boolean bol; // beginning of the line |
ohair@286 | 89 | |
ohair@286 | 90 | /* |
ohair@286 | 91 | * Parses the MIME content. At the EOF, it also closes input stream |
ohair@286 | 92 | */ |
ohair@286 | 93 | MIMEParser(InputStream in, String boundary, MIMEConfig config) { |
ohair@286 | 94 | this.in = in; |
ohair@286 | 95 | this.bndbytes = getBytes("--"+boundary); |
ohair@286 | 96 | bl = bndbytes.length; |
ohair@286 | 97 | this.config = config; |
ohair@286 | 98 | gss = new int[bl]; |
ohair@286 | 99 | compileBoundaryPattern(); |
ohair@286 | 100 | |
ohair@286 | 101 | // \r\n + boundary + "--\r\n" + lots of LWSP |
ohair@286 | 102 | capacity = config.chunkSize+2+bl+4+NO_LWSP; |
ohair@286 | 103 | createBuf(capacity); |
ohair@286 | 104 | } |
ohair@286 | 105 | |
ohair@286 | 106 | /** |
ohair@286 | 107 | * Returns iterator for the parsing events. Use the iterator to advance |
ohair@286 | 108 | * the parsing. |
ohair@286 | 109 | * |
ohair@286 | 110 | * @return iterator for parsing events |
ohair@286 | 111 | */ |
alanb@368 | 112 | @Override |
ohair@286 | 113 | public Iterator<MIMEEvent> iterator() { |
ohair@286 | 114 | return new MIMEEventIterator(); |
ohair@286 | 115 | } |
ohair@286 | 116 | |
ohair@286 | 117 | class MIMEEventIterator implements Iterator<MIMEEvent> { |
ohair@286 | 118 | |
alanb@368 | 119 | @Override |
ohair@286 | 120 | public boolean hasNext() { |
ohair@286 | 121 | return !parsed; |
ohair@286 | 122 | } |
ohair@286 | 123 | |
alanb@368 | 124 | @Override |
ohair@286 | 125 | public MIMEEvent next() { |
ohair@286 | 126 | switch(state) { |
ohair@286 | 127 | case START_MESSAGE : |
alanb@368 | 128 | if (LOGGER.isLoggable(Level.FINER)) {LOGGER.log(Level.FINER, "MIMEParser state={0}", STATE.START_MESSAGE);} |
ohair@286 | 129 | state = STATE.SKIP_PREAMBLE; |
ohair@286 | 130 | return MIMEEvent.START_MESSAGE; |
ohair@286 | 131 | |
ohair@286 | 132 | case SKIP_PREAMBLE : |
alanb@368 | 133 | if (LOGGER.isLoggable(Level.FINER)) {LOGGER.log(Level.FINER, "MIMEParser state={0}", STATE.SKIP_PREAMBLE);} |
ohair@286 | 134 | skipPreamble(); |
ohair@286 | 135 | // fall through |
ohair@286 | 136 | case START_PART : |
alanb@368 | 137 | if (LOGGER.isLoggable(Level.FINER)) {LOGGER.log(Level.FINER, "MIMEParser state={0}", STATE.START_PART);} |
ohair@286 | 138 | state = STATE.HEADERS; |
ohair@286 | 139 | return MIMEEvent.START_PART; |
ohair@286 | 140 | |
ohair@286 | 141 | case HEADERS : |
alanb@368 | 142 | if (LOGGER.isLoggable(Level.FINER)) {LOGGER.log(Level.FINER, "MIMEParser state={0}", STATE.HEADERS);} |
ohair@286 | 143 | InternetHeaders ih = readHeaders(); |
ohair@286 | 144 | state = STATE.BODY; |
ohair@286 | 145 | bol = true; |
ohair@286 | 146 | return new MIMEEvent.Headers(ih); |
ohair@286 | 147 | |
ohair@286 | 148 | case BODY : |
alanb@368 | 149 | if (LOGGER.isLoggable(Level.FINER)) {LOGGER.log(Level.FINER, "MIMEParser state={0}", STATE.BODY);} |
ohair@286 | 150 | ByteBuffer buf = readBody(); |
ohair@286 | 151 | bol = false; |
ohair@286 | 152 | return new MIMEEvent.Content(buf); |
ohair@286 | 153 | |
ohair@286 | 154 | case END_PART : |
alanb@368 | 155 | if (LOGGER.isLoggable(Level.FINER)) {LOGGER.log(Level.FINER, "MIMEParser state={0}", STATE.END_PART);} |
ohair@286 | 156 | if (done) { |
ohair@286 | 157 | state = STATE.END_MESSAGE; |
ohair@286 | 158 | } else { |
ohair@286 | 159 | state = STATE.START_PART; |
ohair@286 | 160 | } |
ohair@286 | 161 | return MIMEEvent.END_PART; |
ohair@286 | 162 | |
ohair@286 | 163 | case END_MESSAGE : |
alanb@368 | 164 | if (LOGGER.isLoggable(Level.FINER)) {LOGGER.log(Level.FINER, "MIMEParser state={0}", STATE.END_MESSAGE);} |
ohair@286 | 165 | parsed = true; |
ohair@286 | 166 | return MIMEEvent.END_MESSAGE; |
ohair@286 | 167 | |
ohair@286 | 168 | default : |
ohair@286 | 169 | throw new MIMEParsingException("Unknown Parser state = "+state); |
ohair@286 | 170 | } |
ohair@286 | 171 | } |
ohair@286 | 172 | |
alanb@368 | 173 | @Override |
ohair@286 | 174 | public void remove() { |
ohair@286 | 175 | throw new UnsupportedOperationException(); |
ohair@286 | 176 | } |
ohair@286 | 177 | } |
ohair@286 | 178 | |
ohair@286 | 179 | /** |
ohair@286 | 180 | * Collects the headers for the current part by parsing mesage stream. |
ohair@286 | 181 | * |
ohair@286 | 182 | * @return headers for the current part |
ohair@286 | 183 | */ |
ohair@286 | 184 | private InternetHeaders readHeaders() { |
ohair@286 | 185 | if (!eof) { |
ohair@286 | 186 | fillBuf(); |
ohair@286 | 187 | } |
ohair@286 | 188 | return new InternetHeaders(new LineInputStream()); |
ohair@286 | 189 | } |
ohair@286 | 190 | |
ohair@286 | 191 | /** |
ohair@286 | 192 | * Reads and saves the part of the current attachment part's content. |
ohair@286 | 193 | * At the end of this method, buf should have the remaining data |
ohair@286 | 194 | * at index 0. |
ohair@286 | 195 | * |
ohair@286 | 196 | * @return a chunk of the part's content |
ohair@286 | 197 | * |
ohair@286 | 198 | */ |
ohair@286 | 199 | private ByteBuffer readBody() { |
ohair@286 | 200 | if (!eof) { |
ohair@286 | 201 | fillBuf(); |
ohair@286 | 202 | } |
ohair@286 | 203 | int start = match(buf, 0, len); // matches boundary |
ohair@286 | 204 | if (start == -1) { |
ohair@286 | 205 | // No boundary is found |
ohair@286 | 206 | assert eof || len >= config.chunkSize; |
ohair@286 | 207 | int chunkSize = eof ? len : config.chunkSize; |
ohair@286 | 208 | if (eof) { |
ohair@286 | 209 | done = true; |
ohair@286 | 210 | throw new MIMEParsingException("Reached EOF, but there is no closing MIME boundary."); |
ohair@286 | 211 | } |
ohair@286 | 212 | return adjustBuf(chunkSize, len-chunkSize); |
ohair@286 | 213 | } |
ohair@286 | 214 | // Found boundary. |
ohair@286 | 215 | // Is it at the start of a line ? |
ohair@286 | 216 | int chunkLen = start; |
ohair@286 | 217 | if (bol && start == 0) { |
ohair@286 | 218 | // nothing to do |
ohair@286 | 219 | } else if (start > 0 && (buf[start-1] == '\n' || buf[start-1] =='\r')) { |
ohair@286 | 220 | --chunkLen; |
ohair@286 | 221 | if (buf[start-1] == '\n' && start >1 && buf[start-2] == '\r') { |
ohair@286 | 222 | --chunkLen; |
ohair@286 | 223 | } |
ohair@286 | 224 | } else { |
ohair@286 | 225 | return adjustBuf(start+1, len-start-1); // boundary is not at beginning of a line |
ohair@286 | 226 | } |
ohair@286 | 227 | |
ohair@286 | 228 | if (start+bl+1 < len && buf[start+bl] == '-' && buf[start+bl+1] == '-') { |
ohair@286 | 229 | state = STATE.END_PART; |
ohair@286 | 230 | done = true; |
ohair@286 | 231 | return adjustBuf(chunkLen, 0); |
ohair@286 | 232 | } |
ohair@286 | 233 | |
ohair@286 | 234 | // Consider all the whitespace in boundary+whitespace+"\r\n" |
ohair@286 | 235 | int lwsp = 0; |
ohair@286 | 236 | for(int i=start+bl; i < len && (buf[i] == ' ' || buf[i] == '\t'); i++) { |
ohair@286 | 237 | ++lwsp; |
ohair@286 | 238 | } |
ohair@286 | 239 | |
ohair@286 | 240 | // Check for \n or \r\n in boundary+whitespace+"\n" or boundary+whitespace+"\r\n" |
ohair@286 | 241 | if (start+bl+lwsp < len && buf[start+bl+lwsp] == '\n') { |
ohair@286 | 242 | state = STATE.END_PART; |
ohair@286 | 243 | return adjustBuf(chunkLen, len-start-bl-lwsp-1); |
ohair@286 | 244 | } else if (start+bl+lwsp+1 < len && buf[start+bl+lwsp] == '\r' && buf[start+bl+lwsp+1] == '\n') { |
ohair@286 | 245 | state = STATE.END_PART; |
ohair@286 | 246 | return adjustBuf(chunkLen, len-start-bl-lwsp-2); |
ohair@286 | 247 | } else if (start+bl+lwsp+1 < len) { |
ohair@286 | 248 | return adjustBuf(chunkLen+1, len-chunkLen-1); // boundary string in a part data |
ohair@286 | 249 | } else if (eof) { |
ohair@286 | 250 | done = true; |
ohair@286 | 251 | throw new MIMEParsingException("Reached EOF, but there is no closing MIME boundary."); |
ohair@286 | 252 | } |
ohair@286 | 253 | |
ohair@286 | 254 | // Some more data needed to determine if it is indeed a proper boundary |
ohair@286 | 255 | return adjustBuf(chunkLen, len-chunkLen); |
ohair@286 | 256 | } |
ohair@286 | 257 | |
ohair@286 | 258 | /** |
ohair@286 | 259 | * Returns a chunk from the original buffer. A new buffer is |
ohair@286 | 260 | * created with the remaining bytes. |
ohair@286 | 261 | * |
ohair@286 | 262 | * @param chunkSize create a chunk with these many bytes |
ohair@286 | 263 | * @param remaining bytes from the end of the buffer that need to be copied to |
ohair@286 | 264 | * the beginning of the new buffer |
ohair@286 | 265 | * @return chunk |
ohair@286 | 266 | */ |
ohair@286 | 267 | private ByteBuffer adjustBuf(int chunkSize, int remaining) { |
ohair@286 | 268 | assert buf != null; |
ohair@286 | 269 | assert chunkSize >= 0; |
ohair@286 | 270 | assert remaining >= 0; |
ohair@286 | 271 | |
ohair@286 | 272 | byte[] temp = buf; |
ohair@286 | 273 | // create a new buf and adjust it without this chunk |
ohair@286 | 274 | createBuf(remaining); |
ohair@286 | 275 | System.arraycopy(temp, len-remaining, buf, 0, remaining); |
ohair@286 | 276 | len = remaining; |
ohair@286 | 277 | |
ohair@286 | 278 | return ByteBuffer.wrap(temp, 0, chunkSize); |
ohair@286 | 279 | } |
ohair@286 | 280 | |
ohair@286 | 281 | private void createBuf(int min) { |
ohair@286 | 282 | buf = new byte[min < capacity ? capacity : min]; |
ohair@286 | 283 | } |
ohair@286 | 284 | |
ohair@286 | 285 | /** |
ohair@286 | 286 | * Skips the preamble to find the first attachment part |
ohair@286 | 287 | */ |
ohair@286 | 288 | private void skipPreamble() { |
ohair@286 | 289 | |
ohair@286 | 290 | while(true) { |
ohair@286 | 291 | if (!eof) { |
ohair@286 | 292 | fillBuf(); |
ohair@286 | 293 | } |
ohair@286 | 294 | int start = match(buf, 0, len); // matches boundary |
ohair@286 | 295 | if (start == -1) { |
ohair@286 | 296 | // No boundary is found |
ohair@286 | 297 | if (eof) { |
ohair@286 | 298 | throw new MIMEParsingException("Missing start boundary"); |
ohair@286 | 299 | } else { |
ohair@286 | 300 | adjustBuf(len-bl+1, bl-1); |
ohair@286 | 301 | continue; |
ohair@286 | 302 | } |
ohair@286 | 303 | } |
ohair@286 | 304 | |
ohair@286 | 305 | if (start > config.chunkSize) { |
ohair@286 | 306 | adjustBuf(start, len-start); |
ohair@286 | 307 | continue; |
ohair@286 | 308 | } |
ohair@286 | 309 | // Consider all the whitespace boundary+whitespace+"\r\n" |
ohair@286 | 310 | int lwsp = 0; |
ohair@286 | 311 | for(int i=start+bl; i < len && (buf[i] == ' ' || buf[i] == '\t'); i++) { |
ohair@286 | 312 | ++lwsp; |
ohair@286 | 313 | } |
ohair@286 | 314 | // Check for \n or \r\n |
ohair@286 | 315 | if (start+bl+lwsp < len && (buf[start+bl+lwsp] == '\n' || buf[start+bl+lwsp] == '\r') ) { |
ohair@286 | 316 | if (buf[start+bl+lwsp] == '\n') { |
ohair@286 | 317 | adjustBuf(start+bl+lwsp+1, len-start-bl-lwsp-1); |
ohair@286 | 318 | break; |
ohair@286 | 319 | } else if (start+bl+lwsp+1 < len && buf[start+bl+lwsp+1] == '\n') { |
ohair@286 | 320 | adjustBuf(start+bl+lwsp+2, len-start-bl-lwsp-2); |
ohair@286 | 321 | break; |
ohair@286 | 322 | } |
ohair@286 | 323 | } |
ohair@286 | 324 | adjustBuf(start+1, len-start-1); |
ohair@286 | 325 | } |
alanb@368 | 326 | if (LOGGER.isLoggable(Level.FINE)) {LOGGER.log(Level.FINE, "Skipped the preamble. buffer len={0}", len);} |
ohair@286 | 327 | } |
ohair@286 | 328 | |
ohair@286 | 329 | private static byte[] getBytes(String s) { |
ohair@286 | 330 | char [] chars= s.toCharArray(); |
ohair@286 | 331 | int size = chars.length; |
ohair@286 | 332 | byte[] bytes = new byte[size]; |
ohair@286 | 333 | |
alanb@368 | 334 | for (int i = 0; i < size;) { |
ohair@286 | 335 | bytes[i] = (byte) chars[i++]; |
alanb@368 | 336 | } |
ohair@286 | 337 | return bytes; |
ohair@286 | 338 | } |
ohair@286 | 339 | |
ohair@286 | 340 | /** |
ohair@286 | 341 | * Boyer-Moore search method. Copied from java.util.regex.Pattern.java |
ohair@286 | 342 | * |
ohair@286 | 343 | * Pre calculates arrays needed to generate the bad character |
ohair@286 | 344 | * shift and the good suffix shift. Only the last seven bits |
ohair@286 | 345 | * are used to see if chars match; This keeps the tables small |
ohair@286 | 346 | * and covers the heavily used ASCII range, but occasionally |
ohair@286 | 347 | * results in an aliased match for the bad character shift. |
ohair@286 | 348 | */ |
ohair@286 | 349 | private void compileBoundaryPattern() { |
ohair@286 | 350 | int i, j; |
ohair@286 | 351 | |
ohair@286 | 352 | // Precalculate part of the bad character shift |
ohair@286 | 353 | // It is a table for where in the pattern each |
ohair@286 | 354 | // lower 7-bit value occurs |
ohair@286 | 355 | for (i = 0; i < bndbytes.length; i++) { |
ohair@286 | 356 | bcs[bndbytes[i]&0x7F] = i + 1; |
ohair@286 | 357 | } |
ohair@286 | 358 | |
ohair@286 | 359 | // Precalculate the good suffix shift |
ohair@286 | 360 | // i is the shift amount being considered |
ohair@286 | 361 | NEXT: for (i = bndbytes.length; i > 0; i--) { |
ohair@286 | 362 | // j is the beginning index of suffix being considered |
ohair@286 | 363 | for (j = bndbytes.length - 1; j >= i; j--) { |
ohair@286 | 364 | // Testing for good suffix |
ohair@286 | 365 | if (bndbytes[j] == bndbytes[j-i]) { |
ohair@286 | 366 | // src[j..len] is a good suffix |
ohair@286 | 367 | gss[j-1] = i; |
ohair@286 | 368 | } else { |
ohair@286 | 369 | // No match. The array has already been |
ohair@286 | 370 | // filled up with correct values before. |
ohair@286 | 371 | continue NEXT; |
ohair@286 | 372 | } |
ohair@286 | 373 | } |
ohair@286 | 374 | // This fills up the remaining of optoSft |
ohair@286 | 375 | // any suffix can not have larger shift amount |
ohair@286 | 376 | // then its sub-suffix. Why??? |
ohair@286 | 377 | while (j > 0) { |
ohair@286 | 378 | gss[--j] = i; |
ohair@286 | 379 | } |
ohair@286 | 380 | } |
ohair@286 | 381 | // Set the guard value because of unicode compression |
ohair@286 | 382 | gss[bndbytes.length -1] = 1; |
ohair@286 | 383 | } |
ohair@286 | 384 | |
ohair@286 | 385 | /** |
ohair@286 | 386 | * Finds the boundary in the given buffer using Boyer-Moore algo. |
ohair@286 | 387 | * Copied from java.util.regex.Pattern.java |
ohair@286 | 388 | * |
ohair@286 | 389 | * @param mybuf boundary to be searched in this mybuf |
ohair@286 | 390 | * @param off start index in mybuf |
ohair@286 | 391 | * @param len number of bytes in mybuf |
ohair@286 | 392 | * |
ohair@286 | 393 | * @return -1 if there is no match or index where the match starts |
ohair@286 | 394 | */ |
ohair@286 | 395 | private int match(byte[] mybuf, int off, int len) { |
ohair@286 | 396 | int last = len - bndbytes.length; |
ohair@286 | 397 | |
ohair@286 | 398 | // Loop over all possible match positions in text |
ohair@286 | 399 | NEXT: while (off <= last) { |
ohair@286 | 400 | // Loop over pattern from right to left |
ohair@286 | 401 | for (int j = bndbytes.length - 1; j >= 0; j--) { |
ohair@286 | 402 | byte ch = mybuf[off+j]; |
ohair@286 | 403 | if (ch != bndbytes[j]) { |
ohair@286 | 404 | // Shift search to the right by the maximum of the |
ohair@286 | 405 | // bad character shift and the good suffix shift |
ohair@286 | 406 | off += Math.max(j + 1 - bcs[ch&0x7F], gss[j]); |
ohair@286 | 407 | continue NEXT; |
ohair@286 | 408 | } |
ohair@286 | 409 | } |
ohair@286 | 410 | // Entire pattern matched starting at off |
ohair@286 | 411 | return off; |
ohair@286 | 412 | } |
ohair@286 | 413 | return -1; |
ohair@286 | 414 | } |
ohair@286 | 415 | |
ohair@286 | 416 | /** |
ohair@286 | 417 | * Fills the remaining buf to the full capacity |
ohair@286 | 418 | */ |
ohair@286 | 419 | private void fillBuf() { |
alanb@368 | 420 | if (LOGGER.isLoggable(Level.FINER)) {LOGGER.log(Level.FINER, "Before fillBuf() buffer len={0}", len);} |
ohair@286 | 421 | assert !eof; |
ohair@286 | 422 | while(len < buf.length) { |
ohair@286 | 423 | int read; |
ohair@286 | 424 | try { |
ohair@286 | 425 | read = in.read(buf, len, buf.length-len); |
ohair@286 | 426 | } catch(IOException ioe) { |
ohair@286 | 427 | throw new MIMEParsingException(ioe); |
ohair@286 | 428 | } |
ohair@286 | 429 | if (read == -1) { |
ohair@286 | 430 | eof = true; |
ohair@286 | 431 | try { |
alanb@368 | 432 | if (LOGGER.isLoggable(Level.FINE)) {LOGGER.fine("Closing the input stream.");} |
ohair@286 | 433 | in.close(); |
ohair@286 | 434 | } catch(IOException ioe) { |
ohair@286 | 435 | throw new MIMEParsingException(ioe); |
ohair@286 | 436 | } |
ohair@286 | 437 | break; |
ohair@286 | 438 | } else { |
ohair@286 | 439 | len += read; |
ohair@286 | 440 | } |
ohair@286 | 441 | } |
alanb@368 | 442 | if (LOGGER.isLoggable(Level.FINER)) {LOGGER.log(Level.FINER, "After fillBuf() buffer len={0}", len);} |
ohair@286 | 443 | } |
ohair@286 | 444 | |
ohair@286 | 445 | private void doubleBuf() { |
ohair@286 | 446 | byte[] temp = new byte[2*len]; |
ohair@286 | 447 | System.arraycopy(buf, 0, temp, 0, len); |
ohair@286 | 448 | buf = temp; |
ohair@286 | 449 | if (!eof) { |
ohair@286 | 450 | fillBuf(); |
ohair@286 | 451 | } |
ohair@286 | 452 | } |
ohair@286 | 453 | |
ohair@286 | 454 | class LineInputStream { |
ohair@286 | 455 | private int offset; |
ohair@286 | 456 | |
ohair@286 | 457 | /* |
ohair@286 | 458 | * Read a line containing only ASCII characters from the input |
ohair@286 | 459 | * stream. A line is terminated by a CR or NL or CR-NL sequence. |
ohair@286 | 460 | * A common error is a CR-CR-NL sequence, which will also terminate |
ohair@286 | 461 | * a line. |
ohair@286 | 462 | * The line terminator is not returned as part of the returned |
ohair@286 | 463 | * String. Returns null if no data is available. <p> |
ohair@286 | 464 | * |
ohair@286 | 465 | * This class is similar to the deprecated |
ohair@286 | 466 | * <code>DataInputStream.readLine()</code> |
ohair@286 | 467 | */ |
ohair@286 | 468 | public String readLine() throws IOException { |
ohair@286 | 469 | |
ohair@286 | 470 | int hdrLen = 0; |
ohair@286 | 471 | int lwsp = 0; |
ohair@286 | 472 | while(offset+hdrLen < len) { |
ohair@286 | 473 | if (buf[offset+hdrLen] == '\n') { |
ohair@286 | 474 | lwsp = 1; |
ohair@286 | 475 | break; |
ohair@286 | 476 | } |
ohair@286 | 477 | if (offset+hdrLen+1 == len) { |
ohair@286 | 478 | doubleBuf(); |
ohair@286 | 479 | } |
ohair@286 | 480 | if (offset+hdrLen+1 >= len) { // No more data in the stream |
ohair@286 | 481 | assert eof; |
ohair@286 | 482 | return null; |
ohair@286 | 483 | } |
ohair@286 | 484 | if (buf[offset+hdrLen] == '\r' && buf[offset+hdrLen+1] == '\n') { |
ohair@286 | 485 | lwsp = 2; |
ohair@286 | 486 | break; |
ohair@286 | 487 | } |
ohair@286 | 488 | ++hdrLen; |
ohair@286 | 489 | } |
ohair@286 | 490 | if (hdrLen == 0) { |
ohair@286 | 491 | adjustBuf(offset+lwsp, len-offset-lwsp); |
ohair@286 | 492 | return null; |
ohair@286 | 493 | } |
ohair@286 | 494 | |
alanb@368 | 495 | String hdr = new String(buf, offset, hdrLen, HEADER_ENCODING); |
ohair@286 | 496 | offset += hdrLen+lwsp; |
ohair@286 | 497 | return hdr; |
ohair@286 | 498 | } |
ohair@286 | 499 | |
ohair@286 | 500 | } |
ohair@286 | 501 | |
ohair@286 | 502 | } |