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