You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by ri...@apache.org on 2006/06/14 12:18:55 UTC

svn commit: r414173 [2/7] - in /geronimo/javamail/trunk: ./ javamail-provider-1.3/ javamail-provider-1.3/src/ javamail-provider-1.3/src/java/ javamail-provider-1.3/src/java/org/ javamail-provider-1.3/src/java/org/apache/ javamail-provider-1.3/src/java/...

Added: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/NNTPGroupFolder.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/NNTPGroupFolder.java?rev=414173&view=auto
==============================================================================
--- geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/NNTPGroupFolder.java (added)
+++ geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/NNTPGroupFolder.java Wed Jun 14 03:18:51 2006
@@ -0,0 +1,384 @@
+/**
+ *
+ * Copyright 2003-2005 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.geronimo.javamail.store.nntp;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import javax.mail.FetchProfile;
+import javax.mail.FolderNotFoundException;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+
+import org.apache.geronimo.javamail.store.nntp.newsrc.NNTPNewsrcGroup;
+import org.apache.geronimo.javamail.transport.nntp.NNTPReply;
+
+/**
+ * The NNTP implementation of the javax.mail.Folder Note that only INBOX is
+ * supported in NNTP
+ * <p>
+ * <url>http://www.faqs.org/rfcs/rfc1939.html</url>
+ * </p>
+ * 
+ * @see javax.mail.Folder
+ * 
+ * @version $Rev$ $Date$
+ */
+public class NNTPGroupFolder extends NNTPFolder {
+
+    // holders for status information returned by the GROUP command.
+    protected int firstArticle = -1;
+
+    protected int lastArticle = -1;
+
+    // retrieved articles, mapped by article number.
+    Map articles;
+
+    // information stored in the newsrc group.
+    NNTPNewsrcGroup groupInfo;
+
+    /**
+     * Construct a "real" folder representing an NNTP news group.
+     * 
+     * @param parent
+     *            The parent root folder.
+     * @param store
+     *            The Store this folder is attached to.
+     * @param name
+     *            The folder name.
+     * @param groupInfo
+     *            The newsrc group information attached to the newsrc database.
+     *            This contains subscription and article "SEEN" information.
+     */
+    protected NNTPGroupFolder(NNTPRootFolder parent, NNTPStore store, String name, NNTPNewsrcGroup groupInfo) {
+        super(store);
+        // the name and the full name are the same.
+        this.name = name;
+        this.fullName = name;
+        // set the parent appropriately.
+        this.parent = parent = parent;
+        this.groupInfo = groupInfo;
+    }
+
+    /**
+     * Ping the server and update the group count, first, and last information.
+     * 
+     * @exception MessagingException
+     */
+    private void updateGroupStats() throws MessagingException {
+        // ask the server for information about the group. This is a one-line
+        // reponse with status on
+        // the group, if it exists.
+        NNTPReply reply = connection.sendCommand("GROUP " + name);
+
+        // explicitly not there?
+        if (reply.getCode() == NNTPReply.NO_SUCH_NEWSGROUP) {
+            throw new FolderNotFoundException(this, "Folder does not exist on server: " + reply);
+        } else if (reply.getCode() != NNTPReply.GROUP_SELECTED) {
+            throw new MessagingException("Error requesting group information: " + reply);
+        }
+
+        // we've gotten back a good response, now parse out the group specifics
+        // from the
+        // status response.
+
+        StringTokenizer tokenizer = new StringTokenizer(reply.getMessage());
+
+        // we should have a least 3 tokens here, in the order "count first
+        // last".
+
+        // article count
+        if (tokenizer.hasMoreTokens()) {
+            String count = tokenizer.nextToken();
+            try {
+                messageCount = Integer.parseInt(count);
+            } catch (NumberFormatException e) {
+                // ignore
+            }
+        }
+
+        // first article number
+        if (tokenizer.hasMoreTokens()) {
+            String first = tokenizer.nextToken();
+            try {
+                firstArticle = Integer.parseInt(first);
+            } catch (NumberFormatException e) {
+                // ignore
+            }
+        }
+
+        // last article number.
+        if (tokenizer.hasMoreTokens()) {
+            String last = tokenizer.nextToken();
+            try {
+                lastArticle = Integer.parseInt(last);
+            } catch (NumberFormatException e) {
+                // ignore
+            }
+        }
+    }
+
+    /**
+     * Test to see if this folder actually exists. This pings the server for
+     * information about the GROUP and updates the article count and index
+     * information.
+     * 
+     * @return true if the newsgroup exists on the server, false otherwise.
+     * @exception MessagingException
+     */
+    public boolean exists() throws MessagingException {
+
+        try {
+            // update the group statistics. If the folder doesn't exist, we'll
+            // get an exception that we
+            // can turn into a false reply.
+            updateGroupStats();
+            // updated ok, so it must be there.
+            return true;
+        } catch (FolderNotFoundException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Ping the NNTP server to check if a newsgroup has any new messages.
+     * 
+     * @return True if the server has new articles from the last time we
+     *         checked. Also returns true if this is the first time we've
+     *         checked.
+     * @exception MessagingException
+     */
+    public boolean hasNewMessages() throws MessagingException {
+        int oldLast = lastArticle;
+        updateGroupStats();
+
+        return lastArticle > oldLast;
+    }
+
+    /**
+     * Open the folder for use. This retrieves article count information from
+     * the server.
+     * 
+     * @exception MessagingException
+     */
+    public void openFolder() throws MessagingException {
+        // update the group specifics, especially the message count.
+        updateGroupStats();
+
+        // get a cache for retrieve articles
+        articles = new HashMap();
+    }
+
+    /**
+     * Close the folder, which also clears out the article caches.
+     * 
+     * @exception MessagingException
+     */
+    public void closeFolder() throws MessagingException {
+        // get ride of any retrieve articles, and flip over the open for
+        // business sign.
+        articles = null;
+    }
+
+    /**
+     * Checks wether the message is in cache, if not will create a new message
+     * object and return it.
+     * 
+     * @see javax.mail.Folder#getMessage(int)
+     */
+    public Message getMessage(int msgNum) throws MessagingException {
+        // Can only be performed on an Open folder
+        checkOpen();
+
+        // get an object form to look up in the retrieve messages list (oh how I
+        // wish there was
+        // something like Map that could use integer keys directly!).
+        Integer key = new Integer(msgNum);
+        NNTPMessage message = (NNTPMessage) articles.get(key);
+        if (message != null) {
+            // piece of cake!
+            return message;
+        }
+
+        // we need to suck a message down from the server.
+        // but first, make sure the group is still valid.
+        updateGroupStats();
+
+        // just send a STAT command to this message. Right now, all we want is
+        // existance proof. We'll
+        // retrieve the other bits when requested.
+        NNTPReply reply = connection.sendCommand("STAT " + Integer.toString(msgNum));
+        if (reply.getCode() != NNTPReply.REQUEST_TEXT_SEPARATELY) {
+            throw new MessagingException("Error retrieving article from NNTP server: " + reply);
+        }
+
+        // we need to parse out the message id.
+        String response = reply.getMessage();
+
+        int idStart = response.indexOf('<');
+        int idEnd = response.indexOf('>');
+
+        message = new NNTPMessage(this, (NNTPStore) store, msgNum, response.substring(idStart + 1, idEnd));
+
+        // add this to the article cache.
+        articles.put(key, message);
+
+        return message;
+    }
+
+    /**
+     * Retrieve all articles in the group.
+     * 
+     * @return An array of all messages in the group.
+     */
+    public Message[] getMessages() throws MessagingException {
+        // we're going to try first with XHDR, which will allow us to retrieve
+        // everything in one shot. If that
+        // fails, we'll fall back on issing STAT commands for the entire article
+        // range.
+        NNTPReply reply = connection.sendCommand("XHDR Message-ID " + Integer.toString(firstArticle) + "-"
+                + Integer.toString(lastArticle), NNTPReply.HEAD_FOLLOWS);
+
+        List messages = new ArrayList();
+
+        if (reply.getCode() == NNTPReply.HEAD_FOLLOWS) {
+            List lines = reply.getData();
+
+            for (int i = 0; i < lines.size(); i++) {
+                String line = (String) lines.get(i);
+
+                try {
+                    int pos = line.indexOf(' ');
+                    int articleID = Integer.parseInt(line.substring(0, pos));
+                    String messageID = line.substring(pos + 1);
+                    Integer key = new Integer(articleID);
+                    // see if we have this message cached, If not, create it.
+                    Message message = (Message) articles.get(key);
+                    if (message == null) {
+                        message = new NNTPMessage(this, (NNTPStore) store, key.intValue(), messageID);
+                        articles.put(key, message);
+                    }
+
+                    messages.add(message);
+
+                } catch (NumberFormatException e) {
+                    // should never happen, but just skip this entry if it does.
+                }
+            }
+        } else {
+            // grumble, we need to stat each article id to see if it
+            // exists....lots of round trips.
+            for (int i = firstArticle; i <= lastArticle; i++) {
+                try {
+                    messages.add(getMessage(i));
+                } catch (MessagingException e) {
+                    // just assume if there is an error, it's because the
+                    // message id doesn't exist.
+                }
+            }
+        }
+
+        return (Message[]) messages.toArray(new Message[0]);
+    }
+
+    /**
+     * @see javax.mail.Folder#fetch(javax.mail.Message[],
+     *      javax.mail.FetchProfile)
+     * 
+     * The JavaMail API recommends that this method be overrident to provide a
+     * meaningfull implementation.
+     */
+    public void fetch(Message[] msgs, FetchProfile fp) throws MessagingException {
+        // Can only be performed on an Open folder
+        checkOpen();
+
+        for (int i = 0; i < msgs.length; i++) {
+            Message msg = msgs[i];
+            // we can only perform this operation for NNTPMessages.
+            if (msg == null || !(msg instanceof NNTPMessage)) {
+                // we can't fetch if it's the wrong message type
+                continue;
+            }
+
+            // fetching both the headers and body?
+            if (fp.contains(FetchProfile.Item.ENVELOPE) && fp.contains(FetchProfile.Item.CONTENT_INFO)) {
+
+                // retrive everything
+                ((NNTPMessage) msg).loadArticle();
+            }
+            // headers only?
+            else if (fp.contains(FetchProfile.Item.ENVELOPE)) {
+                ((NNTPMessage) msg).loadHeaders();
+            } else if (fp.contains(FetchProfile.Item.CONTENT_INFO)) {
+                ((NNTPMessage) msg).loadContent();
+            }
+        }
+    }
+
+    /**
+     * Return the subscription status of this folder.
+     * 
+     * @return true if the folder is marked as subscribed, false for
+     *         unsubscribed.
+     */
+    public boolean isSubscribed() {
+        return groupInfo.isSubscribed();
+    }
+
+    /**
+     * Set or clear the subscription status of a file.
+     * 
+     * @param flag
+     *            The new subscription state.
+     */
+    public void setSubscribed(boolean flag) {
+        groupInfo.setSubscribed(flag);
+    }
+
+    /**
+     * Return the "seen" state for an article in a folder.
+     * 
+     * @param article
+     *            The article number.
+     * 
+     * @return true if the article is marked as seen in the newsrc file, false
+     *         for unseen files.
+     */
+    public boolean isSeen(int article) {
+        return groupInfo.isArticleSeen(article);
+    }
+
+    /**
+     * Set the seen state for an article in a folder.
+     * 
+     * @param article
+     *            The article number.
+     * @param flag
+     *            The new seen state.
+     */
+    public void setSeen(int article, boolean flag) {
+        if (flag) {
+            groupInfo.markArticleSeen(article);
+        } else {
+            groupInfo.markArticleUnseen(article);
+        }
+    }
+}

Propchange: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/NNTPGroupFolder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/NNTPGroupFolder.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/NNTPGroupFolder.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/NNTPMessage.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/NNTPMessage.java?rev=414173&view=auto
==============================================================================
--- geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/NNTPMessage.java (added)
+++ geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/NNTPMessage.java Wed Jun 14 03:18:51 2006
@@ -0,0 +1,375 @@
+/**
+ *
+ * Copyright 2006 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.geronimo.javamail.store.nntp;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+
+import javax.mail.Flags;
+import javax.mail.IllegalWriteException;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.internet.InternetHeaders;
+import javax.mail.internet.MimeMessage;
+
+import org.apache.geronimo.javamail.transport.nntp.NNTPConnection;
+import org.apache.geronimo.javamail.transport.nntp.NNTPReply;
+import org.apache.geronimo.javamail.transport.nntp.StringListInputStream;
+
+/**
+ * NNTP implementation of javax.mail.internet.MimeMessage
+ * 
+ * Only the most basic information is given and Message objects created here is
+ * a light-weight reference to the actual Message As per the JavaMail spec items
+ * from the actual message will get filled up on demand
+ * 
+ * If some other items are obtained from the server as a result of one call,
+ * then the other details are also processed and filled in. For ex if RETR is
+ * called then header information will also be processed in addition to the
+ * content
+ * 
+ * @version $Rev$ $Date$
+ */
+public class NNTPMessage extends MimeMessage {
+    // the server message identifer
+    String messageID = null;
+
+    // our attached session
+    protected Session session;
+
+    // the Store we're stored in (which manages the connection and other stuff).
+    protected NNTPStore store;
+
+    // our active connection.
+    protected NNTPConnection connection;
+
+    // used to force loading of headers
+    protected boolean headersLoaded = false;
+
+    // use to force content loading
+    protected boolean contentLoaded = false;
+
+    /**
+     * Contruct an NNTPMessage instance.
+     * 
+     * @param folder
+     *            The hosting folder for the message.
+     * @param store
+     *            The Store owning the article (and folder).
+     * @param msgnum
+     *            The article message number.
+     * @param messageID
+     *            The article messageID (as assigned by the server).
+     * 
+     * @exception MessagingException
+     */
+    NNTPMessage(NNTPFolder folder, NNTPStore store, int msgnum, String messageID) throws MessagingException {
+        super(folder, msgnum);
+        this.messageID = messageID;
+        this.store = store;
+        this.session = ((NNTPStore) store).getSession();
+        // get the active connection from the store...all commands are sent
+        // there
+        this.connection = ((NNTPStore) store).getConnection();
+
+        // get our flag set from the folder.
+        flags = folder.getPermanentFlags();
+        // now check our initial SEEN state and set the flags appropriately
+        if (folder.isSeen(msgnum)) {
+            flags.add(Flags.Flag.SEEN);
+        } else {
+            flags.remove(Flags.Flag.SEEN);
+        }
+    }
+
+    /**
+     * Retrieve the size of the message content. The content will be retrieved
+     * from the server, if necessary.
+     * 
+     * @return The size of the content.
+     * @exception MessagingException
+     */
+    public int getSize() throws MessagingException {
+        // make sure we've retrieved the message content and continue with the
+        // superclass version.
+        loadContent();
+        return super.getSize();
+    }
+
+    /**
+     * Get a line count for the NNTP message. This is potentially stored in the
+     * Lines article header. If not there, we return a default of -1.
+     * 
+     * @return The header line count estimate, or -1 if not retrieveable.
+     * @exception MessagingException
+     */
+    public int getLineCount() throws MessagingException {
+        String[] headers = getHeader("Lines");
+
+        // hopefully, there's only a single one of these. No sensible way of
+        // interpreting
+        // multiples.
+        if (headers.length == 1) {
+            try {
+                return Integer.parseInt(headers[0].trim());
+
+            } catch (NumberFormatException e) {
+                // ignore
+            }
+        }
+        // dunno...and let them know I don't know.
+        return -1;
+    }
+
+    /**
+     * @see javax.mail.internet.MimeMessage#getContentStream()
+     */
+    protected InputStream getContentStream() throws MessagingException {
+        // get the article information.
+        loadArticle();
+        return super.getContentStream();
+    }
+
+    /***************************************************************************
+     * Following is a set of methods that deal with headers These methods are
+     * just overrides on the superclass methods to allow lazy loading of the
+     * header information.
+     **************************************************************************/
+
+    public String[] getHeader(String name) throws MessagingException {
+        loadHeaders();
+        return headers.getHeader(name);
+    }
+
+    public String getHeader(String name, String delimiter) throws MessagingException {
+        loadHeaders();
+        return headers.getHeader(name, delimiter);
+    }
+
+    public Enumeration getAllHeaders() throws MessagingException {
+        loadHeaders();
+        return headers.getAllHeaders();
+    }
+
+    public Enumeration getMatchingHeaders(String[] names) throws MessagingException {
+        loadHeaders();
+        return headers.getMatchingHeaders(names);
+    }
+
+    public Enumeration getNonMatchingHeaders(String[] names) throws MessagingException {
+        loadHeaders();
+        return headers.getNonMatchingHeaders(names);
+    }
+
+    public Enumeration getAllHeaderLines() throws MessagingException {
+        loadHeaders();
+        return headers.getAllHeaderLines();
+    }
+
+    public Enumeration getMatchingHeaderLines(String[] names) throws MessagingException {
+        loadHeaders();
+        return headers.getMatchingHeaderLines(names);
+    }
+
+    public Enumeration getNonMatchingHeaderLines(String[] names) throws MessagingException {
+        loadHeaders();
+        return headers.getNonMatchingHeaderLines(names);
+    }
+
+    // the following are overrides for header modification methods. These
+    // messages are read only,
+    // so the headers cannot be modified.
+    public void addHeader(String name, String value) throws MessagingException {
+        throw new IllegalWriteException("NNTP messages are read-only");
+    }
+
+    public void setHeader(String name, String value) throws MessagingException {
+        throw new IllegalWriteException("NNTP messages are read-only");
+    }
+
+    public void removeHeader(String name) throws MessagingException {
+        throw new IllegalWriteException("NNTP messages are read-only");
+    }
+
+    public void addHeaderLine(String line) throws MessagingException {
+        throw new IllegalWriteException("IMAP messages are read-only");
+    }
+
+    /**
+     * We cannot modify these messages
+     */
+    public void saveChanges() throws MessagingException {
+        throw new IllegalWriteException("NNTP messages are read-only");
+    }
+
+    /**
+     * Retrieve the message headers from the NNTP server.
+     * 
+     * @exception MessagingException
+     */
+    public void loadHeaders() throws MessagingException {
+        // don't retrieve if already loaded.
+        if (headersLoaded) {
+            return;
+        }
+
+        NNTPReply reply = connection.sendCommand("HEAD " + messageID, NNTPReply.HEAD_FOLLOWS);
+
+        if (reply.getCode() == NNTPReply.HEAD_FOLLOWS) {
+            try {
+                // wrap a stream around the reply data and read as headers.
+                updateHeaders(new StringListInputStream(reply.getData()));
+            } catch (IOException e) {
+                throw new MessagingException("Error retrieving article headers from server", e);
+            }
+        } else {
+            throw new MessagingException("Error retrieving article headers from server: " + reply);
+        }
+    }
+
+    /**
+     * Update the message headers from an input stream.
+     * 
+     * @param in
+     *            The InputStream source for the header information.
+     * 
+     * @exception MessagingException
+     */
+    public void updateHeaders(InputStream in) throws MessagingException {
+        // wrap a stream around the reply data and read as headers.
+        headers = new InternetHeaders(in);
+        headersLoaded = true;
+    }
+
+    /**
+     * Load just the message content from the NNTP server.
+     * 
+     * @exception MessagingException
+     */
+    public void loadContent() throws MessagingException {
+        if (contentLoaded) {
+            return;
+        }
+
+        NNTPReply reply = connection.sendCommand("BODY " + messageID, NNTPReply.BODY_FOLLOWS);
+
+        if (reply.getCode() == NNTPReply.BODY_FOLLOWS) {
+            try {
+                InputStream in = new StringListInputStream(reply.getData());
+                updateContent(in);
+            } catch (IOException e) {
+                throw new MessagingException("Error retrieving article body from server", e);
+            }
+        } else {
+            throw new MessagingException("Error retrieving article body from server: " + reply);
+        }
+    }
+
+    /**
+     * Load the entire article from the NNTP server. This updates both the
+     * headers and the content.
+     * 
+     * @exception MessagingException
+     */
+    public void loadArticle() throws MessagingException {
+        // if the headers are already loaded, retrieve the content portion.
+        if (headersLoaded) {
+            loadContent();
+            return;
+        }
+
+        // we need to retrieve everything.
+        NNTPReply reply = connection.sendCommand("ARTICLE " + messageID, NNTPReply.ARTICLE_FOLLOWS);
+
+        if (reply.getCode() == NNTPReply.ARTICLE_FOLLOWS) {
+            try {
+                InputStream in = new StringListInputStream(reply.getData());
+                // update both the headers and the content.
+                updateHeaders(in);
+                updateContent(in);
+            } catch (IOException e) {
+                throw new MessagingException("Error retrieving article from server", e);
+            }
+        } else {
+            throw new MessagingException("Error retrieving article from server: " + reply);
+        }
+    }
+
+    /**
+     * Update the article content from an input stream.
+     * 
+     * @param in
+     *            The content data source.
+     * 
+     * @exception MessagingException
+     */
+    public void updateContent(InputStream in) throws MessagingException {
+        try {
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+            byte[] buffer = new byte[4096];
+
+            // copy the content data from the stream into a byte buffer for the
+            // content.
+            while (true) {
+                int read = in.read(buffer);
+                if (read == -1) {
+                    break;
+                }
+                out.write(buffer, 0, read);
+            }
+
+            content = out.toByteArray();
+            contentLoaded = true;
+        } catch (IOException e) {
+            throw new MessagingException("Error retrieving message body from server", e);
+        }
+    }
+
+    /**
+     * Get the server assigned messageid for the article.
+     * 
+     * @return The server assigned message id.
+     */
+    public String getMessageId() {
+        return messageID;
+    }
+
+    /**
+     * Override of setFlags(). We need to ensure that if the SEEN flag is set or
+     * cleared, that the newsrc file correctly reflects the current state.
+     * 
+     * @param flag
+     *            The flag being set.
+     * @param newvalue
+     *            The new flag value.
+     * 
+     * @exception MessagingException
+     */
+    public void setFlags(Flags flag, boolean newvalue) throws MessagingException {
+        // if this is the SEEN flag, make sure we shadow this in the newsrc
+        // file.
+        if (flag.contains(Flags.Flag.SEEN)) {
+            ((NNTPFolder) folder).setSeen(msgnum, newvalue);
+        }
+        // have the superclass do the real flag setting.
+        super.setFlags(flag, newvalue);
+    }
+}

Propchange: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/NNTPMessage.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/NNTPMessage.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/NNTPMessage.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/NNTPRootFolder.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/NNTPRootFolder.java?rev=414173&view=auto
==============================================================================
--- geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/NNTPRootFolder.java (added)
+++ geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/NNTPRootFolder.java Wed Jun 14 03:18:51 2006
@@ -0,0 +1,397 @@
+/**
+ *
+ * Copyright 2003-2005 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.geronimo.javamail.store.nntp;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.mail.Folder;
+import javax.mail.MessagingException;
+
+import org.apache.geronimo.javamail.store.nntp.newsrc.NNTPNewsrcGroup;
+import org.apache.geronimo.javamail.transport.nntp.NNTPReply;
+import org.apache.geronimo.mail.util.SessionUtil;
+
+/**
+ * The base NNTP implementation of the javax.mail.Folder This is a base class
+ * for both the Root NNTP server and each NNTP group folder.
+ * 
+ * @see javax.mail.Folder
+ * 
+ * @version $Rev$
+ */
+public class NNTPRootFolder extends NNTPFolder {
+    protected static final String NNTP_LISTALL = "mail.nntp.listall";
+
+    /**
+     * Construct the NNTPRootFolder.
+     * 
+     * @param store
+     *            The owning Store.
+     * @param name
+     *            The folder name (by default, this is the server host name).
+     * @param fullName
+     *            The fullName to use for this server (derived from welcome
+     *            string).
+     */
+    protected NNTPRootFolder(NNTPStore store, String name, String fullName) {
+        super(store);
+
+        this.name = name;
+        this.fullName = fullName;
+    }
+
+    /**
+     * List the subfolders. For group folders, this is a meaningless so we throw
+     * a MethodNotSupportedException.
+     * 
+     * @param pattern
+     *            The folder pattern string.
+     * 
+     * @return Never returns.
+     * @exception MessagingException
+     */
+    public synchronized Folder[] list(String pattern) throws MessagingException {
+        // the pattern specfied for javamail uses two wild card characters, "%"
+        // and "*". The "%" matches
+        // and character except hierarchy separators. Since we have a flag
+        // hierarchy, "%" and "*" are
+        // essentially the same. If we convert the "%" into "*", we can just
+        // treat this as a wildmat
+        // formatted pattern and pass this on to the server rather than having
+        // to read everything and
+        // process the strings on the client side.
+
+        pattern = pattern.replace('%', '*');
+
+        // if we're not supposed to list everything, then just filter the list
+        // of subscribed groups.
+        if (SessionUtil.getBooleanProperty(NNTP_LISTALL, false)) {
+            return filterActiveGroups(pattern);
+        } else {
+            return filterSubscribedGroups(pattern);
+        }
+    }
+
+    /**
+     * Retrieve the list of subscribed folders that match the given pattern
+     * string.
+     * 
+     * @param pattern
+     *            The pattern string used for the matching
+     * 
+     * @return An array of matching folders from the subscribed list.
+     */
+    public Folder[] listSubscribed(String pattern) throws MessagingException {
+        // the pattern specfied for javamail uses two wild card characters, "%"
+        // and "*". The "%" matches
+        // and character except hierarchy separators. Since we have a flag
+        // hierarchy, "%" and "*" are
+        // essentially the same. If we convert the "%" into "*", we can just
+        // treat this as a wildmat
+        // formatted pattern and pass this on to the server rather than having
+        // to read everything and
+        // process the strings on the client side.
+
+        pattern = pattern.replace('%', '*');
+
+        return filterSubscribedGroups(pattern);
+    }
+
+    /**
+     * Retrieve the list of matching groups from the NNTP server using the LIST
+     * ACTIVE command. The server does the wildcard matching for us.
+     * 
+     * @param pattern
+     *            The pattern string (in wildmat format) used to match.
+     * 
+     * @return An array of folders for the matching groups.
+     */
+    protected Folder[] filterActiveGroups(String pattern) throws MessagingException {
+        NNTPReply reply = connection.sendCommand("LIST ACTIVE " + pattern, NNTPReply.LIST_FOLLOWS);
+
+        // if the LIST ACTIVE command isn't supported,
+        if (reply.getCode() == NNTPReply.COMMAND_NOT_RECOGNIZED) {
+            // only way to list all is to retrieve all and filter.
+            return filterAllGroups(pattern);
+        } else if (reply.getCode() != NNTPReply.LIST_FOLLOWS) {
+            throw new MessagingException("Error retrieving group list from NNTP server: " + reply);
+        }
+
+        // get the response back from the server and process each returned group
+        // name.
+        List groups = reply.getData();
+
+        Folder[] folders = new Folder[groups.size()];
+        for (int i = 0; i < groups.size(); i++) {
+            folders[i] = getFolder(getGroupName((String) groups.get(i)));
+        }
+        return folders;
+    }
+
+    /**
+     * Retrieve a list of all groups from the server and filter on the names.
+     * Not recommended for the usenet servers, as there are over 30000 groups to
+     * process.
+     * 
+     * @param pattern
+     *            The pattern string used for the selection.
+     * 
+     * @return The Folders for the matching groups.
+     */
+    protected Folder[] filterAllGroups(String pattern) throws MessagingException {
+        NNTPReply reply = connection.sendCommand("LIST", NNTPReply.LIST_FOLLOWS);
+
+        if (reply.getCode() != NNTPReply.LIST_FOLLOWS) {
+            throw new MessagingException("Error retrieving group list from NNTP server: " + reply);
+        }
+
+        // get the response back from the server and process each returned group
+        // name.
+        List groups = reply.getData();
+
+        WildmatMatcher matcher = new WildmatMatcher(pattern);
+
+        List folders = new ArrayList();
+        for (int i = 0; i < groups.size(); i++) {
+            String name = getGroupName((String) groups.get(i));
+            // does this match our pattern? Add to the list
+            if (matcher.matches(name)) {
+                folders.add(getFolder(name));
+            }
+        }
+        return (Folder[]) folders.toArray(new Folder[0]);
+    }
+
+    /**
+     * Return the set of groups from the newsrc subscribed groups list that
+     * match a given filter.
+     * 
+     * @param pattern
+     *            The selection pattern.
+     * 
+     * @return The Folders for the matching groups.
+     */
+    protected Folder[] filterSubscribedGroups(String pattern) throws MessagingException {
+        Iterator groups = ((NNTPStore) store).getNewsrcGroups();
+
+        WildmatMatcher matcher = new WildmatMatcher(pattern);
+
+        List folders = new ArrayList();
+        while (groups.hasNext()) {
+            NNTPNewsrcGroup group = (NNTPNewsrcGroup) groups.next();
+            if (group.isSubscribed()) {
+                // does this match our pattern? Add to the list
+                if (matcher.matches(group.getName())) {
+                    folders.add(getFolder(group.getName()));
+                }
+            }
+        }
+        return (Folder[]) folders.toArray(new Folder[0]);
+    }
+
+    /**
+     * Utility method for extracting a name from a group list response.
+     * 
+     * @param response
+     *            The response string.
+     * 
+     * @return The group name.
+     */
+    protected String getGroupName(String response) {
+        int blank = response.indexOf(' ');
+        return response.substring(0, blank).trim();
+    }
+
+    /**
+     * Return whether this folder can hold just messages or also subfolders.
+     * Only the root folder can hold other folders, so it will need to override.
+     * 
+     * @return Always returns Folder.HOLDS_FOLDERS.
+     * @exception MessagingException
+     */
+    public int getType() throws MessagingException {
+        return HOLDS_FOLDERS;
+    }
+
+    /**
+     * Get a new folder from the root folder. This creates a new folder, which
+     * might not actually exist on the server. If the folder doesn't exist, an
+     * error will occur on folder open.
+     * 
+     * @param name
+     *            The name of the requested folder.
+     * 
+     * @return A new folder object for this folder.
+     * @exception MessagingException
+     */
+    public Folder getFolder(String name) throws MessagingException {
+        // create a new group folder and return
+        return new NNTPGroupFolder(this, (NNTPStore) store, name, ((NNTPStore) store).getNewsrcGroup(name));
+    }
+
+    /**
+     * Utility class to do Wildmat pattern matching on folder names.
+     */
+    class WildmatMatcher {
+        // middle match sections...because these are separated by wildcards, if
+        // they appear in
+        // sequence in the string, it is a match.
+        List matchSections = new ArrayList();
+
+        // just a "*" match, so everything is true
+        boolean matchAny = false;
+
+        // no wildcards, so this must be an exact match.
+        String exactMatch = null;
+
+        // a leading section which must be at the beginning
+        String firstSection = null;
+
+        // a trailing section which must be at the end of the string.
+        String lastSection = null;
+
+        /**
+         * Create a wildmat pattern matcher.
+         * 
+         * @param pattern
+         *            The wildmat pattern to apply to string matches.
+         */
+        public WildmatMatcher(String pattern) {
+            int section = 0;
+
+            // handle the easy cases first
+
+            // single wild card?
+            if (pattern.equals("*")) {
+                matchAny = true;
+                return;
+            }
+
+            // find the first wild card
+            int wildcard = pattern.indexOf('*');
+
+            // no wild card at all?
+            if (wildcard == -1) {
+                exactMatch = pattern;
+                return;
+            }
+
+            // pattern not begin with a wildcard? We need to pull off the
+            // leading section
+            if (!pattern.startsWith("*")) {
+                firstSection = pattern.substring(0, wildcard);
+                section = wildcard + 1;
+                // this could be "yada*", so we could be done.
+                if (section >= pattern.length()) {
+                    return;
+                }
+            }
+
+            // now parse off the middle sections, making sure to handle the end
+            // condition correctly.
+            while (section < pattern.length()) {
+                // find the next wildcard position
+                wildcard = pattern.indexOf('*', section);
+                if (wildcard == -1) {
+                    // not found, we're at the end of the pattern. We need to
+                    // match on the end.
+                    lastSection = pattern.substring(section);
+                    return;
+                }
+                // we could have a null section, which we'll just ignore.
+                else if (wildcard == section) {
+                    // step over the wild card
+                    section++;
+                } else {
+                    // pluck off the next section
+                    matchSections.add(pattern.substring(section, wildcard));
+                    // step over the wild card character and check if we've
+                    // reached the end.
+                    section = wildcard + 1;
+                }
+            }
+        }
+
+        /**
+         * Test if a name string matches to parsed wildmat pattern.
+         * 
+         * @param name
+         *            The name to test.
+         * 
+         * @return true if the string matches the pattern, false otherwise.
+         */
+        public boolean matches(String name) {
+
+            // handle the easy cases first
+
+            // full wildcard? Always matches
+            if (matchAny) {
+                return true;
+            }
+
+            // required exact matches are easy.
+            if (exactMatch != null) {
+                return exactMatch.equals(name);
+            }
+
+            int span = 0;
+
+            // must match the beginning?
+            if (firstSection != null) {
+                // if it doesn't start with that, it can't be true.
+                if (!name.startsWith(firstSection)) {
+                    return false;
+                }
+
+                // we do all additional matching activity from here.
+                span = firstSection.length();
+            }
+
+            // scan for each of the sections along the string
+            for (int i = 1; i < matchSections.size(); i++) {
+                // if a section is not found, this is false
+
+                String nextMatch = (String) matchSections.get(i);
+                int nextLocation = name.indexOf(nextMatch, span);
+                if (nextLocation == -1) {
+                    return false;
+                }
+                // step over that one
+                span = nextMatch.length() + nextLocation;
+            }
+
+            // we've matched everything up to this point, now check to see if
+            // need an end match
+            if (lastSection != null) {
+                // we need to have at least the number of characters of the end
+                // string left, else this fails.
+                if (name.length() - span < lastSection.length()) {
+                    return false;
+                }
+
+                // ok, make sure we end with this string
+                return name.endsWith(lastSection);
+            }
+
+            // no falsies, this must be the truth.
+            return true;
+        }
+    }
+}

Propchange: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/NNTPRootFolder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/NNTPRootFolder.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/NNTPRootFolder.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/NNTPStore.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/NNTPStore.java?rev=414173&view=auto
==============================================================================
--- geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/NNTPStore.java (added)
+++ geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/NNTPStore.java Wed Jun 14 03:18:51 2006
@@ -0,0 +1,343 @@
+/**
+ *
+ * Copyright 2006 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.geronimo.javamail.store.nntp;
+
+import java.io.File;
+import java.io.PrintStream;
+import java.util.Iterator;
+
+import javax.mail.Folder;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.Store;
+import javax.mail.URLName;
+
+import org.apache.geronimo.javamail.store.nntp.newsrc.NNTPNewsrc;
+import org.apache.geronimo.javamail.store.nntp.newsrc.NNTPNewsrcFile;
+import org.apache.geronimo.javamail.store.nntp.newsrc.NNTPNewsrcGroup;
+import org.apache.geronimo.javamail.transport.nntp.NNTPConnection;
+import org.apache.geronimo.mail.util.SessionUtil;
+
+/**
+ * NNTP implementation of javax.mail.Store POP protocol spec is implemented in
+ * org.apache.geronimo.javamail.store.pop3.NNTPConnection
+ * 
+ * @version $Rev$ $Date$
+ */
+public class NNTPStore extends Store {
+
+    protected static final String NNTP_AUTH = "auth";
+
+    protected static final String NNTP_PORT = "port";
+
+    protected static final String NNTP_NEWSRC = "newsrc";
+
+    protected static final String protocol = "nntp";
+
+    protected static final int DEFAULT_NNTP_PORT = 119;
+
+    // the active connection object.
+    protected NNTPConnection connection;
+
+    // the newsrc file where we store subscriptions and seen message markers.
+    protected NNTPNewsrc newsrc;
+
+    // the root folder
+    protected NNTPRootFolder root;
+
+    // our session provided debug output stream.
+    protected PrintStream debugStream;
+
+    /**
+     * Construct an NNTPStore item. This will load the .newsrc file associated
+     * with the server.
+     * 
+     * @param session
+     *            The owning javamail Session.
+     * @param urlName
+     *            The Store urlName, which can contain server target
+     *            information.
+     */
+    public NNTPStore(Session session, URLName urlName) {
+        super(session, urlName);
+
+        // get our debug output.
+        debugStream = session.getDebugOut();
+
+    }
+
+    /**
+     * @see javax.mail.Store#getDefaultFolder()
+     * 
+     * This returns a root folder object for all of the news groups.
+     */
+    public Folder getDefaultFolder() throws MessagingException {
+        checkConnectionStatus();
+        if (root == null) {
+            return new NNTPRootFolder(this, connection.getHost(), connection.getWelcomeString());
+        }
+        return root;
+    }
+
+    /**
+     * @see javax.mail.Store#getFolder(java.lang.String)
+     */
+    public Folder getFolder(String name) throws MessagingException {
+        return getDefaultFolder().getFolder(name);
+    }
+
+    /**
+     * 
+     * @see javax.mail.Store#getFolder(javax.mail.URLName)
+     */
+    public Folder getFolder(URLName url) throws MessagingException {
+        return getDefaultFolder().getFolder(url.getFile());
+    }
+
+    /**
+     * @see javax.mail.Service#protocolConnect(java.lang.String, int,
+     *      java.lang.String, java.lang.String)
+     */
+    protected synchronized boolean protocolConnect(String host, int port, String username, String password)
+            throws MessagingException {
+        if (debug) {
+            debugOut("Connecting to server " + host + ":" + port + " for user " + username);
+        }
+
+        // first check to see if we need to authenticate. If we need this, then
+        // we must have a username and
+        // password specified. Failing this may result in a user prompt to
+        // collect the information.
+        boolean mustAuthenticate = getBooleanProperty(NNTP_AUTH, false);
+
+        // if we need to authenticate, and we don't have both a userid and
+        // password, then we fail this
+        // immediately. The Service.connect() method will try to obtain the user
+        // information and retry the
+        // connection one time.
+        if (mustAuthenticate && (username == null || password == null)) {
+            return false;
+        }
+
+        // if the port is defaulted, then see if we have something configured in
+        // the session.
+        // if not configured, we just use the default default.
+        if (port == -1) {
+            // check for a property and fall back on the default if it's not
+            // set.
+            port = getIntProperty(NNTP_PORT, DEFAULT_NNTP_PORT);
+        }
+
+        // create socket and connect to server.
+        connection = new NNTPConnection(protocol, session, host, port, username, password, debug);
+        connection.connect();
+
+        // see if we have a newsrc file location specified
+        String newsrcFile = getProperty(NNTP_NEWSRC);
+
+        File source = null;
+
+        // not given as a property? Then look for a file in user.home
+        if (newsrcFile != null) {
+            source = new File(newsrcFile);
+        } else {
+            // ok, look for a file in the user.home directory. If possible,
+            // we'll try for a file
+            // with the hostname appended.
+            String home = SessionUtil.getProperty("user.home");
+
+            // try for a host-specific file first. If not found, use (and
+            // potentially create) a generic
+            // .newsrc file.
+            newsrcFile = ".newsrc-" + host;
+            source = new File(home, newsrcFile);
+            if (!source.exists()) {
+                source = new File(home, ".newsrc");
+            }
+        }
+
+        // now create a newsrc read and load the file.
+        newsrc = new NNTPNewsrcFile(source);
+        newsrc.load();
+
+        // we're going to return success here, but in truth, the server may end
+        // up asking for our
+        // bonafides at any time, and we'll be expected to authenticate then.
+        return true;
+    }
+
+    /**
+     * @see javax.mail.Service#close()
+     */
+    public void close() throws MessagingException {
+        // This is done to ensure proper event notification.
+        super.close();
+        // persist the newsrc file, if possible
+        newsrc.close();
+        connection.close();
+        connection = null;
+    }
+
+    private void checkConnectionStatus() throws MessagingException {
+        if (!this.isConnected()) {
+            throw new MessagingException("Not connected ");
+        }
+    }
+
+    /**
+     * Internal debug output routine.
+     * 
+     * @param value
+     *            The string value to output.
+     */
+    void debugOut(String message) {
+        debugStream.println("NNTPTransport DEBUG: " + message);
+    }
+
+    /**
+     * Internal debugging routine for reporting exceptions.
+     * 
+     * @param message
+     *            A message associated with the exception context.
+     * @param e
+     *            The received exception.
+     */
+    void debugOut(String message, Throwable e) {
+        debugOut("Received exception -> " + message);
+        debugOut("Exception message -> " + e.getMessage());
+        e.printStackTrace(debugStream);
+    }
+
+    /**
+     * Retrieve the server connection created by this store.
+     * 
+     * @return The active connection object.
+     */
+    NNTPConnection getConnection() {
+        return connection;
+    }
+
+    /**
+     * Retrieve the Session object this Store is operating under.
+     * 
+     * @return The attached Session instance.
+     */
+    Session getSession() {
+        return session;
+    }
+
+    /**
+     * Retrieve all of the groups we nave persistent store information about.
+     * 
+     * @return The set of groups contained in the newsrc file.
+     */
+    Iterator getNewsrcGroups() {
+        return newsrc.getGroups();
+    }
+
+    /**
+     * Retrieve the newsrc group information for a named group. If the file does
+     * not currently include this group, an unsubscribed group will be added to
+     * the file.
+     * 
+     * @param name
+     *            The name of the target group.
+     * 
+     * @return The NNTPNewsrcGroup item corresponding to this name.
+     */
+    NNTPNewsrcGroup getNewsrcGroup(String name) {
+        return newsrc.getGroup(name);
+    }
+
+    /**
+     * Get a property associated with this mail protocol.
+     * 
+     * @param name
+     *            The name of the property.
+     * 
+     * @return The property value (returns null if the property has not been
+     *         set).
+     */
+    String getProperty(String name) {
+        // the name we're given is the least qualified part of the name. We
+        // construct the full property name
+        // using the protocol (either "nntp" or "nntp-post").
+        String fullName = "mail." + protocol + "." + name;
+        return session.getProperty(fullName);
+    }
+
+    /**
+     * Get a property associated with this mail session. Returns the provided
+     * default if it doesn't exist.
+     * 
+     * @param name
+     *            The name of the property.
+     * @param defaultValue
+     *            The default value to return if the property doesn't exist.
+     * 
+     * @return The property value (returns defaultValue if the property has not
+     *         been set).
+     */
+    String getProperty(String name, String defaultValue) {
+        // the name we're given is the least qualified part of the name. We
+        // construct the full property name
+        // using the protocol (either "nntp" or "nntp-post").
+        String fullName = "mail." + protocol + "." + name;
+        return SessionUtil.getProperty(session, fullName, defaultValue);
+    }
+
+    /**
+     * Get a property associated with this mail session as an integer value.
+     * Returns the default value if the property doesn't exist or it doesn't
+     * have a valid int value.
+     * 
+     * @param name
+     *            The name of the property.
+     * @param defaultValue
+     *            The default value to return if the property doesn't exist.
+     * 
+     * @return The property value converted to an int.
+     */
+    int getIntProperty(String name, int defaultValue) {
+        // the name we're given is the least qualified part of the name. We
+        // construct the full property name
+        // using the protocol (either "nntp" or "nntp-post").
+        String fullName = "mail." + protocol + "." + name;
+        return SessionUtil.getIntProperty(session, fullName, defaultValue);
+    }
+
+    /**
+     * Get a property associated with this mail session as an boolean value.
+     * Returns the default value if the property doesn't exist or it doesn't
+     * have a valid int value.
+     * 
+     * @param name
+     *            The name of the property.
+     * @param defaultValue
+     *            The default value to return if the property doesn't exist.
+     * 
+     * @return The property value converted to a boolean
+     */
+    boolean getBooleanProperty(String name, boolean defaultValue) {
+        // the name we're given is the least qualified part of the name. We
+        // construct the full property name
+        // using the protocol (either "nntp" or "nntp-post").
+        String fullName = "mail." + protocol + "." + name;
+        return SessionUtil.getBooleanProperty(session, fullName, defaultValue);
+    }
+}

Propchange: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/NNTPStore.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/NNTPStore.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/NNTPStore.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrc.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrc.java?rev=414173&view=auto
==============================================================================
--- geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrc.java (added)
+++ geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrc.java Wed Jun 14 03:18:51 2006
@@ -0,0 +1,180 @@
+/**
+ *
+ * Copyright 2003-2005 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.geronimo.javamail.store.nntp.newsrc;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Base class implementation of a standard news reader news rc file. This is
+ * used to track newsgroup subscriptions and SEEN flags for articles. This is an
+ * abstract class designed for subclasses to bridge to the physical store type
+ * used for the newsgroup information.
+ */
+public abstract class NNTPNewsrc {
+
+    // the group information we've read from the news rc file.
+    Map groups = new HashMap();
+
+    // flag to let us know of we need to persist the newsrc file on close.
+    boolean dirty = false;
+
+    /**
+     * Base class constructor for NNTPNewsrc items. Subclasses provide their own
+     * domain-specific intialization.
+     */
+    protected NNTPNewsrc() {
+    }
+
+    /**
+     * Load the data from the newsrc file and parse into an instore group
+     * database.
+     */
+    public void load() {
+        BufferedReader in = null;
+
+        try {
+            in = getInputReader();
+
+            String line = in.readLine();
+
+            while (line != null) {
+                // parse the line...this returns null if it's something
+                // unrecognized.
+                NNTPNewsrcGroup group = NNTPNewsrcGroup.parse(this, line);
+                // if it parsed ok, add it to the group list, and potentially to
+                // the subscribed list.
+                if (group != null) {
+                    groups.put(group.getName(), group);
+                }
+
+                line = in.readLine();
+            }
+
+            in.close();
+        } catch (IOException e) {
+            // an IOException may mean that the file just doesn't exist, which
+            // is fine. We'll ignore and
+            // proceed with the information we have.
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+        }
+    }
+
+    /**
+     * Save the newsrc file data back to the original source file.
+     * 
+     * @exception IOException
+     */
+    public void save() throws IOException {
+        Writer out = getOutputWriter();
+
+        Iterator i = groups.values().iterator();
+
+        while (i.hasNext()) {
+            NNTPNewsrcGroup group = (NNTPNewsrcGroup) i.next();
+            group.save(out);
+        }
+
+        out.close();
+    }
+
+    /**
+     * Abstract open method intended for sub class initialization. The subclass
+     * is responsible for creating the BufferedReaded used to read the .newsrc
+     * file.
+     * 
+     * @return A BufferedReader for reading the .newsrc file.
+     * @exception IOException
+     */
+    abstract public BufferedReader getInputReader() throws IOException;
+
+    /**
+     * Abstract open for output method intended for subclass implementation. The
+     * subclasses are reponsible for opening the output stream and creating an
+     * appropriate Writer for saving the .newsrc file.
+     * 
+     * @return A Writer target at the .newsrc file save location.
+     * @exception IOException
+     */
+    abstract public Writer getOutputWriter() throws IOException;
+
+    /**
+     * Retrieve the newsrc group information for a named group. If the file does
+     * not currently include this group, an unsubscribed group will be added to
+     * the file.
+     * 
+     * @param name
+     *            The name of the target group.
+     * 
+     * @return The NNTPNewsrcGroup item corresponding to this name.
+     */
+    public NNTPNewsrcGroup getGroup(String name) {
+        NNTPNewsrcGroup group = (NNTPNewsrcGroup) groups.get(name);
+        // if we don't know about this, create a new one and add to the list.
+        // This
+        // will be an unsubscribed one.
+        if (group == null) {
+            group = new NNTPNewsrcGroup(this, name, null, false);
+            groups.put(name, group);
+            // we've added a group, so we need to resave
+            dirty = true;
+        }
+        return group;
+    }
+
+    /**
+     * Mark this newsrc database as dirty.
+     */
+    public void setDirty() {
+        dirty = true;
+    }
+
+    /**
+     * Close the newsrc file, persisting it back to disk if the file has
+     * changed.
+     */
+    public void close() {
+        if (dirty) {
+            try {
+                save();
+            } catch (IOException e) {
+                // ignore
+            }
+        }
+    }
+
+    /**
+     * Retrieve the current set of loaded groups.
+     * 
+     * @return An iterator for traversing the group set.
+     */
+    public Iterator getGroups() {
+        return groups.values().iterator();
+    }
+}

Propchange: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrc.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrc.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrc.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrcFile.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrcFile.java?rev=414173&view=auto
==============================================================================
--- geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrcFile.java (added)
+++ geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrcFile.java Wed Jun 14 03:18:51 2006
@@ -0,0 +1,64 @@
+/**
+ *
+ * Copyright 2003-2005 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.geronimo.javamail.store.nntp.newsrc;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+public class NNTPNewsrcFile extends NNTPNewsrc {
+    // source for the file data
+    File source;
+
+    /**
+     * Construct a NNTPNewsrc object that is targetted at a file-based backing
+     * store.
+     * 
+     * @param source
+     *            The source File for the .newsrc data.
+     */
+    public NNTPNewsrcFile(File source) {
+        this.source = source;
+    }
+
+    /**
+     * Retrieve an input reader for loading the newsrc file.
+     * 
+     * @return A BufferedReader object for reading from the newsrc file.
+     * @exception IOException
+     */
+    public BufferedReader getInputReader() throws IOException {
+        return new BufferedReader(new InputStreamReader(new FileInputStream(source)));
+    }
+
+    /**
+     * Obtain a writer for saving a newsrc file.
+     * 
+     * @return The output writer targetted to the newsrc file.
+     * @exception IOException
+     */
+    public Writer getOutputWriter() throws IOException {
+        // open this for overwriting
+        return new OutputStreamWriter(new FileOutputStream(source, false));
+    }
+}

Propchange: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrcFile.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrcFile.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrcFile.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrcGroup.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrcGroup.java?rev=414173&view=auto
==============================================================================
--- geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrcGroup.java (added)
+++ geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrcGroup.java Wed Jun 14 03:18:51 2006
@@ -0,0 +1,183 @@
+/**
+ *
+ * Copyright 2003-2005 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.geronimo.javamail.store.nntp.newsrc;
+
+import java.io.IOException;
+import java.io.Writer;
+
+public class NNTPNewsrcGroup {
+    // the newsrc database we're part of
+    NNTPNewsrc newsrc;
+
+    // the name of the group
+    protected String name;
+
+    // the subscription flage
+    protected boolean subscribed;
+
+    // the range of already seen articles.
+    protected RangeList ranges;
+
+    /**
+     * Construct a NNTPNewsrcGroup item associated with a given .newsrc
+     * database.
+     * 
+     * @param newsrc
+     *            The owning .newsrc database.
+     * @param line
+     *            The .newsrc range entries in .newsrc format. These ranges are
+     *            parsed to create a set of seen flags.
+     * 
+     * @return A created NNTPNewsrcGroup item.
+     */
+    public static NNTPNewsrcGroup parse(NNTPNewsrc newsrc, String line) {
+        String groupName = null;
+        String ranges = null;
+
+        // subscribed lines have a ':' marker acting as a delimiter
+        int marker = line.indexOf(':');
+
+        if (marker != -1) {
+            groupName = line.substring(0, marker);
+            ranges = line.substring(marker + 1);
+            return new NNTPNewsrcGroup(newsrc, groupName, ranges, true);
+        }
+
+        // now check for an unsubscribed group
+        marker = line.indexOf('!');
+
+        if (marker != -1) {
+            groupName = line.substring(0, marker);
+            ranges = line.substring(marker + 1);
+            return new NNTPNewsrcGroup(newsrc, groupName, ranges, false);
+        }
+
+        // must be a comment line
+        return null;
+    }
+
+    /**
+     * Construct a .newsrc group item.
+     * 
+     * @param newsrc
+     *            The owning newsrc database.
+     * @param name
+     *            The group name.
+     * @param newsrcRanges
+     *            The initial set of seen ranges for the group (may be null).
+     * @param subscribed
+     *            The initial group subscription state.
+     */
+    public NNTPNewsrcGroup(NNTPNewsrc newsrc, String name, String newsrcRanges, boolean subscribed) {
+        this.newsrc = newsrc;
+        this.name = name;
+        this.subscribed = subscribed;
+        this.ranges = new RangeList(newsrcRanges);
+    }
+
+    /**
+     * Get the group name.
+     * 
+     * @return The String name of the group.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Get the newsrc subscribed status for an article.
+     * 
+     * @return The current subscription flag.
+     */
+    public boolean isSubscribed() {
+        return subscribed;
+    }
+
+    /**
+     * Set the subscription status for an article.
+     * 
+     * @param flag
+     *            The new subscription value.
+     */
+    public void setSubscribed(boolean flag) {
+        // we don't blindly set this to the new value since we only want to
+        // resave the newsrc file if
+        // something changes.
+        if (flag && !subscribed) {
+            subscribed = true;
+            newsrc.setDirty();
+        } else if (!flag && subscribed) {
+            subscribed = false;
+            newsrc.setDirty();
+        }
+    }
+
+    /**
+     * Test if an article has been seen yet.
+     * 
+     * @param article
+     *            The target article.
+     * 
+     * @return The seen mark for the article.
+     */
+    public boolean isArticleSeen(int article) {
+        return ranges.isMarked(article);
+    }
+
+    /**
+     * Mark an article as seen.
+     * 
+     * @param article
+     *            The target article number.
+     */
+    public void markArticleSeen(int article) {
+        ranges.setMarked(article);
+        if (ranges.isDirty()) {
+            newsrc.setDirty();
+        }
+    }
+
+    /**
+     * Mark an article as unseen.
+     * 
+     * @param article
+     *            The target article number.
+     */
+    public void markArticleUnseen(int article) {
+        ranges.setUnmarked(article);
+        if (ranges.isDirty()) {
+            newsrc.setDirty();
+        }
+    }
+
+    /**
+     * Save this group definition to a .newsrc file.
+     * 
+     * @param out
+     *            The output writer to send the information to.
+     * 
+     * @exception IOException
+     */
+    public void save(Writer out) throws IOException {
+        out.write(name);
+        out.write(subscribed ? ": " : "! ");
+        ranges.save(out);
+        // put a terminating line end
+        out.write("\r\n");
+    }
+}

