1 /*****************************************************************
2 * Licensed to the Apache Software Foundation (ASF) under one *
3 * or more contributor license agreements. See the NOTICE file *
4 * distributed with this work for additional information *
5 * regarding copyright ownership. The ASF licenses this file *
6 * to you under the Apache License, Version 2.0 (the *
7 * "License"); you may not use this file except in compliance *
8 * with the License. You may obtain a copy of the License at *
9 * *
10 * http://www.apache.org/licenses/LICENSE-2.0 *
11 * *
12 * Unless required by applicable law or agreed to in writing, *
13 * software distributed under the License is distributed on an *
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
15 * KIND, either express or implied. See the License for the *
16 * specific language governing permissions and limitations *
17 * under the License. *
18 ****************************************************************/
19 package org.apache.james.core;
20
21 import org.apache.james.util.InternetPrintWriter;
22 import org.apache.james.util.io.IOUtil;
23
24 import javax.activation.UnsupportedDataTypeException;
25 import javax.mail.MessagingException;
26 import javax.mail.internet.MimeMessage;
27 import javax.mail.internet.MimeUtility;
28
29 import java.io.BufferedWriter;
30 import java.io.ByteArrayInputStream;
31 import java.io.ByteArrayOutputStream;
32 import java.io.IOException;
33 import java.io.InputStream;
34 import java.io.OutputStream;
35 import java.io.OutputStreamWriter;
36 import java.io.PrintWriter;
37 import java.util.Enumeration;
38
39 /***
40 * Utility class to provide optimized write methods for the various MimeMessage
41 * implementations.
42 */
43 public class MimeMessageUtil {
44
45 /***
46 * Convenience method to take any MimeMessage and write the headers and body to two
47 * different output streams
48 */
49 public static void writeTo(MimeMessage message, OutputStream headerOs, OutputStream bodyOs) throws IOException, MessagingException {
50 writeTo(message, headerOs, bodyOs, null);
51 }
52
53 /***
54 * Convenience method to take any MimeMessage and write the headers and body to two
55 * different output streams, with an ignore list
56 */
57 public static void writeTo(MimeMessage message, OutputStream headerOs, OutputStream bodyOs, String[] ignoreList) throws IOException, MessagingException {
58 MimeMessage testMessage = message;
59 if (message instanceof MimeMessageCopyOnWriteProxy) {
60 MimeMessageCopyOnWriteProxy wr = (MimeMessageCopyOnWriteProxy) message;
61 testMessage = wr.getWrappedMessage();
62 }
63 if (testMessage instanceof MimeMessageWrapper) {
64 MimeMessageWrapper wrapper = (MimeMessageWrapper)testMessage;
65 if (!wrapper.isModified()) {
66 wrapper.writeTo(headerOs, bodyOs, ignoreList);
67 return;
68 }
69 }
70 writeToInternal(message, headerOs, bodyOs, ignoreList);
71 }
72
73 /***
74 * @param message
75 * @param headerOs
76 * @param bodyOs
77 * @param ignoreList
78 * @throws MessagingException
79 * @throws IOException
80 * @throws UnsupportedDataTypeException
81 */
82 public static void writeToInternal(MimeMessage message, OutputStream headerOs, OutputStream bodyOs, String[] ignoreList) throws MessagingException, IOException, UnsupportedDataTypeException {
83 if(message.getMessageID() == null) {
84 message.saveChanges();
85 }
86
87 writeHeadersTo(message, headerOs, ignoreList);
88
89
90 writeMessageBodyTo(message, bodyOs);
91 }
92
93 public static void writeMessageBodyTo(MimeMessage message, OutputStream bodyOs) throws IOException, UnsupportedDataTypeException, MessagingException {
94 OutputStream bos;
95 InputStream bis;
96
97 try {
98
99
100
101
102 bos = MimeUtility.encode(bodyOs, message.getEncoding());
103 bis = message.getInputStream();
104 } catch(UnsupportedDataTypeException udte) {
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125 try {
126 bis = message.getRawInputStream();
127 bos = bodyOs;
128 } catch(javax.mail.MessagingException _) {
129 throw udte;
130 }
131 }
132 catch(javax.mail.MessagingException me) {
133
134
135
136
137
138
139
140
141
142
143 try {
144 bis = message.getRawInputStream();
145 bos = bodyOs;
146 } catch(javax.mail.MessagingException _) {
147 throw me;
148 }
149 }
150
151 try {
152 copyStream(bis, bos);
153 }
154 finally {
155 IOUtil.shutdownStream(bis);
156 }
157 }
158
159 /***
160 * Convenience method to copy streams
161 */
162 public static void copyStream(InputStream in, OutputStream out) throws IOException {
163
164
165 byte[] block = new byte[1024];
166 int read = 0;
167 while ((read = in.read(block)) > -1) {
168 out.write(block, 0, read);
169 }
170 out.flush();
171 }
172
173
174 /***
175 * Write the message headers to the given outputstream
176 *
177 * @param message
178 * @param headerOs
179 * @param ignoreList
180 * @throws MessagingException
181 */
182 private static void writeHeadersTo(MimeMessage message, OutputStream headerOs, String[] ignoreList) throws MessagingException {
183
184 Enumeration headers = message.getNonMatchingHeaderLines(ignoreList);
185 writeHeadersTo(headers, headerOs);
186 }
187
188 /***
189 * Write the message headers to the given outputstream
190 *
191 * @param message
192 * @param headerOs
193 * @param ignoreList
194 * @throws MessagingException
195 */
196 public static void writeHeadersTo(Enumeration headers, OutputStream headerOs) throws MessagingException {
197 PrintWriter hos = new InternetPrintWriter(new BufferedWriter(new OutputStreamWriter(headerOs), 512), true);
198 while (headers.hasMoreElements()) {
199 hos.println((String)headers.nextElement());
200 }
201
202 hos.println();
203 hos.flush();
204 }
205
206 /***
207 * @param message
208 * @param ignoreList
209 * @return
210 * @throws MessagingException
211 */
212 public static InputStream getHeadersInputStream(MimeMessage message, String[] ignoreList) throws MessagingException {
213 ByteArrayOutputStream bo = new ByteArrayOutputStream();
214 writeHeadersTo(message,bo,ignoreList);
215 return new ByteArrayInputStream(bo.toByteArray());
216 }
217
218
219 /***
220 * Slow method to calculate the exact size of a message!
221 */
222 private static final class SizeCalculatorOutputStream extends OutputStream {
223 long size = 0;
224
225 public void write(int arg0) throws IOException {
226 size++;
227 }
228
229 public long getSize() {
230 return size;
231 }
232
233 public void write(byte[] arg0, int arg1, int arg2) throws IOException {
234 size += arg2;
235 }
236
237 public void write(byte[] arg0) throws IOException {
238 size += arg0.length;
239 }
240 }
241
242 /***
243 * @return size of full message including headers
244 *
245 * @throws MessagingException if a problem occours while computing the message size
246 */
247 public static long getMessageSize(MimeMessage message) throws MessagingException {
248
249
250 long size = -1;
251
252 if (message instanceof MimeMessageWrapper) {
253 MimeMessageWrapper wrapper = (MimeMessageWrapper) message;
254 size = wrapper.getMessageSize();
255 } else if (message instanceof MimeMessageCopyOnWriteProxy) {
256 MimeMessageCopyOnWriteProxy wrapper = (MimeMessageCopyOnWriteProxy) message;
257 size = wrapper.getMessageSize();
258 }
259
260 if (size == -1) {
261 size = calculateMessageSize(message);
262 }
263
264 return size;
265 }
266
267 /***
268 * @param message
269 * @return the calculated size
270 * @throws MessagingException
271 */
272 public static long calculateMessageSize(MimeMessage message) throws MessagingException {
273 long size;
274
275
276
277 size = message.getSize();
278 if (size != -1) {
279 Enumeration e = message.getAllHeaderLines();
280 if (e.hasMoreElements()) {
281 size += 2;
282 }
283 while (e.hasMoreElements()) {
284
285 size += ((String) e.nextElement()).length()+2;
286 }
287 }
288
289
290 if (size == -1) {
291 SizeCalculatorOutputStream out = new SizeCalculatorOutputStream();
292 try {
293 message.writeTo(out);
294 } catch (IOException e) {
295
296 throw new MessagingException("IOException wrapped by getMessageSize",e);
297 }
298 size = out.getSize();
299 }
300 return size;
301 }
302
303
304 }