001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *  http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    
020    package org.apache.geronimo.javamail.store.nntp;
021    
022    import javax.mail.Flags;
023    import javax.mail.Folder;
024    import javax.mail.IllegalWriteException;
025    import javax.mail.Message;
026    import javax.mail.MessagingException;
027    import javax.mail.MethodNotSupportedException;
028    import javax.mail.Session;
029    import javax.mail.event.ConnectionEvent;
030    
031    import org.apache.geronimo.javamail.transport.nntp.NNTPConnection;
032    
033    /**
034     * The base NNTP implementation of the javax.mail.Folder This is a base class
035     * for both the Root NNTP server and each NNTP group folder.
036     * 
037     * @see javax.mail.Folder
038     * 
039     * @version $Rev: 437941 $
040     */
041    public class NNTPFolder extends Folder {
042    
043        // our active connection.
044        protected NNTPConnection connection;
045    
046        // our attached session
047        protected Session session;
048    
049        // the name of this folder (either the name of the server for the root or
050        // the news group name).
051        protected String name;
052    
053        // the "full" name of the folder. For the root folder, this is the name
054        // returned by the connection
055        // welcome string. Otherwise, this is the same as the name.
056        protected String fullName;
057    
058        // the parent folder. For the root folder, this is null. For a group folder,
059        // this is the root.
060        protected Folder parent;
061    
062        // the folder open state
063        protected boolean folderOpen = false;
064    
065        // the folder message count. For the root folder, this is always 0.
066        protected int messageCount = 0;
067    
068        // the persistent flags we save in the store (basically just the SEEN flag).
069        protected Flags permanentFlags;
070    
071        /**
072         * Super class constructor the base NNTPFolder class.
073         * 
074         * @param store
075         *            The javamail store this folder is attached to.
076         */
077        protected NNTPFolder(NNTPStore store) {
078            super(store);
079            // get the active connection from the store...all commands are sent
080            // there
081            this.connection = store.getConnection();
082            this.session = store.getSession();
083    
084            // set up our permanent flags bit.
085            permanentFlags = new Flags();
086            permanentFlags.add(Flags.Flag.SEEN);
087        }
088    
089        /**
090         * Retrieve the folder name.
091         * 
092         * @return The folder's name.
093         */
094        public String getName() {
095            return name;
096        }
097    
098        /**
099         * Retrieve the folder's full name (including hierarchy information). NNTP
100         * folders are flat, so the full name is generally the same as the name.
101         * 
102         * @return The full name value.
103         */
104        public String getFullName() {
105            return fullName;
106        }
107    
108        /**
109         * Returns the parent folder for this folder. Returns null if this is the
110         * root folder.
111         */
112        public Folder getParent() throws MessagingException {
113            return parent;
114        }
115    
116        /**
117         * Indicated whether the folder "exists" or not. Existance in this context
118         * indicates that the group still exists on the server.
119         * 
120         * @return
121         * @exception MessagingException
122         */
123        public boolean exists() throws MessagingException {
124            // by default, return true. This is really only the case for the root.
125            // The group folder will
126            // need to override this.
127            return true;
128        }
129    
130        /**
131         * List the subfolders. For group folders, this is a meaningless so we throw
132         * a MethodNotSupportedException.
133         * 
134         * @param pattern
135         *            The folder pattern string.
136         * 
137         * @return Never returns.
138         * @exception MessagingException
139         */
140        public Folder[] list(String pattern) throws MessagingException {
141            throw new MethodNotSupportedException("NNTP group folders cannot contain sub folders");
142        }
143    
144        /**
145         * Retrieve the list of subscribed folders that match the given pattern
146         * string.
147         * 
148         * @param pattern
149         *            The pattern string used for the matching
150         * 
151         * @return An array of matching folders from the subscribed list.
152         */
153        public Folder[] listSubscribed(String pattern) throws MessagingException {
154            throw new MethodNotSupportedException("NNTP group folders cannot contain sub folders");
155        }
156    
157        /**
158         * No sub folders, hence there is no notion of a seperator. We return a null
159         * character (consistent with what Sun returns for POP3 folders).
160         */
161        public char getSeparator() throws MessagingException {
162            return '\0';
163        }
164    
165        /**
166         * Return whether this folder can hold just messages or also subfolders.
167         * Only the root folder can hold other folders, so it will need to override.
168         * 
169         * @return Either Folder.HOLDS_MESSAGES or Folder.HOLDS_FOLDERS.
170         * @exception MessagingException
171         */
172        public int getType() throws MessagingException {
173            return HOLDS_MESSAGES;
174        }
175    
176        /**
177         * Create a new folder. NNTP folders are read only, so this is a nop.
178         * 
179         * @param type
180         *            The type of folder.
181         * 
182         * @return Not support, throws an exception.
183         * @exception MessagingException
184         */
185        public boolean create(int type) throws MessagingException {
186            throw new MethodNotSupportedException("Sub folders cannot be created in NNTP");
187        }
188    
189        /**
190         * Check for new messages. We always return false for the root folder. The
191         * group folders will need to override.
192         * 
193         * @return Always returns false.
194         * @exception MessagingException
195         */
196        public boolean hasNewMessages() throws MessagingException {
197            return false;
198        }
199    
200        /**
201         * Get a named subfolder from this folder. This only has meaning from the
202         * root NNTP folder.
203         * 
204         * @param name
205         *            The requested name.
206         * 
207         * @return If the folder exists, returns a Folder object representing the
208         *         named folder.
209         * @exception MessagingException
210         */
211        public Folder getFolder(String name) throws MessagingException {
212            throw new MethodNotSupportedException("NNTP Group folders do not support sub folders");
213        }
214    
215        /**
216         * Delete a folder. This is not supported for NNTP.
217         * 
218         * @param recurse
219         *            The recusion flag.
220         * 
221         * @return Never returns.
222         * @exception MessagingException
223         */
224        public boolean delete(boolean recurse) throws MessagingException {
225            throw new MethodNotSupportedException("Deleting of NNTP folders is not supported");
226        }
227    
228        /**
229         * Rename a folder. Not supported for NNTP folders.
230         * 
231         * @param f
232         *            The new folder specifying the rename location.
233         * 
234         * @return
235         * @exception MessagingException
236         */
237        public boolean renameTo(Folder f) throws MessagingException {
238            throw new MethodNotSupportedException("Renaming of NNTP folders is not supported.");
239        }
240    
241        /**
242         * @see javax.mail.Folder#open(int)
243         */
244        public void open(int mode) throws MessagingException {
245    
246            // we don't support READ_WRITE mode, so don't allow opening in that
247            // mode.
248            if (mode == READ_WRITE) {
249                throw new IllegalWriteException("Newsgroup folders cannot be opened read/write");
250            }
251    
252            // an only be performed on a closed folder
253            checkClosed();
254    
255            this.mode = mode;
256    
257            // perform folder type-specific open actions.
258            openFolder();
259    
260            folderOpen = true;
261    
262            notifyConnectionListeners(ConnectionEvent.OPENED);
263        }
264    
265        /**
266         * Perform folder type-specific open actions. The default action is to do
267         * nothing.
268         * 
269         * @exception MessagingException
270         */
271        protected void openFolder() throws MessagingException {
272        }
273    
274        /**
275         * Peform folder type-specific close actions. The default action is to do
276         * nothing.
277         * 
278         * @exception MessagingException
279         */
280        protected void closeFolder() throws MessagingException {
281        }
282    
283        /**
284         * Close the folder. Cleans up resources, potentially expunges messages
285         * marked for deletion, and sends an event notification.
286         * 
287         * @param expunge
288         *            The expunge flag, which is ignored for NNTP folders.
289         * 
290         * @exception MessagingException
291         */
292        public void close(boolean expunge) throws MessagingException {
293            // Can only be performed on an open folder
294            checkOpen();
295    
296            // give the subclasses an opportunity to do some cleanup
297            closeFolder();
298    
299            folderOpen = false;
300            notifyConnectionListeners(ConnectionEvent.CLOSED);
301        }
302    
303        /**
304         * Tests the open status of the folder.
305         * 
306         * @return true if the folder is open, false otherwise.
307         */
308        public boolean isOpen() {
309            return folderOpen;
310        }
311    
312        /**
313         * Get the permanentFlags
314         * 
315         * @return The set of permanent flags we support (only SEEN).
316         */
317        public Flags getPermanentFlags() {
318            // we need a copy of our master set.
319            return new Flags(permanentFlags);
320        }
321    
322        /**
323         * Get the count of messages in this folder.
324         * 
325         * @return The message count.
326         * @exception MessagingException
327         */
328        public int getMessageCount() throws MessagingException {
329            return messageCount;
330        }
331    
332        /**
333         * Checks wether the message is in cache, if not will create a new message
334         * object and return it.
335         * 
336         * @see javax.mail.Folder#getMessage(int)
337         */
338        public Message getMessage(int msgNum) throws MessagingException {
339            // for the base, we just throw an exception.
340            throw new MethodNotSupportedException("Root NNTP folder does not contain messages");
341        }
342    
343        /**
344         * Append messages to a folder. NNTP folders are read only, so this is not
345         * supported.
346         * 
347         * @param msgs
348         *            The list of messages to append.
349         * 
350         * @exception MessagingException
351         */
352        public void appendMessages(Message[] msgs) throws MessagingException {
353            throw new MethodNotSupportedException("Root NNTP folder does not contain messages");
354    
355        }
356    
357        /**
358         * Expunge messages marked for deletion and return a list of the Messages.
359         * Not supported for NNTP.
360         * 
361         * @return Never returns.
362         * @exception MessagingException
363         */
364        public Message[] expunge() throws MessagingException {
365            throw new MethodNotSupportedException("Root NNTP folder does not contain messages");
366        }
367    
368        /**
369         * Below is a list of convenience methods that avoid repeated checking for a
370         * value and throwing an exception
371         */
372    
373        /** Ensure the folder is open */
374        protected void checkOpen() throws IllegalStateException {
375            if (!folderOpen) {
376                throw new IllegalStateException("Folder is not Open");
377            }
378        }
379    
380        /** Ensure the folder is not open */
381        protected void checkClosed() throws IllegalStateException {
382            if (folderOpen) {
383                throw new IllegalStateException("Folder is Open");
384            }
385        }
386    
387        /**
388         * @see javax.mail.Folder#notifyMessageChangedListeners(int,
389         *      javax.mail.Message)
390         * 
391         * this method is protected and cannot be used outside of Folder, therefore
392         * had to explicitly expose it via a method in NNTPFolder, so that
393         * NNTPMessage has access to it
394         * 
395         * Bad design on the part of the Java Mail API.
396         */
397        public void notifyMessageChangedListeners(int type, Message m) {
398            super.notifyMessageChangedListeners(type, m);
399        }
400    
401        /**
402         * Retrieve the subscribed status for a folder. This default implementation
403         * just returns false (which is true for the root folder).
404         * 
405         * @return Always returns true.
406         */
407        public boolean isSubscribed() {
408            return false;
409        }
410    
411        /**
412         * Set the subscribed status for a folder.
413         * 
414         * @param flag
415         *            The new subscribed status.
416         * 
417         * @exception MessagingException
418         */
419        public void setSubscribed(boolean flag) throws MessagingException {
420            throw new MessagingException("Root NNTP folder cannot be subscribed to");
421        }
422    
423        /**
424         * Test if a given article number is marked as SEEN.
425         * 
426         * @param article
427         *            The target article number.
428         * 
429         * @return The articles current seen status.
430         */
431        public boolean isSeen(int article) {
432            return false;
433        }
434    
435        /**
436         * Set the SEEN status for an article.
437         * 
438         * @param article
439         *            The target article.
440         * @param flag
441         *            The new seen setting.
442         * 
443         * @exception MessagingException
444         */
445        public void setSeen(int article, boolean flag) throws MessagingException {
446            throw new MessagingException("Root NNTP folder does not contain articles");
447        }
448    
449    }