Propchange: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrcGroup.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrcGroup.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/newsrc/NNTPNewsrcGroup.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/newsrc/Range.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/newsrc/Range.java?rev=414173&view=auto
==============================================================================
--- geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/newsrc/Range.java (added)
+++ geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/newsrc/Range.java Wed Jun 14 03:18:51 2006
@@ -0,0 +1,316 @@
+/**
+ *
+ * Copyright 2003-2005 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.geronimo.javamail.store.nntp.newsrc;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Represent a single Range in a newsrc file. A Range can be either a single
+ * number (start == end) or a span of article numbers.
+ */
+public class Range {
+    // the low end of the range
+    int start;
+
+    // the high end of the range (start and end are inclusive);
+    int end;
+
+    /**
+     * Construct a Range item for a single digit range.
+     * 
+     * @param spot
+     *            The location of the singleton.
+     */
+    public Range(int spot) {
+        this(spot, spot);
+    }
+
+    /**
+     * Construct a Range item.
+     * 
+     * @param start
+     *            The starting point of the Range.
+     * @param end
+     *            The Range end point (which may be equal to the starting
+     *            point).
+     */
+    public Range(int start, int end) {
+        this.start = start;
+        this.end = end;
+    }
+
+    /**
+     * Parse a section of a .newsrc range string into a single Range item. The
+     * range is either a single number, or a pair of numbers separated by a
+     * hyphen.
+     * 
+     * @param range
+     *            The range string.
+     * 
+     * @return A constructed Range item, or null if there is a parsing error.
+     */
+    static public Range parse(String range) {
+        // a range from a newsrc file is either a single number or in the format
+        // 'nnnn-mmmm'. We need
+        // to figure out which type this is.
+        int marker = range.indexOf('-');
+
+        try {
+            if (marker != -1) {
+                String rangeStart = range.substring(0, marker).trim();
+                String rangeEnd = range.substring(marker + 1).trim();
+
+                int start = Integer.parseInt(rangeStart);
+                int end = Integer.parseInt(rangeEnd);
+
+                if (start >= 0 && end >= 0) {
+                    return new Range(start, end);
+                }
+            } else {
+                // use the entire token
+                int start = Integer.parseInt(range);
+                // and start and the end are the same
+                return new Range(start, start);
+
+            }
+        } catch (NumberFormatException e) {
+        }
+        // return null for any bad values
+        return null;
+    }
+
+    /**
+     * Get the starting point for the Range.
+     * 
+     * @return The beginning of the mark range.
+     */
+    public int getStart() {
+        return start;
+    }
+
+    /**
+     * Set the starting point for a Range.
+     * 
+     * @param start
+     *            The new start value.
+     */
+    public void setStart(int start) {
+        this.start = start;
+    }
+
+    /**
+     * Get the ending point for the Range.
+     * 
+     * @return The end of the mark range.
+     */
+    public int getEnd() {
+        return end;
+    }
+
+    /**
+     * Set the ending point for a Range.
+     * 
+     * @param end
+     *            The new end value.
+     */
+    public void setEnd(int end) {
+        this.end = end;
+    }
+
+    /**
+     * Test if a range contains a point value.
+     * 
+     * @param target
+     *            The article location to test.
+     * 
+     * @return True if the target is between the start and end values,
+     *         inclusive.
+     */
+    public boolean contains(int target) {
+        return target >= start && target <= end;
+    }
+
+    /**
+     * Test if one range is completely contained within another Range.
+     * 
+     * @param other
+     *            The other test range.
+     * 
+     * @return true if the other start and end points are contained within this
+     *         range.
+     */
+    public boolean contains(Range other) {
+        return contains(other.getStart()) && contains(other.getEnd());
+    }
+
+    /**
+     * Tests if two ranges overlap
+     * 
+     * @param other
+     *            The other test range.
+     * 
+     * @return true if the start or end points of either range are contained
+     *         within the range of the other.
+     */
+    public boolean overlaps(Range other) {
+        return other.contains(start) || other.contains(end) || contains(other.getStart()) || contains(other.getEnd());
+    }
+
+    /**
+     * Test if two ranges exactly abutt each other.
+     * 
+     * @param other
+     *            The other Range to test.
+     * 
+     * @return true if the end of one range abutts the start of the other range.
+     */
+    public boolean abutts(Range other) {
+        return other.getStart() == end + 1 || other.getEnd() == start - 1;
+    }
+
+    /**
+     * Tests if a single point abutts either the start or end of this Range.
+     * 
+     * @param article
+     *            The point to test.
+     * 
+     * @return true if test point is equal to start - 1 or end + 1.
+     */
+    public boolean abutts(int article) {
+        return article == start - 1 || article == end + 1;
+    }
+
+    /**
+     * Test if a point is below the test Range.
+     * 
+     * @param article
+     *            The point to test.
+     * 
+     * @return true if the entire range is less than the test point.
+     */
+    public boolean lessThan(int article) {
+        return end < article;
+    }
+
+    /**
+     * Test if another Range is less than this Range.
+     * 
+     * @param other
+     *            The other Range to test.
+     * 
+     * @return true if the other Range lies completely below this Range.
+     */
+    public boolean lessThan(Range other) {
+        return end < other.start;
+    }
+
+    /**
+     * Test if a point is above the test Range.
+     * 
+     * @param article
+     *            The point to test.
+     * 
+     * @return true if the entire range is greater than the test point.
+     */
+    public boolean greaterThan(int article) {
+        return start > article;
+    }
+
+    /**
+     * Test if another Range is greater than this Range.
+     * 
+     * @param other
+     *            The other Range to test.
+     * 
+     * @return true if the other Range lies completely below this Range.
+     */
+    public boolean greaterThan(Range other) {
+        return start > other.end;
+    }
+
+    /**
+     * Merge another Range into this one. Merging will increase the bounds of
+     * this Range to encompass the entire span of the two. If the Ranges do not
+     * overlap, the newly created range will include the gap between the two.
+     * 
+     * @param other
+     *            The Range to merge.
+     */
+    public void merge(Range other) {
+        if (other.start < start) {
+            start = other.start;
+        }
+
+        if (other.end > end) {
+            end = other.end;
+        }
+    }
+
+    /**
+     * Split a range at a given split point. Splitting will truncate at the
+     * split location - 1 and return a new range beginning at location + 1; This
+     * code assumes that the split location is at neither end poing.
+     * 
+     * @param location
+     *            The split location. Location must be in the range start <
+     *            location < end.
+     * 
+     * @return A new Range object for the split portion of the range.
+     */
+    public Range split(int location) {
+        int newEnd = end;
+
+        end = location - 1;
+
+        return new Range(location + 1, newEnd);
+    }
+
+    /**
+     * Save an individual range element to a newsrc file. The range is expressed
+     * either as a single number, or a hypenated pair of numbers.
+     * 
+     * @param out
+     *            The output writer used to save the data.
+     * 
+     * @exception IOException
+     */
+    public void save(Writer out) throws IOException {
+        // do we have a single data point range?
+        if (start == end) {
+            out.write(Integer.toString(start));
+        } else {
+            out.write(Integer.toString(start));
+            out.write("-");
+            out.write(Integer.toString(end));
+        }
+    }
+
+    /**
+     * Convert a Range into String form. Used mostly for debugging.
+     * 
+     * @return The String representation of the Range.
+     */
+    public String toString() {
+        if (start == end) {
+            return Integer.toString(start);
+        } else {
+            return Integer.toString(start) + "-" + Integer.toString(end);
+        }
+    }
+}

Propchange: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/newsrc/Range.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/newsrc/Range.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/javamail/trunk/javamail-provider-1.3/src/java/org/apache/geronimo/javamail/store/nntp/newsrc/Range.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain