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