1 /**
2 *
3 * Copyright 2003-2005 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.util.ArrayList;
21 import java.util.HashMap;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.StringTokenizer;
25
26 import javax.mail.FetchProfile;
27 import javax.mail.FolderNotFoundException;
28 import javax.mail.Message;
29 import javax.mail.MessagingException;
30
31 import org.apache.geronimo.javamail.store.nntp.newsrc.NNTPNewsrcGroup;
32 import org.apache.geronimo.javamail.transport.nntp.NNTPReply;
33
34 /**
35 * The NNTP implementation of the javax.mail.Folder Note that only INBOX is
36 * supported in NNTP
37 * <p>
38 * <url>http://www.faqs.org/rfcs/rfc1939.html</url>
39 * </p>
40 *
41 * @see javax.mail.Folder
42 *
43 * @version $Rev: 432884 $ $Date: 2006-08-19 14:53:20 -0700 (Sat, 19 Aug 2006) $
44 */
45 public class NNTPGroupFolder extends NNTPFolder {
46
47
48 protected int firstArticle = -1;
49
50 protected int lastArticle = -1;
51
52
53 Map articles;
54
55
56 NNTPNewsrcGroup groupInfo;
57
58 /**
59 * Construct a "real" folder representing an NNTP news group.
60 *
61 * @param parent
62 * The parent root folder.
63 * @param store
64 * The Store this folder is attached to.
65 * @param name
66 * The folder name.
67 * @param groupInfo
68 * The newsrc group information attached to the newsrc database.
69 * This contains subscription and article "SEEN" information.
70 */
71 protected NNTPGroupFolder(NNTPRootFolder parent, NNTPStore store, String name, NNTPNewsrcGroup groupInfo) {
72 super(store);
73
74 this.name = name;
75 this.fullName = name;
76
77 this.parent = parent = parent;
78 this.groupInfo = groupInfo;
79 }
80
81 /**
82 * Ping the server and update the group count, first, and last information.
83 *
84 * @exception MessagingException
85 */
86 private void updateGroupStats() throws MessagingException {
87
88
89
90 NNTPReply reply = connection.sendCommand("GROUP " + name);
91
92
93 if (reply.getCode() == NNTPReply.NO_SUCH_NEWSGROUP) {
94 throw new FolderNotFoundException(this, "Folder does not exist on server: " + reply);
95 } else if (reply.getCode() != NNTPReply.GROUP_SELECTED) {
96 throw new MessagingException("Error requesting group information: " + reply);
97 }
98
99
100
101
102
103 StringTokenizer tokenizer = new StringTokenizer(reply.getMessage());
104
105
106
107
108
109 if (tokenizer.hasMoreTokens()) {
110 String count = tokenizer.nextToken();
111 try {
112 messageCount = Integer.parseInt(count);
113 } catch (NumberFormatException e) {
114
115 }
116 }
117
118
119 if (tokenizer.hasMoreTokens()) {
120 String first = tokenizer.nextToken();
121 try {
122 firstArticle = Integer.parseInt(first);
123 } catch (NumberFormatException e) {
124
125 }
126 }
127
128
129 if (tokenizer.hasMoreTokens()) {
130 String last = tokenizer.nextToken();
131 try {
132 lastArticle = Integer.parseInt(last);
133 } catch (NumberFormatException e) {
134
135 }
136 }
137 }
138
139 /**
140 * Test to see if this folder actually exists. This pings the server for
141 * information about the GROUP and updates the article count and index
142 * information.
143 *
144 * @return true if the newsgroup exists on the server, false otherwise.
145 * @exception MessagingException
146 */
147 public boolean exists() throws MessagingException {
148
149 try {
150
151
152
153 updateGroupStats();
154
155 return true;
156 } catch (FolderNotFoundException e) {
157 return false;
158 }
159 }
160
161 /**
162 * Ping the NNTP server to check if a newsgroup has any new messages.
163 *
164 * @return True if the server has new articles from the last time we
165 * checked. Also returns true if this is the first time we've
166 * checked.
167 * @exception MessagingException
168 */
169 public boolean hasNewMessages() throws MessagingException {
170 int oldLast = lastArticle;
171 updateGroupStats();
172
173 return lastArticle > oldLast;
174 }
175
176 /**
177 * Open the folder for use. This retrieves article count information from
178 * the server.
179 *
180 * @exception MessagingException
181 */
182 public void openFolder() throws MessagingException {
183
184 updateGroupStats();
185
186
187 articles = new HashMap();
188 }
189
190 /**
191 * Close the folder, which also clears out the article caches.
192 *
193 * @exception MessagingException
194 */
195 public void closeFolder() throws MessagingException {
196
197
198 articles = null;
199 }
200
201 /**
202 * Checks wether the message is in cache, if not will create a new message
203 * object and return it.
204 *
205 * @see javax.mail.Folder#getMessage(int)
206 */
207 public Message getMessage(int msgNum) throws MessagingException {
208
209 checkOpen();
210
211
212
213
214 Integer key = new Integer(msgNum);
215 NNTPMessage message = (NNTPMessage) articles.get(key);
216 if (message != null) {
217
218 return message;
219 }
220
221
222
223 updateGroupStats();
224
225
226
227
228 NNTPReply reply = connection.sendCommand("STAT " + Integer.toString(msgNum));
229 if (reply.getCode() != NNTPReply.REQUEST_TEXT_SEPARATELY) {
230 throw new MessagingException("Error retrieving article from NNTP server: " + reply);
231 }
232
233
234 String response = reply.getMessage();
235
236 int idStart = response.indexOf('<');
237 int idEnd = response.indexOf('>');
238
239 message = new NNTPMessage(this, (NNTPStore) store, msgNum, response.substring(idStart + 1, idEnd));
240
241
242 articles.put(key, message);
243
244 return message;
245 }
246
247 /**
248 * Retrieve all articles in the group.
249 *
250 * @return An array of all messages in the group.
251 */
252 public Message[] getMessages() throws MessagingException {
253
254
255
256
257 NNTPReply reply = connection.sendCommand("XHDR Message-ID " + Integer.toString(firstArticle) + "-"
258 + Integer.toString(lastArticle), NNTPReply.HEAD_FOLLOWS);
259
260 List messages = new ArrayList();
261
262 if (reply.getCode() == NNTPReply.HEAD_FOLLOWS) {
263 List lines = reply.getData();
264
265 for (int i = 0; i < lines.size(); i++) {
266 String line = (String) lines.get(i);
267
268 try {
269 int pos = line.indexOf(' ');
270 int articleID = Integer.parseInt(line.substring(0, pos));
271 String messageID = line.substring(pos + 1);
272 Integer key = new Integer(articleID);
273
274 Message message = (Message) articles.get(key);
275 if (message == null) {
276 message = new NNTPMessage(this, (NNTPStore) store, key.intValue(), messageID);
277 articles.put(key, message);
278 }
279
280 messages.add(message);
281
282 } catch (NumberFormatException e) {
283
284 }
285 }
286 } else {
287
288
289 for (int i = firstArticle; i <= lastArticle; i++) {
290 try {
291 messages.add(getMessage(i));
292 } catch (MessagingException e) {
293
294
295 }
296 }
297 }
298
299 return (Message[]) messages.toArray(new Message[0]);
300 }
301
302 /**
303 * @see javax.mail.Folder#fetch(javax.mail.Message[],
304 * javax.mail.FetchProfile)
305 *
306 * The JavaMail API recommends that this method be overrident to provide a
307 * meaningfull implementation.
308 */
309 public void fetch(Message[] msgs, FetchProfile fp) throws MessagingException {
310
311 checkOpen();
312
313 for (int i = 0; i < msgs.length; i++) {
314 Message msg = msgs[i];
315
316 if (msg == null || !(msg instanceof NNTPMessage)) {
317
318 continue;
319 }
320
321
322 if (fp.contains(FetchProfile.Item.ENVELOPE) && fp.contains(FetchProfile.Item.CONTENT_INFO)) {
323
324
325 ((NNTPMessage) msg).loadArticle();
326 }
327
328 else if (fp.contains(FetchProfile.Item.ENVELOPE)) {
329 ((NNTPMessage) msg).loadHeaders();
330 } else if (fp.contains(FetchProfile.Item.CONTENT_INFO)) {
331 ((NNTPMessage) msg).loadContent();
332 }
333 }
334 }
335
336 /**
337 * Return the subscription status of this folder.
338 *
339 * @return true if the folder is marked as subscribed, false for
340 * unsubscribed.
341 */
342 public boolean isSubscribed() {
343 return groupInfo.isSubscribed();
344 }
345
346 /**
347 * Set or clear the subscription status of a file.
348 *
349 * @param flag
350 * The new subscription state.
351 */
352 public void setSubscribed(boolean flag) {
353 groupInfo.setSubscribed(flag);
354 }
355
356 /**
357 * Return the "seen" state for an article in a folder.
358 *
359 * @param article
360 * The article number.
361 *
362 * @return true if the article is marked as seen in the newsrc file, false
363 * for unseen files.
364 */
365 public boolean isSeen(int article) {
366 return groupInfo.isArticleSeen(article);
367 }
368
369 /**
370 * Set the seen state for an article in a folder.
371 *
372 * @param article
373 * The article number.
374 * @param flag
375 * The new seen state.
376 */
377 public void setSeen(int article, boolean flag) {
378 if (flag) {
379 groupInfo.markArticleSeen(article);
380 } else {
381 groupInfo.markArticleUnseen(article);
382 }
383 }
384 }