1 /**
2 *
3 * Copyright 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.javamail.store.nntp;
19
20 import java.io.ByteArrayOutputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.util.Enumeration;
24
25 import javax.mail.Flags;
26 import javax.mail.IllegalWriteException;
27 import javax.mail.MessagingException;
28 import javax.mail.Session;
29 import javax.mail.internet.InternetHeaders;
30 import javax.mail.internet.MimeMessage;
31
32 import org.apache.geronimo.javamail.transport.nntp.NNTPConnection;
33 import org.apache.geronimo.javamail.transport.nntp.NNTPReply;
34 import org.apache.geronimo.javamail.transport.nntp.StringListInputStream;
35
36 /**
37 * NNTP implementation of javax.mail.internet.MimeMessage
38 *
39 * Only the most basic information is given and Message objects created here is
40 * a light-weight reference to the actual Message As per the JavaMail spec items
41 * from the actual message will get filled up on demand
42 *
43 * If some other items are obtained from the server as a result of one call,
44 * then the other details are also processed and filled in. For ex if RETR is
45 * called then header information will also be processed in addition to the
46 * content
47 *
48 * @version $Rev: 432884 $ $Date: 2006-08-19 14:53:20 -0700 (Sat, 19 Aug 2006) $
49 */
50 public class NNTPMessage extends MimeMessage {
51
52 String messageID = null;
53
54
55 protected Session session;
56
57
58 protected NNTPStore store;
59
60
61 protected NNTPConnection connection;
62
63
64 protected boolean headersLoaded = false;
65
66
67 protected boolean contentLoaded = false;
68
69 /**
70 * Contruct an NNTPMessage instance.
71 *
72 * @param folder
73 * The hosting folder for the message.
74 * @param store
75 * The Store owning the article (and folder).
76 * @param msgnum
77 * The article message number.
78 * @param messageID
79 * The article messageID (as assigned by the server).
80 *
81 * @exception MessagingException
82 */
83 NNTPMessage(NNTPFolder folder, NNTPStore store, int msgnum, String messageID) throws MessagingException {
84 super(folder, msgnum);
85 this.messageID = messageID;
86 this.store = store;
87 this.session = ((NNTPStore) store).getSession();
88
89
90 this.connection = ((NNTPStore) store).getConnection();
91
92
93 flags = folder.getPermanentFlags();
94
95 if (folder.isSeen(msgnum)) {
96 flags.add(Flags.Flag.SEEN);
97 } else {
98 flags.remove(Flags.Flag.SEEN);
99 }
100 }
101
102 /**
103 * Retrieve the size of the message content. The content will be retrieved
104 * from the server, if necessary.
105 *
106 * @return The size of the content.
107 * @exception MessagingException
108 */
109 public int getSize() throws MessagingException {
110
111
112 loadContent();
113 return super.getSize();
114 }
115
116 /**
117 * Get a line count for the NNTP message. This is potentially stored in the
118 * Lines article header. If not there, we return a default of -1.
119 *
120 * @return The header line count estimate, or -1 if not retrieveable.
121 * @exception MessagingException
122 */
123 public int getLineCount() throws MessagingException {
124 String[] headers = getHeader("Lines");
125
126
127
128
129 if (headers.length == 1) {
130 try {
131 return Integer.parseInt(headers[0].trim());
132
133 } catch (NumberFormatException e) {
134
135 }
136 }
137
138 return -1;
139 }
140
141 /**
142 * @see javax.mail.internet.MimeMessage#getContentStream()
143 */
144 protected InputStream getContentStream() throws MessagingException {
145
146 loadArticle();
147 return super.getContentStream();
148 }
149
150 /***************************************************************************
151 * Following is a set of methods that deal with headers These methods are
152 * just overrides on the superclass methods to allow lazy loading of the
153 * header information.
154 **************************************************************************/
155
156 public String[] getHeader(String name) throws MessagingException {
157 loadHeaders();
158 return headers.getHeader(name);
159 }
160
161 public String getHeader(String name, String delimiter) throws MessagingException {
162 loadHeaders();
163 return headers.getHeader(name, delimiter);
164 }
165
166 public Enumeration getAllHeaders() throws MessagingException {
167 loadHeaders();
168 return headers.getAllHeaders();
169 }
170
171 public Enumeration getMatchingHeaders(String[] names) throws MessagingException {
172 loadHeaders();
173 return headers.getMatchingHeaders(names);
174 }
175
176 public Enumeration getNonMatchingHeaders(String[] names) throws MessagingException {
177 loadHeaders();
178 return headers.getNonMatchingHeaders(names);
179 }
180
181 public Enumeration getAllHeaderLines() throws MessagingException {
182 loadHeaders();
183 return headers.getAllHeaderLines();
184 }
185
186 public Enumeration getMatchingHeaderLines(String[] names) throws MessagingException {
187 loadHeaders();
188 return headers.getMatchingHeaderLines(names);
189 }
190
191 public Enumeration getNonMatchingHeaderLines(String[] names) throws MessagingException {
192 loadHeaders();
193 return headers.getNonMatchingHeaderLines(names);
194 }
195
196
197
198
199 public void addHeader(String name, String value) throws MessagingException {
200 throw new IllegalWriteException("NNTP messages are read-only");
201 }
202
203 public void setHeader(String name, String value) throws MessagingException {
204 throw new IllegalWriteException("NNTP messages are read-only");
205 }
206
207 public void removeHeader(String name) throws MessagingException {
208 throw new IllegalWriteException("NNTP messages are read-only");
209 }
210
211 public void addHeaderLine(String line) throws MessagingException {
212 throw new IllegalWriteException("IMAP messages are read-only");
213 }
214
215 /**
216 * We cannot modify these messages
217 */
218 public void saveChanges() throws MessagingException {
219 throw new IllegalWriteException("NNTP messages are read-only");
220 }
221
222 /**
223 * Retrieve the message headers from the NNTP server.
224 *
225 * @exception MessagingException
226 */
227 public void loadHeaders() throws MessagingException {
228
229 if (headersLoaded) {
230 return;
231 }
232
233 NNTPReply reply = connection.sendCommand("HEAD " + messageID, NNTPReply.HEAD_FOLLOWS);
234
235 if (reply.getCode() == NNTPReply.HEAD_FOLLOWS) {
236 try {
237
238 updateHeaders(new StringListInputStream(reply.getData()));
239 } catch (IOException e) {
240 throw new MessagingException("Error retrieving article headers from server", e);
241 }
242 } else {
243 throw new MessagingException("Error retrieving article headers from server: " + reply);
244 }
245 }
246
247 /**
248 * Update the message headers from an input stream.
249 *
250 * @param in
251 * The InputStream source for the header information.
252 *
253 * @exception MessagingException
254 */
255 public void updateHeaders(InputStream in) throws MessagingException {
256
257 headers = new InternetHeaders(in);
258 headersLoaded = true;
259 }
260
261 /**
262 * Load just the message content from the NNTP server.
263 *
264 * @exception MessagingException
265 */
266 public void loadContent() throws MessagingException {
267 if (contentLoaded) {
268 return;
269 }
270
271 NNTPReply reply = connection.sendCommand("BODY " + messageID, NNTPReply.BODY_FOLLOWS);
272
273 if (reply.getCode() == NNTPReply.BODY_FOLLOWS) {
274 try {
275 InputStream in = new StringListInputStream(reply.getData());
276 updateContent(in);
277 } catch (IOException e) {
278 throw new MessagingException("Error retrieving article body from server", e);
279 }
280 } else {
281 throw new MessagingException("Error retrieving article body from server: " + reply);
282 }
283 }
284
285 /**
286 * Load the entire article from the NNTP server. This updates both the
287 * headers and the content.
288 *
289 * @exception MessagingException
290 */
291 public void loadArticle() throws MessagingException {
292
293 if (headersLoaded) {
294 loadContent();
295 return;
296 }
297
298
299 NNTPReply reply = connection.sendCommand("ARTICLE " + messageID, NNTPReply.ARTICLE_FOLLOWS);
300
301 if (reply.getCode() == NNTPReply.ARTICLE_FOLLOWS) {
302 try {
303 InputStream in = new StringListInputStream(reply.getData());
304
305 updateHeaders(in);
306 updateContent(in);
307 } catch (IOException e) {
308 throw new MessagingException("Error retrieving article from server", e);
309 }
310 } else {
311 throw new MessagingException("Error retrieving article from server: " + reply);
312 }
313 }
314
315 /**
316 * Update the article content from an input stream.
317 *
318 * @param in
319 * The content data source.
320 *
321 * @exception MessagingException
322 */
323 public void updateContent(InputStream in) throws MessagingException {
324 try {
325 ByteArrayOutputStream out = new ByteArrayOutputStream();
326
327 byte[] buffer = new byte[4096];
328
329
330
331 while (true) {
332 int read = in.read(buffer);
333 if (read == -1) {
334 break;
335 }
336 out.write(buffer, 0, read);
337 }
338
339 content = out.toByteArray();
340 contentLoaded = true;
341 } catch (IOException e) {
342 throw new MessagingException("Error retrieving message body from server", e);
343 }
344 }
345
346 /**
347 * Get the server assigned messageid for the article.
348 *
349 * @return The server assigned message id.
350 */
351 public String getMessageId() {
352 return messageID;
353 }
354
355 /**
356 * Override of setFlags(). We need to ensure that if the SEEN flag is set or
357 * cleared, that the newsrc file correctly reflects the current state.
358 *
359 * @param flag
360 * The flag being set.
361 * @param newvalue
362 * The new flag value.
363 *
364 * @exception MessagingException
365 */
366 public void setFlags(Flags flag, boolean newvalue) throws MessagingException {
367
368
369 if (flag.contains(Flags.Flag.SEEN)) {
370 ((NNTPFolder) folder).setSeen(msgnum, newvalue);
371 }
372
373 super.setFlags(flag, newvalue);
374 }
375 }