    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017
    018package org.apache.commons.net.nntp;
    019
    020import java.io.BufferedReader;
    021import java.io.IOException;
    022import java.io.Reader;
    023import java.io.StringWriter;
    024import java.io.Writer;
    025import java.util.ArrayList;
    026import java.util.Vector;
    027
    028import org.apache.commons.net.MalformedServerReplyException;
    029import org.apache.commons.net.io.DotTerminatedMessageReader;
    030import org.apache.commons.net.io.DotTerminatedMessageWriter;
    031import org.apache.commons.net.io.Util;
    032
    033/***
    034 * NNTPClient encapsulates all the functionality necessary to post and
    035 * retrieve articles from an NNTP server.  As with all classes derived
    036 * from {@link org.apache.commons.net.SocketClient},
    037 * you must first connect to the server with
    038 * {@link org.apache.commons.net.SocketClient#connect  connect }
    039 * before doing anything, and finally
    040 * {@link org.apache.commons.net.nntp.NNTP#disconnect  disconnect() }
    041 * after you're completely finished interacting with the server.
    042 * Remember that the
    043 * {@link org.apache.commons.net.nntp.NNTP#isAllowedToPost isAllowedToPost()}
    044 *  method is defined in
    045 * {@link org.apache.commons.net.nntp.NNTP}.
    046 * <p>
    047 * You should keep in mind that the NNTP server may choose to prematurely
    048 * close a connection if the client has been idle for longer than a
    049 * given time period or if the server is being shutdown by the operator or
    050 * some other reason.  The NNTP class will detect a
    051 * premature NNTP server connection closing when it receives a
    052 * {@link org.apache.commons.net.nntp.NNTPReply#SERVICE_DISCONTINUED NNTPReply.SERVICE_DISCONTINUED }
    053 *  response to a command.
    054 * When that occurs, the NNTP class method encountering that reply will throw
    055 * an {@link org.apache.commons.net.nntp.NNTPConnectionClosedException}
    056 * .
    057 * <code>NNTPConectionClosedException</code>
    058 * is a subclass of <code> IOException </code> and therefore need not be
    059 * caught separately, but if you are going to catch it separately, its
    060 * catch block must appear before the more general <code> IOException </code>
    061 * catch block.  When you encounter an
    062 * {@link org.apache.commons.net.nntp.NNTPConnectionClosedException}
    063 * , you must disconnect the connection with
    064 * {@link org.apache.commons.net.nntp.NNTP#disconnect  disconnect() }
    065 *  to properly clean up the
    066 * system resources used by NNTP.  Before disconnecting, you may check the
    067 * last reply code and text with
    068 * {@link org.apache.commons.net.nntp.NNTP#getReplyCode  getReplyCode } and
    069 * {@link org.apache.commons.net.nntp.NNTP#getReplyString  getReplyString }.
    070 * <p>
    071 * Rather than list it separately for each method, we mention here that
    072 * every method communicating with the server and throwing an IOException
    073 * can also throw a
    074 * {@link org.apache.commons.net.MalformedServerReplyException}
    075 * , which is a subclass
    076 * of IOException.  A MalformedServerReplyException will be thrown when
    077 * the reply received from the server deviates enough from the protocol
    078 * specification that it cannot be interpreted in a useful manner despite
    079 * attempts to be as lenient as possible.
    080 * <p>
    081 * <p>
    082 * @author Rory Winston
    083 * @author Ted Wise
    084 * @see NNTP
    085 * @see NNTPConnectionClosedException
    086 * @see org.apache.commons.net.MalformedServerReplyException
    087 ***/
    088
    089public class NNTPClient extends NNTP
    090{
    091
    092    /**
    093     * Parse the reply and store the id and number in the pointer.
    094     *
    095     * @param reply the reply to parse "22n nnn <aaa>"
    096     * @param pointer the pointer to update
    097     *
    098     * @throws MalformedServerReplyException
    099     */
    100    private void __parseArticlePointer(String reply, ArticleInfo pointer)
    101    throws MalformedServerReplyException
    102    {
    103        String tokens[] = reply.split(" ");
    104        if (tokens.length >= 3) { // OK, we can parset the line
    105            int i = 1; // skip reply code
    106            try
    107            {
    108                // Get article number
    109                pointer.articleNumber = Long.parseLong(tokens[i++]);
    110                // Get article id
    111                pointer.articleId = tokens[i++];
    112                return; // done
    113            }
    114            catch (NumberFormatException e)
    115            {
    116                // drop through and raise exception
    117            }
    118        }
    119        throw new MalformedServerReplyException(
    120            "Could not parse article pointer.\nServer reply: " + reply);
    121    }
    122
    123    /*
    124     * 211 n f l s group selected
    125     *     (n = estimated number of articles in group,
    126     *     f = first article number in the group,
    127     *     l = last article number in the group,
    128     *     s = name of the group.)
    129     */
    130
    131    private static void __parseGroupReply(String reply, NewsgroupInfo info)
    132    throws MalformedServerReplyException
    133    {
    134        String tokens[] = reply.split(" ");
    135        if (tokens.length >= 5) {
    136            int i = 1;  // Skip numeric response value
    137            try
    138            {
    139                // Get estimated article count
    140                info._setArticleCount(Long.parseLong(tokens[i++]));
    141                // Get first article number
    142                info._setFirstArticle(Long.parseLong(tokens[i++]));
    143                // Get last article number
    144                info._setLastArticle(Long.parseLong(tokens[i++]));
    145                // Get newsgroup name
    146                info._setNewsgroup(tokens[i++]);
    147
    148                info._setPostingPermission(NewsgroupInfo.UNKNOWN_POSTING_PERMISSION);
    149                return ;
    150            } catch (NumberFormatException e)
    151            {
    152               // drop through to report error
    153            }
    154        }
    155
    156        throw new MalformedServerReplyException(
    157            "Could not parse newsgroup info.\nServer reply: " + reply);
    158    }
    159
    160
    161    // Format: group last first p
    162    static NewsgroupInfo __parseNewsgroupListEntry(String entry)
    163    {
    164        String tokens[] = entry.split(" ");
    165        if (tokens.length < 4) {
    166            return null;
    167        }
    168        NewsgroupInfo result = new NewsgroupInfo();
    169
    170        int i = 0;
    171
    172        result._setNewsgroup(tokens[i++]);
    173
    174        try
    175        {
    176            long lastNum = Long.parseLong(tokens[i++]);
    177            long firstNum = Long.parseLong(tokens[i++]);
    178            result._setFirstArticle(firstNum);
    179            result._setLastArticle(lastNum);
    180            if ((firstNum == 0) && (lastNum == 0)) {
    181                result._setArticleCount(0);
    182            } else {
    183                result._setArticleCount(lastNum - firstNum + 1);
    184            }
    185        } catch (NumberFormatException e) {
    186            return null;
    187        }
    188
    189        switch (tokens[i++].charAt(0))
    190        {
    191        case 'y':
    192        case 'Y':
    193            result._setPostingPermission(
    194                NewsgroupInfo.PERMITTED_POSTING_PERMISSION);
    195            break;
    196        case 'n':
    197        case 'N':
    198            result._setPostingPermission(
    199                NewsgroupInfo.PROHIBITED_POSTING_PERMISSION);
    200            break;
    201        case 'm':
    202        case 'M':
    203            result._setPostingPermission(
    204                NewsgroupInfo.MODERATED_POSTING_PERMISSION);
    205            break;
    206        default:
    207            result._setPostingPermission(
    208                NewsgroupInfo.UNKNOWN_POSTING_PERMISSION);
    209            break;
    210        }
    211
    212        return result;
    213    }
    214
    215    /**
    216     * Parse a response line from {@link #retrieveArticleInfo(long, long)}.
    217     *
    218     * @param line a response line
    219     * @return the parsed {@link Article}, if unparseable then isDummy()
    220     * will be true, and the subject will contain the raw info.
    221     * @since 3.0
    222     */
    223    static Article __parseArticleEntry(String line) {
    224        // Extract the article information
    225        // Mandatory format (from NNTP RFC 2980) is :
    226        // articleNumber\tSubject\tAuthor\tDate\tID\tReference(s)\tByte Count\tLine Count
    227
    228        Article article = new Article();
    229        article.setSubject(line); // in case parsing fails
    230        String parts[] = line.split("\t");
    231        if (parts.length > 6) {
    232            int i = 0;
    233            try {
    234                article.setArticleNumber(Long.parseLong(parts[i++]));
    235                article.setSubject(parts[i++]);
    236                article.setFrom(parts[i++]);
    237                article.setDate(parts[i++]);
    238                article.setArticleId(parts[i++]);
    239                article.addReference(parts[i++]);
    240            } catch (NumberFormatException e) {
    241                // ignored, already handled
    242            }
    243        }
    244        return article;
    245    }
    246
    247    private NewsgroupInfo[] __readNewsgroupListing() throws IOException
    248    {
    249
    250        BufferedReader reader = new DotTerminatedMessageReader(_reader_);
    251        // Start of with a big vector because we may be reading a very large
    252        // amount of groups.
    253        Vector<NewsgroupInfo> list = new Vector<NewsgroupInfo>(2048);
    254
    255        String line;
    256        try {
    257            while ((line = reader.readLine()) != null) {
    258                NewsgroupInfo tmp = __parseNewsgroupListEntry(line);
    259                if (tmp != null) {
    260                    list.addElement(tmp);
    261                } else {
    262                    throw new MalformedServerReplyException(line);
    263                }
    264            }
    265        } finally {
    266            reader.close();
    267        }
    268        int size;
    269        if ((size = list.size()) < 1) {
    270            return new NewsgroupInfo[0];
    271        }
    272
    273        NewsgroupInfo[] info = new NewsgroupInfo[size];
    274        list.copyInto(info);
    275
    276        return info;
    277    }
    278
    279
    280    private BufferedReader __retrieve(int command,
    281                              String articleId, ArticleInfo pointer)
    282    throws IOException
    283    {
    284        if (articleId != null)
    285        {
    286            if (!NNTPReply.isPositiveCompletion(sendCommand(command, articleId))) {
    287                return null;
    288            }
    289        }
    290        else
    291        {
    292            if (!NNTPReply.isPositiveCompletion(sendCommand(command))) {
    293                return null;
    294            }
    295        }
    296
    297
    298        if (pointer != null) {
    299            __parseArticlePointer(getReplyString(), pointer);
    300        }
    301
    302        return new DotTerminatedMessageReader(_reader_);
    303    }
    304
    305
    306    private BufferedReader __retrieve(int command,
    307                              long articleNumber, ArticleInfo pointer)
    308    throws IOException
    309    {
    310        if (!NNTPReply.isPositiveCompletion(sendCommand(command,
    311                                            Long.toString(articleNumber)))) {
    312            return null;
    313        }
    314
    315        if (pointer != null) {
    316            __parseArticlePointer(getReplyString(), pointer);
    317        }
    318
    319        return new DotTerminatedMessageReader(_reader_);
    320    }
    321
    322
    323
    324    /***
    325     * Retrieves an article from the NNTP server.  The article is referenced
    326     * by its unique article identifier (including the enclosing &lt and &gt).
    327     * The article number and identifier contained in the server reply
    328     * are returned through an ArticleInfo.  The <code> articleId </code>
    329     * field of the ArticleInfo cannot always be trusted because some
    330     * NNTP servers do not correctly follow the RFC 977 reply format.
    331     * <p>
    332     * A DotTerminatedMessageReader is returned from which the article can
    333     * be read.  If the article does not exist, null is returned.
    334     * <p>
    335     * You must not issue any commands to the NNTP server (i.e., call any
    336     * other methods) until you finish reading the message from the returned
    337     * BufferedReader instance.
    338     * The NNTP protocol uses the same stream for issuing commands as it does
    339     * for returning results.  Therefore the returned BufferedReader actually reads
    340     * directly from the NNTP connection.  After the end of message has been
    341     * reached, new commands can be executed and their replies read.  If
    342     * you do not follow these requirements, your program will not work
    343     * properly.
    344     * <p>
    345     * @param articleId  The unique article identifier of the article to
    346     *     retrieve.  If this parameter is null, the currently selected
    347     *     article is retrieved.
    348     * @param pointer    A parameter through which to return the article's
    349     *   number and unique id.  The articleId field cannot always be trusted
    350     *   because of server deviations from RFC 977 reply formats.  You may
    351     *   set this parameter to null if you do not desire to retrieve the
    352     *   returned article information.
    353     * @return A DotTerminatedMessageReader instance from which the article
    354     *         be read.  null if the article does not exist.
    355     * @exception NNTPConnectionClosedException
    356     *      If the NNTP server prematurely closes the connection as a result
    357     *      of the client being idle or some other reason causing the server
    358     *      to send NNTP reply code 400.  This exception may be caught either
    359     *      as an IOException or independently as itself.
    360     * @exception IOException  If an I/O error occurs while either sending a
    361     *      command to the server or receiving a reply from the server.
    362     ***/
    363    public BufferedReader retrieveArticle(String articleId, ArticleInfo pointer)
    364    throws IOException
    365    {
    366        return __retrieve(NNTPCommand.ARTICLE, articleId, pointer);
    367
    368    }
    369
    370    /**
    371     * Same as <code> retrieveArticle(articleId, (ArticleInfo) null) </code>
    372     * Note: the return can be cast to a {@link BufferedReader}
    373     */
    374    public Reader retrieveArticle(String articleId) throws IOException
    375    {
    376        return retrieveArticle(articleId, (ArticleInfo) null);
    377    }
    378
    379    /**
    380     * Same as <code> retrieveArticle((String) null) </code>
    381     * Note: the return can be cast to a {@link BufferedReader}
    382     */
    383    public Reader retrieveArticle() throws IOException
    384    {
    385        return retrieveArticle((String) null);
    386    }
    387
    388
    389    /***
    390     * Retrieves an article from the currently selected newsgroup.  The
    391     * article is referenced by its article number.
    392     * The article number and identifier contained in the server reply
    393     * are returned through an ArticleInfo.  The <code> articleId </code>
    394     * field of the ArticleInfo cannot always be trusted because some
    395     * NNTP servers do not correctly follow the RFC 977 reply format.
    396     * <p>
    397     * A DotTerminatedMessageReader is returned from which the article can
    398     * be read.  If the article does not exist, null is returned.
    399     * <p>
    400     * You must not issue any commands to the NNTP server (i.e., call any
    401     * other methods) until you finish reading the message from the returned
    402     * BufferedReader instance.
    403     * The NNTP protocol uses the same stream for issuing commands as it does
    404     * for returning results.  Therefore the returned BufferedReader actually reads
    405     * directly from the NNTP connection.  After the end of message has been
    406     * reached, new commands can be executed and their replies read.  If
    407     * you do not follow these requirements, your program will not work
    408     * properly.
    409     * <p>
    410     * @param articleNumber  The number of the the article to
    411     *     retrieve.
    412     * @param pointer    A parameter through which to return the article's
    413     *   number and unique id.  The articleId field cannot always be trusted
    414     *   because of server deviations from RFC 977 reply formats.  You may
    415     *   set this parameter to null if you do not desire to retrieve the
    416     *   returned article information.
    417     * @return A DotTerminatedMessageReader instance from which the article
    418     *         be read.  null if the article does not exist.
    419     * @exception NNTPConnectionClosedException
    420     *      If the NNTP server prematurely closes the connection as a result
    421     *      of the client being idle or some other reason causing the server
    422     *      to send NNTP reply code 400.  This exception may be caught either
    423     *      as an IOException or independently as itself.
    424     * @exception IOException  If an I/O error occurs while either sending a
    425     *      command to the server or receiving a reply from the server.
    426     ***/
    427    public BufferedReader retrieveArticle(long articleNumber, ArticleInfo pointer)
    428    throws IOException
    429    {
    430        return __retrieve(NNTPCommand.ARTICLE, articleNumber, pointer);
    431    }
    432
    433    /*** Same as <code> retrieveArticle(articleNumber, null) </code> ***/
    434    public BufferedReader retrieveArticle(long articleNumber) throws IOException
    435    {
    436        return retrieveArticle(articleNumber, null);
    437    }
    438
    439
    440
    441    /***
    442     * Retrieves an article header from the NNTP server.  The article is
    443     * referenced
    444     * by its unique article identifier (including the enclosing &lt and &gt).
    445     * The article number and identifier contained in the server reply
    446     * are returned through an ArticleInfo.  The <code> articleId </code>
    447     * field of the ArticleInfo cannot always be trusted because some
    448     * NNTP servers do not correctly follow the RFC 977 reply format.
    449     * <p>
    450     * A DotTerminatedMessageReader is returned from which the article can
    451     * be read.  If the article does not exist, null is returned.
    452     * <p>
    453     * You must not issue any commands to the NNTP server (i.e., call any
    454     * other methods) until you finish reading the message from the returned
    455     * BufferedReader instance.
    456     * The NNTP protocol uses the same stream for issuing commands as it does
    457     * for returning results.  Therefore the returned BufferedReader actually reads
    458     * directly from the NNTP connection.  After the end of message has been
    459     * reached, new commands can be executed and their replies read.  If
    460     * you do not follow these requirements, your program will not work
    461     * properly.
    462     * <p>
    463     * @param articleId  The unique article identifier of the article whose
    464     *    header is being retrieved.  If this parameter is null, the
    465     *    header of the currently selected article is retrieved.
    466     * @param pointer    A parameter through which to return the article's
    467     *   number and unique id.  The articleId field cannot always be trusted
    468     *   because of server deviations from RFC 977 reply formats.  You may
    469     *   set this parameter to null if you do not desire to retrieve the
    470     *   returned article information.
    471     * @return A DotTerminatedMessageReader instance from which the article
    472     *         header can be read.  null if the article does not exist.
    473     * @exception NNTPConnectionClosedException
    474     *      If the NNTP server prematurely closes the connection as a result
    475     *      of the client being idle or some other reason causing the server
    476     *      to send NNTP reply code 400.  This exception may be caught either
    477     *      as an IOException or independently as itself.
    478     * @exception IOException  If an I/O error occurs while either sending a
    479     *      command to the server or receiving a reply from the server.
    480     ***/
    481    public BufferedReader retrieveArticleHeader(String articleId, ArticleInfo pointer)
    482    throws IOException
    483    {
    484        return __retrieve(NNTPCommand.HEAD, articleId, pointer);
    485
    486    }
    487
    488    /**
    489     * Same as <code> retrieveArticleHeader(articleId, (ArticleInfo) null) </code>
    490     *  Note: the return can be cast to a {@link BufferedReader}
    491     */
    492    public Reader retrieveArticleHeader(String articleId) throws IOException
    493    {
    494        return retrieveArticleHeader(articleId, (ArticleInfo) null);
    495    }
    496
    497    /**
    498     * Same as <code> retrieveArticleHeader((String) null) </code>
    499     *  Note: the return can be cast to a {@link BufferedReader}
    500     */
    501    public Reader retrieveArticleHeader() throws IOException
    502    {
    503        return retrieveArticleHeader((String) null);
    504    }
    505
    506
    507    /***
    508     * Retrieves an article header from the currently selected newsgroup.  The
    509     * article is referenced by its article number.
    510     * The article number and identifier contained in the server reply
    511     * are returned through an ArticleInfo.  The <code> articleId </code>
    512     * field of the ArticleInfo cannot always be trusted because some
    513     * NNTP servers do not correctly follow the RFC 977 reply format.
    514     * <p>
    515     * A DotTerminatedMessageReader is returned from which the article can
    516     * be read.  If the article does not exist, null is returned.
    517     * <p>
    518     * You must not issue any commands to the NNTP server (i.e., call any
    519     * other methods) until you finish reading the message from the returned
    520     * BufferedReader instance.
    521     * The NNTP protocol uses the same stream for issuing commands as it does
    522     * for returning results.  Therefore the returned BufferedReader actually reads
    523     * directly from the NNTP connection.  After the end of message has been
    524     * reached, new commands can be executed and their replies read.  If
    525     * you do not follow these requirements, your program will not work
    526     * properly.
    527     * <p>
    528     * @param articleNumber  The number of the the article whose header is
    529     *     being retrieved.
    530     * @param pointer    A parameter through which to return the article's
    531     *   number and unique id.  The articleId field cannot always be trusted
    532     *   because of server deviations from RFC 977 reply formats.  You may
    533     *   set this parameter to null if you do not desire to retrieve the
    534     *   returned article information.
    535     * @return A DotTerminatedMessageReader instance from which the article
    536     *         header can be read.  null if the article does not exist.
    537     * @exception NNTPConnectionClosedException
    538     *      If the NNTP server prematurely closes the connection as a result
    539     *      of the client being idle or some other reason causing the server
    540     *      to send NNTP reply code 400.  This exception may be caught either
    541     *      as an IOException or independently as itself.
    542     * @exception IOException  If an I/O error occurs while either sending a
    543     *      command to the server or receiving a reply from the server.
    544     ***/
    545    public BufferedReader retrieveArticleHeader(long articleNumber,
    546                                        ArticleInfo pointer)
    547    throws IOException
    548    {
    549        return __retrieve(NNTPCommand.HEAD, articleNumber, pointer);
    550    }
    551
    552
    553    /*** Same as <code> retrieveArticleHeader(articleNumber, null) </code> ***/
    554    public BufferedReader retrieveArticleHeader(long articleNumber) throws IOException
    555    {
    556        return retrieveArticleHeader(articleNumber, null);
    557    }
    558
    559
    560
    561    /***
    562     * Retrieves an article body from the NNTP server.  The article is
    563     * referenced
    564     * by its unique article identifier (including the enclosing &lt and &gt).
    565     * The article number and identifier contained in the server reply
    566     * are returned through an ArticleInfo.  The <code> articleId </code>
    567     * field of the ArticleInfo cannot always be trusted because some
    568     * NNTP servers do not correctly follow the RFC 977 reply format.
    569     * <p>
    570     * A DotTerminatedMessageReader is returned from which the article can
    571     * be read.  If the article does not exist, null is returned.
    572     * <p>
    573     * You must not issue any commands to the NNTP server (i.e., call any
    574     * other methods) until you finish reading the message from the returned
    575     * BufferedReader instance.
    576     * The NNTP protocol uses the same stream for issuing commands as it does
    577     * for returning results.  Therefore the returned BufferedReader actually reads
    578     * directly from the NNTP connection.  After the end of message has been
    579     * reached, new commands can be executed and their replies read.  If
    580     * you do not follow these requirements, your program will not work
    581     * properly.
    582     * <p>
    583     * @param articleId  The unique article identifier of the article whose
    584     *    body is being retrieved.  If this parameter is null, the
    585     *    body of the currently selected article is retrieved.
    586     * @param pointer    A parameter through which to return the article's
    587     *   number and unique id.  The articleId field cannot always be trusted
    588     *   because of server deviations from RFC 977 reply formats.  You may
    589     *   set this parameter to null if you do not desire to retrieve the
    590     *   returned article information.
    591     * @return A DotTerminatedMessageReader instance from which the article
    592     *         body can be read.  null if the article does not exist.
    593     * @exception NNTPConnectionClosedException
    594     *      If the NNTP server prematurely closes the connection as a result
    595     *      of the client being idle or some other reason causing the server
    596     *      to send NNTP reply code 400.  This exception may be caught either
    597     *      as an IOException or independently as itself.
    598     * @exception IOException  If an I/O error occurs while either sending a
    599     *      command to the server or receiving a reply from the server.
    600     ***/
    601    public BufferedReader retrieveArticleBody(String articleId, ArticleInfo pointer)
    602    throws IOException
    603    {
    604        return __retrieve(NNTPCommand.BODY, articleId, pointer);
    605
    606    }
    607
    608    /**
    609     * Same as <code> retrieveArticleBody(articleId, (ArticleInfo) null) </code>
    610     *  Note: the return can be cast to a {@link BufferedReader}
    611     */
    612    public Reader retrieveArticleBody(String articleId) throws IOException
    613    {
    614        return retrieveArticleBody(articleId, (ArticleInfo) null);
    615    }
    616
    617    /**
    618     * Same as <code> retrieveArticleBody(null) </code>
    619     *  Note: the return can be cast to a {@link BufferedReader}
    620     */
    621    public Reader retrieveArticleBody() throws IOException
    622    {
    623        return retrieveArticleBody(null);
    624    }
    625
    626
    627    /***
    628     * Retrieves an article body from the currently selected newsgroup.  The
    629     * article is referenced by its article number.
    630     * The article number and identifier contained in the server reply
    631     * are returned through an ArticleInfo.  The <code> articleId </code>
    632     * field of the ArticleInfo cannot always be trusted because some
    633     * NNTP servers do not correctly follow the RFC 977 reply format.
    634     * <p>
    635     * A DotTerminatedMessageReader is returned from which the article can
    636     * be read.  If the article does not exist, null is returned.
    637     * <p>
    638     * You must not issue any commands to the NNTP server (i.e., call any
    639     * other methods) until you finish reading the message from the returned
    640     * BufferedReader instance.
    641     * The NNTP protocol uses the same stream for issuing commands as it does
    642     * for returning results.  Therefore the returned BufferedReader actually reads
    643     * directly from the NNTP connection.  After the end of message has been
    644     * reached, new commands can be executed and their replies read.  If
    645     * you do not follow these requirements, your program will not work
    646     * properly.
    647     * <p>
    648     * @param articleNumber  The number of the the article whose body is
    649     *     being retrieved.
    650     * @param pointer    A parameter through which to return the article's
    651     *   number and unique id.  The articleId field cannot always be trusted
    652     *   because of server deviations from RFC 977 reply formats.  You may
    653     *   set this parameter to null if you do not desire to retrieve the
    654     *   returned article information.
    655     * @return A DotTerminatedMessageReader instance from which the article
    656     *         body can be read.  null if the article does not exist.
    657     * @exception NNTPConnectionClosedException
    658     *      If the NNTP server prematurely closes the connection as a result
    659     *      of the client being idle or some other reason causing the server
    660     *      to send NNTP reply code 400.  This exception may be caught either
    661     *      as an IOException or independently as itself.
    662     * @exception IOException  If an I/O error occurs while either sending a
    663     *      command to the server or receiving a reply from the server.
    664     ***/
    665    public BufferedReader retrieveArticleBody(long articleNumber,
    666                                      ArticleInfo pointer)
    667    throws IOException
    668    {
    669        return __retrieve(NNTPCommand.BODY, articleNumber, pointer);
    670    }
    671
    672
    673    /*** Same as <code> retrieveArticleBody(articleNumber, null) </code> ***/
    674    public BufferedReader retrieveArticleBody(long articleNumber) throws IOException
    675    {
    676        return retrieveArticleBody(articleNumber, null);
    677    }
    678
    679
    680    /***
    681     * Select the specified newsgroup to be the target of for future article
    682     * retrieval and posting operations.  Also return the newsgroup
    683     * information contained in the server reply through the info parameter.
    684     * <p>
    685     * @param newsgroup  The newsgroup to select.
    686     * @param info  A parameter through which the newsgroup information of
    687     *      the selected newsgroup contained in the server reply is returned.
    688     *      Set this to null if you do not desire this information.
    689     * @return True if the newsgroup exists and was selected, false otherwise.
    690     * @exception NNTPConnectionClosedException
    691     *      If the NNTP server prematurely closes the connection as a result
    692     *      of the client being idle or some other reason causing the server
    693     *      to send NNTP reply code 400.  This exception may be caught either
    694     *      as an IOException or independently as itself.
    695     * @exception IOException  If an I/O error occurs while either sending a
    696     *      command to the server or receiving a reply from the server.
    697     ***/
    698    public boolean selectNewsgroup(String newsgroup, NewsgroupInfo info)
    699    throws IOException
    700    {
    701        if (!NNTPReply.isPositiveCompletion(group(newsgroup))) {
    702            return false;
    703        }
    704
    705        if (info != null) {
    706            __parseGroupReply(getReplyString(), info);
    707        }
    708
    709        return true;
    710    }
    711
    712    /*** Same as <code> selectNewsgroup(newsgroup, null) </code> ***/
    713    public boolean selectNewsgroup(String newsgroup) throws IOException
    714    {
    715        return selectNewsgroup(newsgroup, null);
    716    }
    717
    718    /***
    719     * List the command help from the server.
    720     * <p>
    721     * @return The sever help information.
    722     * @exception NNTPConnectionClosedException
    723     *      If the NNTP server prematurely closes the connection as a result
    724     *      of the client being idle or some other reason causing the server
    725     *      to send NNTP reply code 400.  This exception may be caught either
    726     *      as an IOException or independently as itself.
    727     * @exception IOException  If an I/O error occurs while either sending a
    728     *      command to the server or receiving a reply from the server.
    729     ***/
    730    public String listHelp() throws IOException
    731    {
    732        if (!NNTPReply.isInformational(help())) {
    733            return null;
    734        }
    735
    736        StringWriter help = new StringWriter();
    737        BufferedReader reader = new DotTerminatedMessageReader(_reader_);
    738        Util.copyReader(reader, help);
    739        reader.close();
    740        help.close();
    741        return help.toString();
    742    }
    743
    744    /**
    745     * Send a "LIST OVERVIEW.FMT" command to the server.
    746     *
    747     * @return the contents of the Overview format, of {@code null} if the command failed
    748     * @throws IOException
    749     */
    750    public String[] listOverviewFmt() throws IOException
    751    {
    752        if (!NNTPReply.isPositiveCompletion(sendCommand("LIST", "OVERVIEW.FMT"))){
    753            return null;
    754        }
    755
    756        BufferedReader reader = new DotTerminatedMessageReader(_reader_);
    757        String line;
    758        ArrayList<String> list = new ArrayList<String>();
    759        while((line=reader.readLine()) != null) {
    760            list.add(line);
    761        }
    762        reader.close();
    763        return list.toArray(new String[list.size()]);
    764    }
    765
    766    /***
    767     * Select an article by its unique identifier (including enclosing
    768     * &lt and &gt) and return its article number and id through the
    769     * pointer parameter.  This is achieved through the STAT command.
    770     * According to RFC 977, this will NOT set the current article pointer
    771     * on the server.  To do that, you must reference the article by its
    772     * number.
    773     * <p>
    774     * @param articleId  The unique article identifier of the article that
    775     *    is being selectedd.  If this parameter is null, the
    776     *    body of the current article is selected
    777     * @param pointer    A parameter through which to return the article's
    778     *   number and unique id.  The articleId field cannot always be trusted
    779     *   because of server deviations from RFC 977 reply formats.  You may
    780     *   set this parameter to null if you do not desire to retrieve the
    781     *   returned article information.
    782     * @return True if successful, false if not.
    783     * @exception NNTPConnectionClosedException
    784     *      If the NNTP server prematurely closes the connection as a result
    785     *      of the client being idle or some other reason causing the server
    786     *      to send NNTP reply code 400.  This exception may be caught either
    787     *      as an IOException or independently as itself.
    788     * @exception IOException  If an I/O error occurs while either sending a
    789     *      command to the server or receiving a reply from the server.
    790     ***/
    791    public boolean selectArticle(String articleId, ArticleInfo pointer)
    792    throws IOException
    793    {
    794        if (articleId != null) {
    795            if (!NNTPReply.isPositiveCompletion(stat(articleId))) {
    796                return false;
    797            }
    798        } else {
    799            if (!NNTPReply.isPositiveCompletion(stat())) {
    800                return false;
    801            }
    802        }
    803
    804        if (pointer != null) {
    805            __parseArticlePointer(getReplyString(), pointer);
    806        }
    807
    808        return true;
    809    }
    810
    811    /**** Same as <code> selectArticle(articleId, (ArticleInfo) null) </code> ***/
    812    public boolean selectArticle(String articleId) throws IOException
    813    {
    814        return selectArticle(articleId, (ArticleInfo) null);
    815    }
    816
    817    /****
    818     * Same as <code> selectArticle((String) null, articleId) </code>.  Useful
    819     * for retrieving the current article number.
    820     ***/
    821    public boolean selectArticle(ArticleInfo pointer) throws IOException
    822    {
    823        return selectArticle(null, pointer);
    824    }
    825
    826
    827    /***
    828     * Select an article in the currently selected newsgroup by its number.
    829     * and return its article number and id through the
    830     * pointer parameter.  This is achieved through the STAT command.
    831     * According to RFC 977, this WILL set the current article pointer
    832     * on the server.  Use this command to select an article before retrieving
    833     * it, or to obtain an article's unique identifier given its number.
    834     * <p>
    835     * @param articleNumber The number of the article to select from the
    836     *       currently selected newsgroup.
    837     * @param pointer    A parameter through which to return the article's
    838     *   number and unique id.  Although the articleId field cannot always
    839     *   be trusted because of server deviations from RFC 977 reply formats,
    840     *   we haven't found a server that misformats this information in response
    841     *   to this particular command.  You may set this parameter to null if
    842     *   you do not desire to retrieve the returned article information.
    843     * @return True if successful, false if not.
    844     * @exception NNTPConnectionClosedException
    845     *      If the NNTP server prematurely closes the connection as a result
    846     *      of the client being idle or some other reason causing the server
    847     *      to send NNTP reply code 400.  This exception may be caught either
    848     *      as an IOException or independently as itself.
    849     * @exception IOException  If an I/O error occurs while either sending a
    850     *      command to the server or receiving a reply from the server.
    851     ***/
    852    public boolean selectArticle(long articleNumber, ArticleInfo pointer)
    853    throws IOException
    854    {
    855        if (!NNTPReply.isPositiveCompletion(stat(articleNumber))) {
    856            return false;
    857        }
    858
    859        if (pointer != null) {
    860            __parseArticlePointer(getReplyString(), pointer);
    861        }
    862
    863        return true;
    864    }
    865
    866
    867    /*** Same as <code> selectArticle(articleNumber, null) </code> ***/
    868    public boolean selectArticle(long articleNumber) throws IOException
    869    {
    870        return selectArticle(articleNumber, null);
    871    }
    872
    873
    874    /***
    875     * Select the article preceeding the currently selected article in the
    876     * currently selected newsgroup and return its number and unique id
    877     * through the pointer parameter.  Because of deviating server
    878     * implementations, the articleId information cannot be trusted.  To
    879     * obtain the article identifier, issue a
    880     * <code> selectArticle(pointer.articleNumber, pointer) </code> immediately
    881     * afterward.
    882     * <p>
    883     * @param pointer    A parameter through which to return the article's
    884     *   number and unique id.  The articleId field cannot always be trusted
    885     *   because of server deviations from RFC 977 reply formats.  You may
    886     *   set this parameter to null if you do not desire to retrieve the
    887     *   returned article information.
    888     * @return True if successful, false if not (e.g., there is no previous
    889     *     article).
    890     * @exception NNTPConnectionClosedException
    891     *      If the NNTP server prematurely closes the connection as a result
    892     *      of the client being idle or some other reason causing the server
    893     *      to send NNTP reply code 400.  This exception may be caught either
    894     *      as an IOException or independently as itself.
    895     * @exception IOException  If an I/O error occurs while either sending a
    896     *      command to the server or receiving a reply from the server.
    897     ***/
    898    public boolean selectPreviousArticle(ArticleInfo pointer)
    899    throws IOException
    900    {
    901        if (!NNTPReply.isPositiveCompletion(last())) {
    902            return false;
    903        }
    904
    905        if (pointer != null) {
    906            __parseArticlePointer(getReplyString(), pointer);
    907        }
    908
    909        return true;
    910    }
    911
    912    /*** Same as <code> selectPreviousArticle((ArticleInfo) null) </code> ***/
    913    public boolean selectPreviousArticle() throws IOException
    914    {
    915        return selectPreviousArticle((ArticleInfo) null);
    916    }
    917
    918
    919    /***
    920     * Select the article following the currently selected article in the
    921     * currently selected newsgroup and return its number and unique id
    922     * through the pointer parameter.  Because of deviating server
    923     * implementations, the articleId information cannot be trusted.  To
    924     * obtain the article identifier, issue a
    925     * <code> selectArticle(pointer.articleNumber, pointer) </code> immediately
    926     * afterward.
    927     * <p>
    928     * @param pointer    A parameter through which to return the article's
    929     *   number and unique id.  The articleId field cannot always be trusted
    930     *   because of server deviations from RFC 977 reply formats.  You may
    931     *   set this parameter to null if you do not desire to retrieve the
    932     *   returned article information.
    933     * @return True if successful, false if not (e.g., there is no following
    934     *         article).
    935     * @exception NNTPConnectionClosedException
    936     *      If the NNTP server prematurely closes the connection as a result
    937     *      of the client being idle or some other reason causing the server
    938     *      to send NNTP reply code 400.  This exception may be caught either
    939     *      as an IOException or independently as itself.
    940     * @exception IOException  If an I/O error occurs while either sending a
    941     *      command to the server or receiving a reply from the server.
    942     ***/
    943    public boolean selectNextArticle(ArticleInfo pointer) throws IOException
    944    {
    945        if (!NNTPReply.isPositiveCompletion(next())) {
    946            return false;
    947        }
    948
    949        if (pointer != null) {
    950            __parseArticlePointer(getReplyString(), pointer);
    951        }
    952
    953        return true;
    954    }
    955
    956
    957    /*** Same as <code> selectNextArticle((ArticleInfo) null) </code> ***/
    958    public boolean selectNextArticle() throws IOException
    959    {
    960        return selectNextArticle((ArticleInfo) null);
    961    }
    962
    963
    964    /***
    965     * List all newsgroups served by the NNTP server.  If no newsgroups
    966     * are served, a zero length array will be returned.  If the command
    967     * fails, null will be returned.
    968     * The method uses the "LIST" command.
    969     * <p>
    970     * @return An array of NewsgroupInfo instances containing the information
    971     *    for each newsgroup served by the NNTP server.   If no newsgroups
    972     *    are served, a zero length array will be returned.  If the command
    973     *    fails, null will be returned.
    974     * @exception NNTPConnectionClosedException
    975     *      If the NNTP server prematurely closes the connection as a result
    976     *      of the client being idle or some other reason causing the server
    977     *      to send NNTP reply code 400.  This exception may be caught either
    978     *      as an IOException or independently as itself.
    979     * @exception IOException  If an I/O error occurs while either sending a
    980     *      command to the server or receiving a reply from the server.
    981     * @see #iterateNewsgroupListing()
    982     * @see #iterateNewsgroups()
    983     ***/
    984    public NewsgroupInfo[] listNewsgroups() throws IOException
    985    {
    986        if (!NNTPReply.isPositiveCompletion(list())) {
    987            return null;
    988        }
    989
    990        return __readNewsgroupListing();
    991    }
    992
    993    /**
    994     * List all newsgroups served by the NNTP server.  If no newsgroups
    995     * are served, no entries will be returned.
    996     * The method uses the "LIST" command.
    997     * <p>
    998     * @return An iterable of NewsgroupInfo instances containing the information
    999     *    for each newsgroup served by the NNTP server.   If no newsgroups
    1000     *    are served, no entries will be returned.
    1001     * @exception NNTPConnectionClosedException
    1002     *      If the NNTP server prematurely closes the connection as a result
    1003     *      of the client being idle or some other reason causing the server
    1004     *      to send NNTP reply code 400.  This exception may be caught either
    1005     *      as an IOException or independently as itself.
    1006     * @exception IOException  If an I/O error occurs while either sending a
    1007     *      command to the server or receiving a reply from the server.
    1008     * @since 3.0
    1009     */
    1010    public Iterable<String> iterateNewsgroupListing() throws IOException {
    1011        if (NNTPReply.isPositiveCompletion(list())) {
    1012            return new ReplyIterator(_reader_);
    1013        }
    1014        throw new IOException("LIST command failed: "+getReplyString());
    1015    }
    1016
    1017    /**
    1018     * List all newsgroups served by the NNTP server.  If no newsgroups
    1019     * are served, no entries will be returned.
    1020     * The method uses the "LIST" command.
    1021     * <p>
    1022     * @return An iterable of Strings containing the raw information
    1023     *    for each newsgroup served by the NNTP server.   If no newsgroups
    1024     *    are served, no entries will be returned.
    1025     * @exception NNTPConnectionClosedException
    1026     *      If the NNTP server prematurely closes the connection as a result
    1027     *      of the client being idle or some other reason causing the server
    1028     *      to send NNTP reply code 400.  This exception may be caught either
    1029     *      as an IOException or independently as itself.
    1030     * @exception IOException  If an I/O error occurs while either sending a
    1031     *      command to the server or receiving a reply from the server.
    1032     * @since 3.0
    1033     */
    1034    public Iterable<NewsgroupInfo> iterateNewsgroups() throws IOException {
    1035        return new NewsgroupIterator(iterateNewsgroupListing());
    1036    }
    1037
    1038    /**
    1039     * List the newsgroups that match a given pattern.
    1040     * Uses the "LIST ACTIVE" command.
    1041     * <p>
    1042     * @param wildmat a pseudo-regex pattern (cf. RFC 2980)
    1043     * @return An array of NewsgroupInfo instances containing the information
    1044     *    for each newsgroup served by the NNTP server corresponding to the
    1045     *    supplied pattern.   If no such newsgroups are served, a zero length
    1046     *    array will be returned.  If the command fails, null will be returned.
    1047     * @throws IOException
    1048     * @see #iterateNewsgroupListing(String)
    1049     * @see #iterateNewsgroups(String)
    1050     */
    1051    public NewsgroupInfo[] listNewsgroups(String wildmat) throws IOException
    1052    {
    1053        if(!NNTPReply.isPositiveCompletion(listActive(wildmat))) {
    1054            return null;
    1055        }
    1056        return __readNewsgroupListing();
    1057    }
    1058
    1059
    1060    /**
    1061     * List the newsgroups that match a given pattern.
    1062     * Uses the "LIST ACTIVE" command.
    1063     * <p>
    1064     * @param wildmat a pseudo-regex pattern (cf. RFC 2980)
    1065     * @return An iterable of Strings containing the raw information
    1066     *    for each newsgroup served by the NNTP server corresponding to the
    1067     *    supplied pattern.   If no such newsgroups are served, no entries
    1068     *    will be returned.
    1069     * @throws IOException
    1070     * @since 3.0
    1071     */
    1072    public Iterable<String> iterateNewsgroupListing(String wildmat) throws IOException {
    1073        if(NNTPReply.isPositiveCompletion(listActive(wildmat))) {
    1074            return new ReplyIterator(_reader_);
    1075        }
    1076        throw new IOException("LIST ACTIVE "+wildmat+" command failed: "+getReplyString());
    1077    }
    1078
    1079    /**
    1080     * List the newsgroups that match a given pattern.
    1081     * Uses the "LIST ACTIVE" command.
    1082     * <p>
    1083     * @param wildmat a pseudo-regex pattern (cf. RFC 2980)
    1084     * @return An iterable NewsgroupInfo instances containing the information
    1085     *    for each newsgroup served by the NNTP server corresponding to the
    1086     *    supplied pattern.   If no such newsgroups are served, no entries
    1087     *    will be returned.
    1088     * @throws IOException
    1089     * @since 3.0
    1090     */
    1091    public Iterable<NewsgroupInfo> iterateNewsgroups(String wildmat) throws IOException {
    1092        return new NewsgroupIterator(iterateNewsgroupListing(wildmat));
    1093    }
    1094
    1095    /***
    1096     * List all new newsgroups added to the NNTP server since a particular
    1097     * date subject to the conditions of the specified query.  If no new
    1098     * newsgroups were added, a zero length array will be returned.  If the
    1099     * command fails, null will be returned.
    1100     * This uses the "NEWGROUPS" command.
    1101     * <p>
    1102     * @param query  The query restricting how to search for new newsgroups.
    1103     * @return An array of NewsgroupInfo instances containing the information
    1104     *    for each new newsgroup added to the NNTP server.   If no newsgroups
    1105     *    were added, a zero length array will be returned.  If the command
    1106     *    fails, null will be returned.
    1107     * @exception NNTPConnectionClosedException
    1108     *      If the NNTP server prematurely closes the connection as a result
    1109     *      of the client being idle or some other reason causing the server
    1110     *      to send NNTP reply code 400.  This exception may be caught either
    1111     *      as an IOException or independently as itself.
    1112     * @exception IOException  If an I/O error occurs while either sending a
    1113     *      command to the server or receiving a reply from the server.
    1114     * @see #iterateNewNewsgroups(NewGroupsOrNewsQuery)
    1115     * @see #iterateNewNewsgroupListing(NewGroupsOrNewsQuery)
    1116     ***/
    1117    public NewsgroupInfo[] listNewNewsgroups(NewGroupsOrNewsQuery query)
    1118    throws IOException
    1119    {
    1120        if (!NNTPReply.isPositiveCompletion(newgroups(
    1121                                                query.getDate(), query.getTime(),
    1122                                                query.isGMT(), query.getDistributions())))
    1123        {
    1124            return null;
    1125        }
    1126
    1127        return __readNewsgroupListing();
    1128    }
    1129
    1130    /**
    1131     * List all new newsgroups added to the NNTP server since a particular
    1132     * date subject to the conditions of the specified query.  If no new
    1133     * newsgroups were added, no entries will be returned.
    1134     * This uses the "NEWGROUPS" command.
    1135     * <p>
    1136     * @param query  The query restricting how to search for new newsgroups.
    1137     * @return An iterable of Strings containing the raw information
    1138     *    for each new newsgroup added to the NNTP server.   If no newsgroups
    1139     *    were added, no entries will be returned.
    1140     * @exception NNTPConnectionClosedException
    1141     *      If the NNTP server prematurely closes the connection as a result
    1142     *      of the client being idle or some other reason causing the server
    1143     *      to send NNTP reply code 400.  This exception may be caught either
    1144     *      as an IOException or independently as itself.
    1145     * @exception IOException  If an I/O error occurs while either sending a
    1146     *      command to the server or receiving a reply from the server.
    1147     * @since 3.0
    1148     */
    1149    public Iterable<String> iterateNewNewsgroupListing(NewGroupsOrNewsQuery query) throws IOException {
    1150        if (NNTPReply.isPositiveCompletion(newgroups(
    1151                query.getDate(), query.getTime(),
    1152                query.isGMT(), query.getDistributions()))) {
    1153            return new ReplyIterator(_reader_);
    1154        }
    1155        throw new IOException("NEWGROUPS command failed: "+getReplyString());
    1156    }
    1157
    1158    /**
    1159     * List all new newsgroups added to the NNTP server since a particular
    1160     * date subject to the conditions of the specified query.  If no new
    1161     * newsgroups were added, no entries will be returned.
    1162     * This uses the "NEWGROUPS" command.
    1163     * <p>
    1164     * @param query  The query restricting how to search for new newsgroups.
    1165     * @return An iterable of NewsgroupInfo instances containing the information
    1166     *    for each new newsgroup added to the NNTP server.   If no newsgroups
    1167     *    were added, no entries will be returned.
    1168     * @exception NNTPConnectionClosedException
    1169     *      If the NNTP server prematurely closes the connection as a result
    1170     *      of the client being idle or some other reason causing the server
    1171     *      to send NNTP reply code 400.  This exception may be caught either
    1172     *      as an IOException or independently as itself.
    1173     * @exception IOException  If an I/O error occurs while either sending a
    1174     *      command to the server or receiving a reply from the server.
    1175     * @since 3.0
    1176     */
    1177    public Iterable<NewsgroupInfo> iterateNewNewsgroups(NewGroupsOrNewsQuery query) throws IOException {
    1178        return new NewsgroupIterator(iterateNewNewsgroupListing(query));
    1179    }
    1180
    1181    /***
    1182     * List all new articles added to the NNTP server since a particular
    1183     * date subject to the conditions of the specified query.  If no new
    1184     * new news is found, a zero length array will be returned.  If the
    1185     * command fails, null will be returned.  You must add at least one
    1186     * newsgroup to the query, else the command will fail.  Each String
    1187     * in the returned array is a unique message identifier including the
    1188     * enclosing &lt and &gt.
    1189     * This uses the "NEWNEWS" command.
    1190     * <p>
    1191     * @param query  The query restricting how to search for new news.  You
    1192     *    must add at least one newsgroup to the query.
    1193     * @return An array of String instances containing the unique message
    1194     *    identifiers for each new article added to the NNTP server.  If no
    1195     *    new news is found, a zero length array will be returned.  If the
    1196     *    command fails, null will be returned.
    1197     * @exception NNTPConnectionClosedException
    1198     *      If the NNTP server prematurely closes the connection as a result
    1199     *      of the client being idle or some other reason causing the server
    1200     *      to send NNTP reply code 400.  This exception may be caught either
    1201     *      as an IOException or independently as itself.
    1202     * @exception IOException  If an I/O error occurs while either sending a
    1203     *      command to the server or receiving a reply from the server.
    1204     *
    1205     * @see #iterateNewNews(NewGroupsOrNewsQuery)
    1206     ***/
    1207    public String[] listNewNews(NewGroupsOrNewsQuery query)
    1208    throws IOException
    1209    {
    1210        if (!NNTPReply.isPositiveCompletion(
    1211                newnews(query.getNewsgroups(), query.getDate(), query.getTime(),
    1212                        query.isGMT(), query.getDistributions()))) {
    1213            return null;
    1214        }
    1215
    1216        Vector<String> list = new Vector<String>();
    1217        BufferedReader reader = new DotTerminatedMessageReader(_reader_);
    1218
    1219        String line;
    1220        try {
    1221            while ((line = reader.readLine()) != null) {
    1222                list.addElement(line);
    1223            }
    1224        } finally {
    1225            reader.close();
    1226        }
    1227
    1228        int size = list.size();
    1229        if (size < 1) {
    1230            return new String[0];
    1231        }
    1232
    1233        String[] result = new String[size];
    1234        list.copyInto(result);
    1235
    1236        return result;
    1237    }
    1238
    1239    /**
    1240     * List all new articles added to the NNTP server since a particular
    1241     * date subject to the conditions of the specified query.  If no new
    1242     * new news is found, no entries will be returned.
    1243     * This uses the "NEWNEWS" command.
    1244     * You must add at least one newsgroup to the query, else the command will fail.
    1245     * Each String which is returned is a unique message identifier including the
    1246     * enclosing &lt and &gt.
    1247     * <p>
    1248     * @param query  The query restricting how to search for new news.  You
    1249     *    must add at least one newsgroup to the query.
    1250     * @return An iterator of String instances containing the unique message
    1251     *    identifiers for each new article added to the NNTP server.  If no
    1252     *    new news is found, no strings will be returned.
    1253     * @exception NNTPConnectionClosedException
    1254     *      If the NNTP server prematurely closes the connection as a result
    1255     *      of the client being idle or some other reason causing the server
    1256     *      to send NNTP reply code 400.  This exception may be caught either
    1257     *      as an IOException or independently as itself.
    1258     * @exception IOException  If an I/O error occurs while either sending a
    1259     *      command to the server or receiving a reply from the server.
    1260     * @since 3.0
    1261     */
    1262    public Iterable<String> iterateNewNews(NewGroupsOrNewsQuery query) throws IOException {
    1263        if (NNTPReply.isPositiveCompletion(newnews(
    1264                query.getNewsgroups(), query.getDate(), query.getTime(),
    1265                query.isGMT(), query.getDistributions()))) {
    1266            return new ReplyIterator(_reader_);
    1267        }
    1268        throw new IOException("NEWNEWS command failed: "+getReplyString());
    1269    }
    1270
    1271    /***
    1272     * There are a few NNTPClient methods that do not complete the
    1273     * entire sequence of NNTP commands to complete a transaction.  These
    1274     * commands require some action by the programmer after the reception
    1275     * of a positive preliminary command.  After the programmer's code
    1276     * completes its actions, it must call this method to receive
    1277     * the completion reply from the server and verify the success of the
    1278     * entire transaction.
    1279     * <p>
    1280     * For example
    1281     * <pre>
    1282     * writer = client.postArticle();
    1283     * if(writer == null) // failure
    1284     *   return false;
    1285     * header = new SimpleNNTPHeader("foobar@foo.com", "Just testing");
    1286     * header.addNewsgroup("alt.test");
    1287     * writer.write(header.toString());
    1288     * writer.write("This is just a test");
    1289     * writer.close();
    1290     * if(!client.completePendingCommand()) // failure
    1291     *   return false;
    1292     * </pre>
    1293     * <p>
    1294     * @return True if successfully completed, false if not.
    1295     * @exception NNTPConnectionClosedException
    1296     *      If the NNTP server prematurely closes the connection as a result
    1297     *      of the client being idle or some other reason causing the server
    1298     *      to send NNTP reply code 400.  This exception may be caught either
    1299     *      as an IOException or independently as itself.
    1300     * @exception IOException  If an I/O error occurs while either sending a
    1301     *      command to the server or receiving a reply from the server.
    1302     ***/
    1303    public boolean completePendingCommand() throws IOException
    1304    {
    1305        return NNTPReply.isPositiveCompletion(getReply());
    1306    }
    1307
    1308    /***
    1309     * Post an article to the NNTP server.  This method returns a
    1310     * DotTerminatedMessageWriter instance to which the article can be
    1311     * written.  Null is returned if the posting attempt fails.  You
    1312     * should check {@link NNTP#isAllowedToPost isAllowedToPost() }
    1313     *  before trying to post.  However, a posting
    1314     * attempt can fail due to malformed headers.
    1315     * <p>
    1316     * You must not issue any commands to the NNTP server (i.e., call any
    1317     * (other methods) until you finish writing to the returned Writer
    1318     * instance and close it.  The NNTP protocol uses the same stream for
    1319     * issuing commands as it does for returning results.  Therefore the
    1320     * returned Writer actually writes directly to the NNTP connection.
    1321     * After you close the writer, you can execute new commands.  If you
    1322     * do not follow these requirements your program will not work properly.
    1323     * <p>
    1324     * Different NNTP servers will require different header formats, but
    1325     * you can use the provided
    1326     * {@link org.apache.commons.net.nntp.SimpleNNTPHeader}
    1327     * class to construct the bare minimum acceptable header for most
    1328     * news readers.  To construct more complicated headers you should
    1329     * refer to RFC 822.  When the Java Mail API is finalized, you will be
    1330     * able to use it to compose fully compliant Internet text messages.
    1331     * The DotTerminatedMessageWriter takes care of doubling line-leading
    1332     * dots and ending the message with a single dot upon closing, so all
    1333     * you have to worry about is writing the header and the message.
    1334     * <p>
    1335     * Upon closing the returned Writer, you need to call
    1336     * {@link #completePendingCommand  completePendingCommand() }
    1337     * to finalize the posting and verify its success or failure from
    1338     * the server reply.
    1339     * <p>
    1340     * @return A DotTerminatedMessageWriter to which the article (including
    1341     *      header) can be written.  Returns null if the command fails.
    1342     * @exception IOException  If an I/O error occurs while either sending a
    1343     *      command to the server or receiving a reply from the server.
    1344     ***/
    1345
    1346    public Writer postArticle() throws IOException
    1347    {
    1348        if (!NNTPReply.isPositiveIntermediate(post())) {
    1349            return null;
    1350        }
    1351
    1352        return new DotTerminatedMessageWriter(_writer_);
    1353    }
    1354
    1355
    1356    public Writer forwardArticle(String articleId) throws IOException
    1357    {
    1358        if (!NNTPReply.isPositiveIntermediate(ihave(articleId))) {
    1359            return null;
    1360        }
    1361
    1362        return new DotTerminatedMessageWriter(_writer_);
    1363    }
    1364
    1365
    1366    /***
    1367     * Logs out of the news server gracefully by sending the QUIT command.
    1368     * However, you must still disconnect from the server before you can open
    1369     * a new connection.
    1370     * <p>
    1371     * @return True if successfully completed, false if not.
    1372     * @exception IOException  If an I/O error occurs while either sending a
    1373     *      command to the server or receiving a reply from the server.
    1374     ***/
    1375    public boolean logout() throws IOException
    1376    {
    1377        return NNTPReply.isPositiveCompletion(quit());
    1378    }
    1379
    1380
    1381    /**
    1382     * Log into a news server by sending the AUTHINFO USER/AUTHINFO
    1383     * PASS command sequence. This is usually sent in response to a
    1384     * 480 reply code from the NNTP server.
    1385     * <p>
    1386     * @param username a valid username
    1387     * @param password the corresponding password
    1388     * @return True for successful login, false for a failure
    1389     * @throws IOException
    1390     */
    1391    public boolean authenticate(String username, String password)
    1392        throws IOException
    1393    {
    1394        int replyCode = authinfoUser(username);
    1395
    1396        if (replyCode == NNTPReply.MORE_AUTH_INFO_REQUIRED)
    1397            {
    1398                replyCode = authinfoPass(password);
    1399
    1400                if (replyCode == NNTPReply.AUTHENTICATION_ACCEPTED)
    1401                    {
    1402                        _isAllowedToPost = true;
    1403                        return true;
    1404                    }
    1405            }
    1406        return false;
    1407    }
    1408
    1409    /***
    1410     * Private implementation of XOVER functionality.
    1411     *
    1412     * See {@link NNTP#xover}
    1413     * for legal agument formats. Alternatively, read RFC 2980 :-)
    1414     * <p>
    1415     * @param articleRange
    1416     * @return Returns a DotTerminatedMessageReader if successful, null
    1417     *         otherwise
    1418     * @exception IOException
    1419     */
    1420    private BufferedReader __retrieveArticleInfo(String articleRange)
    1421        throws IOException
    1422    {
    1423        if (!NNTPReply.isPositiveCompletion(xover(articleRange))) {
    1424            return null;
    1425        }
    1426
    1427        return new DotTerminatedMessageReader(_reader_);
    1428    }
    1429
    1430    /**
    1431     * Return article headers for a specified post.
    1432     * <p>
    1433     * @param articleNumber the article to retrieve headers for
    1434     * @return a DotTerminatedReader if successful, null otherwise
    1435     * @throws IOException
    1436     */
    1437    public BufferedReader retrieveArticleInfo(long articleNumber) throws IOException
    1438    {
    1439        return __retrieveArticleInfo(Long.toString(articleNumber));
    1440    }
    1441
    1442    /**
    1443     * Return article headers for all articles between lowArticleNumber
    1444     * and highArticleNumber, inclusively. Uses the XOVER command.
    1445     * <p>
    1446     * @param lowArticleNumber
    1447     * @param highArticleNumber
    1448     * @return a DotTerminatedReader if successful, null otherwise
    1449     * @throws IOException
    1450     */
    1451    public BufferedReader retrieveArticleInfo(long lowArticleNumber,
    1452            long highArticleNumber)
    1453        throws IOException
    1454    {
    1455        return
    1456            __retrieveArticleInfo(lowArticleNumber + "-" +
    1457                                             highArticleNumber);
    1458    }
    1459
    1460    /**
    1461     * Return article headers for all articles between lowArticleNumber
    1462     * and highArticleNumber, inclusively, using the XOVER command.
    1463     * <p>
    1464     * @param lowArticleNumber
    1465     * @param highArticleNumber
    1466     * @return an Iterable of Articles
    1467     * @throws IOException if the command failed
    1468     * @since 3.0
    1469     */
    1470    public Iterable<Article> iterateArticleInfo(long lowArticleNumber, long highArticleNumber)
    1471        throws IOException
    1472    {
    1473        BufferedReader info = retrieveArticleInfo(lowArticleNumber,highArticleNumber);
    1474        if (info == null) {
    1475            throw new IOException("XOVER command failed: "+getReplyString());
    1476        }
    1477        // N.B. info is already DotTerminated, so don't rewrap
    1478        return new ArticleIterator(new ReplyIterator(info, false));
    1479    }
    1480
    1481    /***
    1482     * Private implementation of XHDR functionality.
    1483     *
    1484     * See {@link NNTP#xhdr}
    1485     * for legal agument formats. Alternatively, read RFC 1036.
    1486     * <p>
    1487     * @param header
    1488     * @param articleRange
    1489     * @return Returns a DotTerminatedMessageReader if successful, null
    1490     *         otherwise
    1491     * @exception IOException
    1492     */
    1493    private BufferedReader __retrieveHeader(String header, String articleRange)
    1494        throws IOException
    1495    {
    1496        if (!NNTPReply.isPositiveCompletion(xhdr(header, articleRange))) {
    1497            return null;
    1498        }
    1499
    1500        return new DotTerminatedMessageReader(_reader_);
    1501    }
    1502
    1503    /**
    1504     * Return an article header for a specified post.
    1505     * <p>
    1506     * @param header the header to retrieve
    1507     * @param articleNumber the article to retrieve the header for
    1508     * @return a DotTerminatedReader if successful, null otherwise
    1509     * @throws IOException
    1510     */
    1511    public BufferedReader retrieveHeader(String header, long articleNumber)
    1512        throws IOException
    1513    {
    1514        return __retrieveHeader(header, Long.toString(articleNumber));
    1515    }
    1516
    1517    /**
    1518     * Return an article header for all articles between lowArticleNumber
    1519     * and highArticleNumber, inclusively.
    1520     * <p>
    1521     * @param header
    1522     * @param lowArticleNumber
    1523     * @param highArticleNumber
    1524     * @return a DotTerminatedReader if successful, null otherwise
    1525     * @throws IOException
    1526     */
    1527    public BufferedReader retrieveHeader(String header, long lowArticleNumber,
    1528                                 long highArticleNumber)
    1529        throws IOException
    1530    {
    1531        return
    1532            __retrieveHeader(header,lowArticleNumber + "-" + highArticleNumber);
    1533    }
    1534
    1535
    1536
    1537
    1538
    1539    // DEPRECATED METHODS - for API compatibility only - DO NOT USE
    1540    // ============================================================
    1541
    1542
    1543
    1544    /**
    1545     * @deprecated 3.0 use {@link #retrieveHeader(String, long, long)} instead
    1546     */
    1547    @Deprecated
    1548    public Reader retrieveHeader(String s, int l, int h)
    1549        throws IOException
    1550    {
    1551        return retrieveHeader(s, (long) l, (long) h);
    1552    }
    1553
    1554    /**
    1555     * @deprecated 3.0 use {@link #retrieveArticleInfo(long, long)} instead
    1556     */
    1557    @Deprecated
    1558    public Reader retrieveArticleInfo(int a, int b) throws IOException {
    1559        return retrieveArticleInfo((long) a, (long) b);
    1560    }
    1561
    1562    /**
    1563     * @deprecated 3.0 use {@link #retrieveHeader(String, long)} instead
    1564     */
    1565    @Deprecated
    1566    public Reader retrieveHeader(String a, int b) throws IOException {
    1567        return retrieveHeader(a, (long) b);
    1568    }
    1569
    1570    /**
    1571     * @deprecated 3.0 use {@link #selectArticle(long, ArticleInfo)} instead
    1572     */
    1573    @Deprecated
    1574    public boolean selectArticle(int a, ArticlePointer ap) throws IOException {
    1575        ArticleInfo ai =  __ap2ai(ap);
    1576        boolean b = selectArticle(a, ai);
    1577        __ai2ap(ai, ap);
    1578        return b;
    1579    }
    1580
    1581    /**
    1582     * @deprecated 3.0 use {@link #retrieveArticleInfo(long)} instead
    1583     */
    1584    @Deprecated
    1585    public Reader retrieveArticleInfo(int a) throws IOException {
    1586        return retrieveArticleInfo((long) a);
    1587    }
    1588
    1589    /**
    1590     * @deprecated 3.0 use {@link #selectArticle(long)} instead
    1591     */
    1592    @Deprecated
    1593    public boolean selectArticle(int a) throws IOException {
    1594        return selectArticle((long) a);
    1595    }
    1596
    1597    /**
    1598     * @deprecated 3.0 use {@link #retrieveArticleHeader(long)} instead
    1599     */
    1600    @Deprecated
    1601    public Reader retrieveArticleHeader(int a) throws IOException {
    1602        return retrieveArticleHeader((long) a);
    1603    }
    1604
    1605    /**
    1606     * @deprecated 3.0 use {@link #retrieveArticleHeader(long, ArticleInfo)} instead
    1607     */
    1608    @Deprecated
    1609    public Reader retrieveArticleHeader(int a, ArticlePointer ap) throws IOException {
    1610        ArticleInfo ai =  __ap2ai(ap);
    1611        Reader rdr = retrieveArticleHeader(a, ai);
    1612        __ai2ap(ai, ap);
    1613        return rdr;
    1614    }
    1615
    1616    /**
    1617     * @deprecated 3.0 use {@link #retrieveArticleBody(long)} instead
    1618     */
    1619    @Deprecated
    1620    public Reader retrieveArticleBody(int a) throws IOException {
    1621        return retrieveArticleBody((long) a);
    1622    }
    1623
    1624    /**
    1625     * @deprecated 3.0 use {@link #retrieveArticle(long, ArticleInfo)} instead
    1626     */
    1627    @Deprecated
    1628    public Reader retrieveArticle(int a, ArticlePointer ap) throws IOException {
    1629        ArticleInfo ai =  __ap2ai(ap);
    1630        Reader rdr = retrieveArticle(a, ai);
    1631        __ai2ap(ai, ap);
    1632        return rdr;
    1633    }
    1634
    1635    /**
    1636     * @deprecated 3.0 use {@link #retrieveArticle(long)} instead
    1637     */
    1638    @Deprecated
    1639    public Reader retrieveArticle(int a) throws IOException {
    1640        return retrieveArticle((long) a);
    1641    }
    1642
    1643    /**
    1644     * @deprecated 3.0 use {@link #retrieveArticleBody(long, ArticleInfo)} instead
    1645     */
    1646    @Deprecated
    1647    public Reader retrieveArticleBody(int a, ArticlePointer ap) throws IOException {
    1648        ArticleInfo ai =  __ap2ai(ap);
    1649        Reader rdr = retrieveArticleBody(a, ai);
    1650        __ai2ap(ai, ap);
    1651        return rdr;
    1652    }
    1653
    1654    /**
    1655     * @deprecated 3.0 use {@link #retrieveArticle(String, ArticleInfo)} instead
    1656     */
    1657    @Deprecated
    1658    public Reader retrieveArticle(String a, ArticlePointer ap) throws IOException {
    1659        ArticleInfo ai =  __ap2ai(ap);
    1660        Reader rdr = retrieveArticle(a, ai);
    1661        __ai2ap(ai, ap);
    1662        return rdr;
    1663    }
    1664
    1665    /**
    1666     * @deprecated 3.0 use {@link #retrieveArticleBody(String, ArticleInfo)} instead
    1667     */
    1668    @Deprecated
    1669    public Reader retrieveArticleBody(String a, ArticlePointer ap) throws IOException {
    1670        ArticleInfo ai =  __ap2ai(ap);
    1671        Reader rdr = retrieveArticleBody(a, ai);
    1672        __ai2ap(ai, ap);
    1673        return rdr;
    1674    }
    1675
    1676    /**
    1677     * @deprecated 3.0 use {@link #retrieveArticleHeader(String, ArticleInfo)} instead
    1678     */
    1679    @Deprecated
    1680    public Reader retrieveArticleHeader(String a, ArticlePointer ap) throws IOException {
    1681        ArticleInfo ai =  __ap2ai(ap);
    1682        Reader rdr = retrieveArticleHeader(a, ai);
    1683        __ai2ap(ai, ap);
    1684        return rdr;
    1685    }
    1686
    1687    /**
    1688     * @deprecated 3.0 use {@link #selectArticle(String, ArticleInfo)} instead
    1689     */
    1690    @Deprecated
    1691    public boolean selectArticle(String a, ArticlePointer ap) throws IOException {
    1692        ArticleInfo ai =  __ap2ai(ap);
    1693        boolean b = selectArticle(a, ai);
    1694        __ai2ap(ai, ap);
    1695        return b;
    1696
    1697    }
    1698
    1699    /**
    1700     * @deprecated 3.0 use {@link #selectArticle(ArticleInfo)} instead
    1701     */
    1702    @Deprecated
    1703    public boolean selectArticle(ArticlePointer ap) throws IOException {
    1704        ArticleInfo ai =  __ap2ai(ap);
    1705        boolean b = selectArticle(ai);
    1706        __ai2ap(ai, ap);
    1707        return b;
    1708
    1709    }
    1710
    1711    /**
    1712     * @deprecated 3.0 use {@link #selectNextArticle(ArticleInfo)} instead
    1713     */
    1714    @Deprecated
    1715    public boolean selectNextArticle(ArticlePointer ap) throws IOException {
    1716        ArticleInfo ai =  __ap2ai(ap);
    1717        boolean b = selectNextArticle(ai);
    1718        __ai2ap(ai, ap);
    1719        return b;
    1720
    1721    }
    1722
    1723    /**
    1724     * @deprecated 3.0 use {@link #selectPreviousArticle(ArticleInfo)} instead
    1725     */
    1726    @Deprecated
    1727    public boolean selectPreviousArticle(ArticlePointer ap) throws IOException {
    1728        ArticleInfo ai =  __ap2ai(ap);
    1729        boolean b = selectPreviousArticle(ai);
    1730        __ai2ap(ai, ap);
    1731        return b;
    1732    }
    1733
    1734   // Helper methods
    1735
    1736    private ArticleInfo __ap2ai(@SuppressWarnings("deprecation") ArticlePointer ap) {
    1737        if (ap == null) {
    1738            return null;
    1739        }
    1740        ArticleInfo ai = new ArticleInfo();
    1741        return ai;
    1742    }
    1743
    1744    @SuppressWarnings("deprecation")
    1745    private void __ai2ap(ArticleInfo ai, ArticlePointer ap){
    1746        if (ap != null) { // ai cannot be null
    1747            ap.articleId = ai.articleId;
    1748            ap.articleNumber = (int) ai.articleNumber;
    1749        }
    1750    }
    1751}
    1752
    1753
    1754/* Emacs configuration
    1755 * Local variables:        **
    1756 * mode:             java  **
    1757 * c-basic-offset:   4     **
    1758 * indent-tabs-mode: nil   **
    1759 * End:                    **
    1760 */