Sat, 07 Nov 2020 10:30:02 +0800
Added tag mips-jdk8u275-b01 for changeset fdbe50121f48
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 | /* FROM mail.jar */ |
aoqi@0 | 27 | package com.sun.xml.internal.org.jvnet.mimepull; |
aoqi@0 | 28 | |
aoqi@0 | 29 | import java.io.*; |
aoqi@0 | 30 | |
aoqi@0 | 31 | /** |
aoqi@0 | 32 | * This class implements a UUDecoder. It is implemented as |
aoqi@0 | 33 | * a FilterInputStream, so one can just wrap this class around |
aoqi@0 | 34 | * any input stream and read bytes from this filter. The decoding |
aoqi@0 | 35 | * is done as the bytes are read out. |
aoqi@0 | 36 | * |
aoqi@0 | 37 | * @author John Mani |
aoqi@0 | 38 | * @author Bill Shannon |
aoqi@0 | 39 | */ |
aoqi@0 | 40 | |
aoqi@0 | 41 | final class UUDecoderStream extends FilterInputStream { |
aoqi@0 | 42 | private String name; |
aoqi@0 | 43 | private int mode; |
aoqi@0 | 44 | |
aoqi@0 | 45 | private byte[] buffer = new byte[45]; // max decoded chars in a line = 45 |
aoqi@0 | 46 | private int bufsize = 0; // size of the cache |
aoqi@0 | 47 | private int index = 0; // index into the cache |
aoqi@0 | 48 | private boolean gotPrefix = false; |
aoqi@0 | 49 | private boolean gotEnd = false; |
aoqi@0 | 50 | private LineInputStream lin; |
aoqi@0 | 51 | private boolean ignoreErrors; |
aoqi@0 | 52 | private boolean ignoreMissingBeginEnd; |
aoqi@0 | 53 | private String readAhead; |
aoqi@0 | 54 | |
aoqi@0 | 55 | /** |
aoqi@0 | 56 | * Create a UUdecoder that decodes the specified input stream. |
aoqi@0 | 57 | * The System property <code>mail.mime.uudecode.ignoreerrors</code> |
aoqi@0 | 58 | * controls whether errors in the encoded data cause an exception |
aoqi@0 | 59 | * or are ignored. The default is false (errors cause exception). |
aoqi@0 | 60 | * The System property <code>mail.mime.uudecode.ignoremissingbeginend</code> |
aoqi@0 | 61 | * controls whether a missing begin or end line cause an exception |
aoqi@0 | 62 | * or are ignored. The default is false (errors cause exception). |
aoqi@0 | 63 | * @param in the input stream |
aoqi@0 | 64 | */ |
aoqi@0 | 65 | public UUDecoderStream(InputStream in) { |
aoqi@0 | 66 | super(in); |
aoqi@0 | 67 | lin = new LineInputStream(in); |
aoqi@0 | 68 | // default to false |
aoqi@0 | 69 | ignoreErrors = PropUtil.getBooleanSystemProperty( |
aoqi@0 | 70 | "mail.mime.uudecode.ignoreerrors", false); |
aoqi@0 | 71 | // default to false |
aoqi@0 | 72 | ignoreMissingBeginEnd = PropUtil.getBooleanSystemProperty( |
aoqi@0 | 73 | "mail.mime.uudecode.ignoremissingbeginend", false); |
aoqi@0 | 74 | } |
aoqi@0 | 75 | |
aoqi@0 | 76 | /** |
aoqi@0 | 77 | * Create a UUdecoder that decodes the specified input stream. |
aoqi@0 | 78 | * @param in the input stream |
aoqi@0 | 79 | * @param ignoreErrors ignore errors? |
aoqi@0 | 80 | * @param ignoreMissingBeginEnd ignore missing begin or end? |
aoqi@0 | 81 | */ |
aoqi@0 | 82 | public UUDecoderStream(InputStream in, boolean ignoreErrors, |
aoqi@0 | 83 | boolean ignoreMissingBeginEnd) { |
aoqi@0 | 84 | super(in); |
aoqi@0 | 85 | lin = new LineInputStream(in); |
aoqi@0 | 86 | this.ignoreErrors = ignoreErrors; |
aoqi@0 | 87 | this.ignoreMissingBeginEnd = ignoreMissingBeginEnd; |
aoqi@0 | 88 | } |
aoqi@0 | 89 | |
aoqi@0 | 90 | /** |
aoqi@0 | 91 | * Read the next decoded byte from this input stream. The byte |
aoqi@0 | 92 | * is returned as an <code>int</code> in the range <code>0</code> |
aoqi@0 | 93 | * to <code>255</code>. If no byte is available because the end of |
aoqi@0 | 94 | * the stream has been reached, the value <code>-1</code> is returned. |
aoqi@0 | 95 | * This method blocks until input data is available, the end of the |
aoqi@0 | 96 | * stream is detected, or an exception is thrown. |
aoqi@0 | 97 | * |
aoqi@0 | 98 | * @return next byte of data, or <code>-1</code> if the end of |
aoqi@0 | 99 | * stream is reached. |
aoqi@0 | 100 | * @exception IOException if an I/O error occurs. |
aoqi@0 | 101 | * @see java.io.FilterInputStream#in |
aoqi@0 | 102 | */ |
aoqi@0 | 103 | @Override |
aoqi@0 | 104 | public int read() throws IOException { |
aoqi@0 | 105 | if (index >= bufsize) { |
aoqi@0 | 106 | readPrefix(); |
aoqi@0 | 107 | if (!decode()) { |
aoqi@0 | 108 | return -1; |
aoqi@0 | 109 | } |
aoqi@0 | 110 | index = 0; // reset index into buffer |
aoqi@0 | 111 | } |
aoqi@0 | 112 | return buffer[index++] & 0xff; // return lower byte |
aoqi@0 | 113 | } |
aoqi@0 | 114 | |
aoqi@0 | 115 | @Override |
aoqi@0 | 116 | public int read(byte[] buf, int off, int len) throws IOException { |
aoqi@0 | 117 | int i, c; |
aoqi@0 | 118 | for (i = 0; i < len; i++) { |
aoqi@0 | 119 | if ((c = read()) == -1) { |
aoqi@0 | 120 | if (i == 0) {// At end of stream, so we should |
aoqi@0 | 121 | i = -1; // return -1, NOT 0. |
aoqi@0 | 122 | } |
aoqi@0 | 123 | break; |
aoqi@0 | 124 | } |
aoqi@0 | 125 | buf[off+i] = (byte)c; |
aoqi@0 | 126 | } |
aoqi@0 | 127 | return i; |
aoqi@0 | 128 | } |
aoqi@0 | 129 | |
aoqi@0 | 130 | @Override |
aoqi@0 | 131 | public boolean markSupported() { |
aoqi@0 | 132 | return false; |
aoqi@0 | 133 | } |
aoqi@0 | 134 | |
aoqi@0 | 135 | @Override |
aoqi@0 | 136 | public int available() throws IOException { |
aoqi@0 | 137 | // This is only an estimate, since in.available() |
aoqi@0 | 138 | // might include CRLFs too .. |
aoqi@0 | 139 | return ((in.available() * 3)/4 + (bufsize-index)); |
aoqi@0 | 140 | } |
aoqi@0 | 141 | |
aoqi@0 | 142 | /** |
aoqi@0 | 143 | * Get the "name" field from the prefix. This is meant to |
aoqi@0 | 144 | * be the pathname of the decoded file |
aoqi@0 | 145 | * |
aoqi@0 | 146 | * @return name of decoded file |
aoqi@0 | 147 | * @exception IOException if an I/O error occurs. |
aoqi@0 | 148 | */ |
aoqi@0 | 149 | public String getName() throws IOException { |
aoqi@0 | 150 | readPrefix(); |
aoqi@0 | 151 | return name; |
aoqi@0 | 152 | } |
aoqi@0 | 153 | |
aoqi@0 | 154 | /** |
aoqi@0 | 155 | * Get the "mode" field from the prefix. This is the permission |
aoqi@0 | 156 | * mode of the source file. |
aoqi@0 | 157 | * |
aoqi@0 | 158 | * @return permission mode of source file |
aoqi@0 | 159 | * @exception IOException if an I/O error occurs. |
aoqi@0 | 160 | */ |
aoqi@0 | 161 | public int getMode() throws IOException { |
aoqi@0 | 162 | readPrefix(); |
aoqi@0 | 163 | return mode; |
aoqi@0 | 164 | } |
aoqi@0 | 165 | |
aoqi@0 | 166 | /** |
aoqi@0 | 167 | * UUencoded streams start off with the line: |
aoqi@0 | 168 | * "begin <mode> <filename>" |
aoqi@0 | 169 | * Search for this prefix and gobble it up. |
aoqi@0 | 170 | */ |
aoqi@0 | 171 | private void readPrefix() throws IOException { |
aoqi@0 | 172 | if (gotPrefix) { |
aoqi@0 | 173 | return; |
aoqi@0 | 174 | } |
aoqi@0 | 175 | |
aoqi@0 | 176 | mode = 0666; // defaults, overridden below |
aoqi@0 | 177 | name = "encoder.buf"; // same default used by encoder |
aoqi@0 | 178 | String line; |
aoqi@0 | 179 | for (;;) { |
aoqi@0 | 180 | // read till we get the prefix: "begin MODE FILENAME" |
aoqi@0 | 181 | line = lin.readLine(); // NOTE: readLine consumes CRLF pairs too |
aoqi@0 | 182 | if (line == null) { |
aoqi@0 | 183 | if (!ignoreMissingBeginEnd) { |
aoqi@0 | 184 | throw new DecodingException("UUDecoder: Missing begin"); |
aoqi@0 | 185 | } |
aoqi@0 | 186 | // at EOF, fake it |
aoqi@0 | 187 | gotPrefix = true; |
aoqi@0 | 188 | gotEnd = true; |
aoqi@0 | 189 | break; |
aoqi@0 | 190 | } |
aoqi@0 | 191 | if (line.regionMatches(false, 0, "begin", 0, 5)) { |
aoqi@0 | 192 | try { |
aoqi@0 | 193 | mode = Integer.parseInt(line.substring(6,9)); |
aoqi@0 | 194 | } catch (NumberFormatException ex) { |
aoqi@0 | 195 | if (!ignoreErrors) { |
aoqi@0 | 196 | throw new DecodingException( |
aoqi@0 | 197 | "UUDecoder: Error in mode: " + ex.toString()); |
aoqi@0 | 198 | } |
aoqi@0 | 199 | } |
aoqi@0 | 200 | if (line.length() > 10) { |
aoqi@0 | 201 | name = line.substring(10); |
aoqi@0 | 202 | } else { |
aoqi@0 | 203 | if (!ignoreErrors) { |
aoqi@0 | 204 | throw new DecodingException( |
aoqi@0 | 205 | "UUDecoder: Missing name: " + line); |
aoqi@0 | 206 | } |
aoqi@0 | 207 | } |
aoqi@0 | 208 | gotPrefix = true; |
aoqi@0 | 209 | break; |
aoqi@0 | 210 | } else if (ignoreMissingBeginEnd && line.length() != 0) { |
aoqi@0 | 211 | int count = line.charAt(0); |
aoqi@0 | 212 | count = (count - ' ') & 0x3f; |
aoqi@0 | 213 | int need = ((count * 8)+5)/6; |
aoqi@0 | 214 | if (need == 0 || line.length() >= need + 1) { |
aoqi@0 | 215 | /* |
aoqi@0 | 216 | * Looks like a legitimate encoded line. |
aoqi@0 | 217 | * Pretend we saw the "begin" line and |
aoqi@0 | 218 | * save this line for later processing in |
aoqi@0 | 219 | * decode(). |
aoqi@0 | 220 | */ |
aoqi@0 | 221 | readAhead = line; |
aoqi@0 | 222 | gotPrefix = true; // fake it |
aoqi@0 | 223 | break; |
aoqi@0 | 224 | } |
aoqi@0 | 225 | } |
aoqi@0 | 226 | } |
aoqi@0 | 227 | } |
aoqi@0 | 228 | |
aoqi@0 | 229 | private boolean decode() throws IOException { |
aoqi@0 | 230 | |
aoqi@0 | 231 | if (gotEnd) { |
aoqi@0 | 232 | return false; |
aoqi@0 | 233 | } |
aoqi@0 | 234 | bufsize = 0; |
aoqi@0 | 235 | int count = 0; |
aoqi@0 | 236 | String line; |
aoqi@0 | 237 | for (;;) { |
aoqi@0 | 238 | /* |
aoqi@0 | 239 | * If we ignored a missing "begin", the first line |
aoqi@0 | 240 | * will be saved in readAhead. |
aoqi@0 | 241 | */ |
aoqi@0 | 242 | if (readAhead != null) { |
aoqi@0 | 243 | line = readAhead; |
aoqi@0 | 244 | readAhead = null; |
aoqi@0 | 245 | } else { |
aoqi@0 | 246 | line = lin.readLine(); |
aoqi@0 | 247 | } |
aoqi@0 | 248 | |
aoqi@0 | 249 | /* |
aoqi@0 | 250 | * Improperly encoded data sometimes omits the zero length |
aoqi@0 | 251 | * line that starts with a space character, we detect the |
aoqi@0 | 252 | * following "end" line here. |
aoqi@0 | 253 | */ |
aoqi@0 | 254 | if (line == null) { |
aoqi@0 | 255 | if (!ignoreMissingBeginEnd) { |
aoqi@0 | 256 | throw new DecodingException( |
aoqi@0 | 257 | "UUDecoder: Missing end at EOF"); |
aoqi@0 | 258 | } |
aoqi@0 | 259 | gotEnd = true; |
aoqi@0 | 260 | return false; |
aoqi@0 | 261 | } |
aoqi@0 | 262 | if (line.equals("end")) { |
aoqi@0 | 263 | gotEnd = true; |
aoqi@0 | 264 | return false; |
aoqi@0 | 265 | } |
aoqi@0 | 266 | if (line.length() == 0) { |
aoqi@0 | 267 | continue; |
aoqi@0 | 268 | } |
aoqi@0 | 269 | count = line.charAt(0); |
aoqi@0 | 270 | if (count < ' ') { |
aoqi@0 | 271 | if (!ignoreErrors) { |
aoqi@0 | 272 | throw new DecodingException( |
aoqi@0 | 273 | "UUDecoder: Buffer format error"); |
aoqi@0 | 274 | } |
aoqi@0 | 275 | continue; |
aoqi@0 | 276 | } |
aoqi@0 | 277 | |
aoqi@0 | 278 | /* |
aoqi@0 | 279 | * The first character in a line is the number of original (not |
aoqi@0 | 280 | * the encoded atoms) characters in the line. Note that all the |
aoqi@0 | 281 | * code below has to handle the <SPACE> character that indicates |
aoqi@0 | 282 | * end of encoded stream. |
aoqi@0 | 283 | */ |
aoqi@0 | 284 | count = (count - ' ') & 0x3f; |
aoqi@0 | 285 | |
aoqi@0 | 286 | if (count == 0) { |
aoqi@0 | 287 | line = lin.readLine(); |
aoqi@0 | 288 | if (line == null || !line.equals("end")) { |
aoqi@0 | 289 | if (!ignoreMissingBeginEnd) { |
aoqi@0 | 290 | throw new DecodingException( |
aoqi@0 | 291 | "UUDecoder: Missing End after count 0 line"); |
aoqi@0 | 292 | } |
aoqi@0 | 293 | } |
aoqi@0 | 294 | gotEnd = true; |
aoqi@0 | 295 | return false; |
aoqi@0 | 296 | } |
aoqi@0 | 297 | |
aoqi@0 | 298 | int need = ((count * 8)+5)/6; |
aoqi@0 | 299 | //System.out.println("count " + count + ", need " + need + ", len " + line.length()); |
aoqi@0 | 300 | if (line.length() < need + 1) { |
aoqi@0 | 301 | if (!ignoreErrors) { |
aoqi@0 | 302 | throw new DecodingException( |
aoqi@0 | 303 | "UUDecoder: Short buffer error"); |
aoqi@0 | 304 | } |
aoqi@0 | 305 | continue; |
aoqi@0 | 306 | } |
aoqi@0 | 307 | |
aoqi@0 | 308 | // got a line we're committed to, break out and decode it |
aoqi@0 | 309 | break; |
aoqi@0 | 310 | } |
aoqi@0 | 311 | |
aoqi@0 | 312 | int i = 1; |
aoqi@0 | 313 | byte a, b; |
aoqi@0 | 314 | /* |
aoqi@0 | 315 | * A correct uuencoder always encodes 3 characters at a time, even |
aoqi@0 | 316 | * if there aren't 3 characters left. But since some people out |
aoqi@0 | 317 | * there have broken uuencoders we handle the case where they |
aoqi@0 | 318 | * don't include these "unnecessary" characters. |
aoqi@0 | 319 | */ |
aoqi@0 | 320 | while (bufsize < count) { |
aoqi@0 | 321 | // continue decoding until we get 'count' decoded chars |
aoqi@0 | 322 | a = (byte)((line.charAt(i++) - ' ') & 0x3f); |
aoqi@0 | 323 | b = (byte)((line.charAt(i++) - ' ') & 0x3f); |
aoqi@0 | 324 | buffer[bufsize++] = (byte)(((a << 2) & 0xfc) | ((b >>> 4) & 3)); |
aoqi@0 | 325 | |
aoqi@0 | 326 | if (bufsize < count) { |
aoqi@0 | 327 | a = b; |
aoqi@0 | 328 | b = (byte)((line.charAt(i++) - ' ') & 0x3f); |
aoqi@0 | 329 | buffer[bufsize++] = |
aoqi@0 | 330 | (byte)(((a << 4) & 0xf0) | ((b >>> 2) & 0xf)); |
aoqi@0 | 331 | } |
aoqi@0 | 332 | |
aoqi@0 | 333 | if (bufsize < count) { |
aoqi@0 | 334 | a = b; |
aoqi@0 | 335 | b = (byte)((line.charAt(i++) - ' ') & 0x3f); |
aoqi@0 | 336 | buffer[bufsize++] = (byte)(((a << 6) & 0xc0) | (b & 0x3f)); |
aoqi@0 | 337 | } |
aoqi@0 | 338 | } |
aoqi@0 | 339 | return true; |
aoqi@0 | 340 | } |
aoqi@0 | 341 | |
aoqi@0 | 342 | /*** begin TEST program ***** |
aoqi@0 | 343 | public static void main(String argv[]) throws Exception { |
aoqi@0 | 344 | FileInputStream infile = new FileInputStream(argv[0]); |
aoqi@0 | 345 | UUDecoderStream decoder = new UUDecoderStream(infile); |
aoqi@0 | 346 | int c; |
aoqi@0 | 347 | |
aoqi@0 | 348 | try { |
aoqi@0 | 349 | while ((c = decoder.read()) != -1) |
aoqi@0 | 350 | System.out.write(c); |
aoqi@0 | 351 | System.out.flush(); |
aoqi@0 | 352 | } catch (Exception e) { |
aoqi@0 | 353 | e.printStackTrace(); |
aoqi@0 | 354 | } |
aoqi@0 | 355 | } |
aoqi@0 | 356 | **** end TEST program ****/ |
aoqi@0 | 357 | } |