1 /**
2 *
3 * Copyright 2003-2006 The Apache Software Foundation
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.geronimo.mail.util;
19
20 import java.io.IOException;
21 import java.io.OutputStream;
22 import java.io.UnsupportedEncodingException;
23
24 public class UUEncoder implements Encoder {
25
26
27
28 static private final int MAX_CHARS_PER_LINE = 45;
29
30
31 public UUEncoder()
32 {
33 }
34
35 /**
36 * encode the input data producing a UUEncoded output stream.
37 *
38 * @param data The array of byte data.
39 * @param off The starting offset within the data.
40 * @param length Length of the data to encode.
41 * @param out The output stream the encoded data is written to.
42 *
43 * @return the number of bytes produced.
44 */
45 public int encode(byte[] data, int off, int length, OutputStream out) throws IOException
46 {
47 int byteCount = 0;
48
49 while (true) {
50
51 if (length > MAX_CHARS_PER_LINE) {
52
53 byteCount += encodeLine(data, off, MAX_CHARS_PER_LINE, out);
54 length -= MAX_CHARS_PER_LINE;
55 off += MAX_CHARS_PER_LINE;
56 }
57 else {
58
59 byteCount += encodeLine(data, off, MAX_CHARS_PER_LINE, out);
60 break;
61 }
62 }
63 return byteCount;
64 }
65
66
67 /**
68 * Encode a single line of data (less than or equal to 45 characters).
69 *
70 * @param data The array of byte data.
71 * @param off The starting offset within the data.
72 * @param length Length of the data to encode.
73 * @param out The output stream the encoded data is written to.
74 *
75 * @return The number of bytes written to the output stream.
76 * @exception IOException
77 */
78 private int encodeLine(byte[] data, int offset, int length, OutputStream out) throws IOException {
79
80 out.write((byte)((length & 0x3F) + ' '));
81 byte a;
82 byte b;
83 byte c;
84
85
86 int bytesWritten = 2;
87
88 for (int i = 0; i < length;) {
89
90 b = 1;
91 c = 1;
92
93 a = data[offset + i++];
94 if (i < length) {
95 b = data[offset + i++];
96 if (i < length) {
97 c = data[offset + i++];
98 }
99 }
100
101 byte d1 = (byte)(((a >>> 2) & 0x3F) + ' ');
102 byte d2 = (byte)(((( a << 4) & 0x30) | ((b >>> 4) & 0x0F)) + ' ');
103 byte d3 = (byte)((((b << 2) & 0x3C) | ((c >>> 6) & 0x3)) + ' ');
104 byte d4 = (byte)((c & 0x3F) + ' ');
105
106 out.write(d1);
107 out.write(d2);
108 out.write(d3);
109 out.write(d4);
110
111 bytesWritten += 4;
112 }
113
114
115 out.write('\n');
116
117 return bytesWritten;
118 }
119
120
121 /**
122 * decode the uuencoded byte data writing it to the given output stream
123 *
124 * @param data The array of byte data to decode.
125 * @param off Starting offset within the array.
126 * @param length The length of data to encode.
127 * @param out The output stream used to return the decoded data.
128 *
129 * @return the number of bytes produced.
130 * @exception IOException
131 */
132 public int decode(byte[] data, int off, int length, OutputStream out) throws IOException
133 {
134 int bytesWritten = 0;
135
136 while (length > 0) {
137 int lineOffset = off;
138
139
140 while (length > 0 && data[off] != '\n') {
141 off++;
142 length--;
143 }
144
145
146 bytesWritten += decodeLine(data, lineOffset, off - lineOffset, out);
147
148
149
150 off++;
151 length--;
152 }
153 return bytesWritten;
154 }
155
156
157 /**
158 * decode a single line of uuencoded byte data writing it to the given output stream
159 *
160 * @param data The array of byte data to decode.
161 * @param off Starting offset within the array.
162 * @param length The length of data to decode (length does NOT include the terminating new line).
163 * @param out The output stream used to return the decoded data.
164 *
165 * @return the number of bytes produced.
166 * @exception IOException
167 */
168 private int decodeLine(byte[] data, int off, int length, OutputStream out) throws IOException {
169 int count = data[off++];
170
171
172 if (count < ' ') {
173 throw new IOException("Invalid UUEncode line length");
174 }
175
176 count = (count - ' ') & 0x3F;
177
178
179
180 int requiredLength = (((count * 8) + 5) / 6) + 1;
181 if (length < requiredLength) {
182 throw new IOException("UUEncoded data and length do not match");
183 }
184
185 int bytesWritten = 0;
186
187 while (bytesWritten < count) {
188
189 byte a = (byte)((data[off++] - ' ') & 0x3F);
190 byte b = (byte)((data[off++] - ' ') & 0x3F);
191 byte c = 0;
192 byte d = 0;
193
194
195 byte first = (byte)(((a << 2) & 0xFC) | ((b >>> 4) & 3));
196 out.write(first);
197 bytesWritten++;
198
199
200
201 if (bytesWritten < count) {
202 c = (byte)((data[off++] - ' ') & 0x3F);
203 byte second = (byte)(((b << 4) & 0xF0) | ((c >>> 2) & 0x0F));
204 out.write(second);
205 bytesWritten++;
206
207
208 if (bytesWritten < count) {
209 d = (byte)((data[off++] - ' ') & 0x3F);
210 byte third = (byte)(((c << 6) & 0xC0) | (d & 0x3F));
211 out.write(third);
212 bytesWritten++;
213 }
214 }
215 }
216 return bytesWritten;
217 }
218
219
220 /**
221 * decode the UUEncoded String data writing it to the given output stream.
222 *
223 * @param data The String data to decode.
224 * @param out The output stream to write the decoded data to.
225 *
226 * @return the number of bytes produced.
227 * @exception IOException
228 */
229 public int decode(String data, OutputStream out) throws IOException
230 {
231 try {
232
233 byte[] bytes = data.getBytes("US-ASCII");
234 return decode(bytes, 0, bytes.length, out);
235 } catch (UnsupportedEncodingException e) {
236 throw new IOException("Invalid UUEncoding");
237 }
238 }
239 }
240
241