src/share/jaxws_classes/com/sun/xml/internal/org/jvnet/mimepull/UUDecoderStream.java

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

mercurial