    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(String reply, ArticleInfo pointer)
    098    throws MalformedServerReplyException
    099    {
    100        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 (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(String reply, NewsgroupInfo info)
    129    throws MalformedServerReplyException
    130    {
    131        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 (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(String entry)
    160    {
    161        String tokens[] = entry.split(" ");
    162        if (tokens.length < 4) {
    163            return null;
    164        }
    165        NewsgroupInfo result = new NewsgroupInfo();
    166
    167        int i = 0;
    168
    169        result._setNewsgroup(tokens[i++]);
    170
    171        try
    172        {
    173            long lastNum = Long.parseLong(tokens[i++]);
    174            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 (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(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        Article article = new Article();
    226        article.setSubject(line); // in case parsing fails
    227        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 (NumberFormatException e) {
    238                // ignored, already handled
    239            }
    240        }
    241        return article;
    242    }
    243
    244    private NewsgroupInfo[] __readNewsgroupListing() throws IOException
    245    {
    246
    247        BufferedReader reader = new DotTerminatedMessageReader(_reader_);
    248        // Start of with a big vector because we may be reading a very large
    249        // amount of groups.
    250        Vector<NewsgroupInfo> list = new Vector<NewsgroupInfo>(2048);
    251
    252        String line;
    253        try {
    254            while ((line = reader.readLine()) != null) {
    255                NewsgroupInfo tmp = __parseNewsgroupListEntry(line);
    256                if (tmp != null) {
    257                    list.addElement(tmp);
    258                } else {
    259                    throw new MalformedServerReplyException(line);
    260                }
    261            }
    262        } finally {
    263            reader.close();
    264        }
    265        int size;
    266        if ((size = list.size()) < 1) {
    267            return new NewsgroupInfo[0];
    268        }
    269
    270        NewsgroupInfo[] info = new NewsgroupInfo[size];
    271        list.copyInto(info);
    272
    273        return info;
    274    }
    275
    276
    277    private BufferedReader __retrieve(int command, String articleId, 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(int command, long articleNumber, 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(String articleId, 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(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(long articleNumber, 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(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(String articleId, 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(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(long articleNumber,
    559                                        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(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(String articleId, 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(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(long articleNumber,
    692                                      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(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(String newsgroup, 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(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        StringWriter help = new StringWriter();
    773        BufferedReader reader = new DotTerminatedMessageReader(_reader_);
    774        Util.copyReader(reader, help);
    775        reader.close();
    776        help.close();
    777        return help.toString();
    778    }
    779
    780    /**
    781     * Send a "LIST OVERVIEW.FMT" command to the server.
    782     *
    783     * @return the contents of the Overview format, of {@code null} if the command failed
    784     * @throws IOException on error
    785     */
    786    public String[] listOverviewFmt() throws IOException
    787    {
    788        if (!NNTPReply.isPositiveCompletion(sendCommand("LIST", "OVERVIEW.FMT"))){
    789            return null;
    790        }
    791
    792        BufferedReader reader = new DotTerminatedMessageReader(_reader_);
    793        String line;
    794        ArrayList<String> list = new ArrayList<String>();
    795        while((line=reader.readLine()) != null) {
    796            list.add(line);
    797        }
    798        reader.close();
    799        return list.toArray(new String[list.size()]);
    800    }
    801
    802    /***
    803     * Select an article by its unique identifier (including enclosing
    804     * &lt; and &gt;) and return its article number and id through the
    805     * pointer parameter.  This is achieved through the STAT command.
    806     * According to RFC 977, this will NOT set the current article pointer
    807     * on the server.  To do that, you must reference the article by its
    808     * number.
    809     * <p>
    810     * @param articleId  The unique article identifier of the article that
    811     *    is being selectedd.  If this parameter is null, the
    812     *    body of the current article is selected
    813     * @param pointer    A parameter through which to return the article's
    814     *   number and unique id.  The articleId field cannot always be trusted
    815     *   because of server deviations from RFC 977 reply formats.  You may
    816     *   set this parameter to null if you do not desire to retrieve the
    817     *   returned article information.
    818     * @return True if successful, false if not.
    819     * @throws NNTPConnectionClosedException
    820     *      If the NNTP server prematurely closes the connection as a result
    821     *      of the client being idle or some other reason causing the server
    822     *      to send NNTP reply code 400.  This exception may be caught either
    823     *      as an IOException or independently as itself.
    824     * @throws IOException  If an I/O error occurs while either sending a
    825     *      command to the server or receiving a reply from the server.
    826     ***/
    827    public boolean selectArticle(String articleId, ArticleInfo pointer)
    828    throws IOException
    829    {
    830        if (articleId != null) {
    831            if (!NNTPReply.isPositiveCompletion(stat(articleId))) {
    832                return false;
    833            }
    834        } else {
    835            if (!NNTPReply.isPositiveCompletion(stat())) {
    836                return false;
    837            }
    838        }
    839
    840        if (pointer != null) {
    841            __parseArticlePointer(getReplyString(), pointer);
    842        }
    843
    844        return true;
    845    }
    846
    847    /**
    848     * Same as <code> selectArticle(articleId, (ArticleInfo) null) </code>
    849     * @param articleId the article Id
    850     * @return true if successful
    851     * @throws IOException on error
    852     */
    853    public boolean selectArticle(String articleId) throws IOException
    854    {
    855        return selectArticle(articleId, (ArticleInfo) null);
    856    }
    857
    858    /****
    859     * Same as <code> selectArticle((String) null, articleId) </code>.  Useful
    860     * for retrieving the current article number.
    861     * @param pointer to the article
    862     * @return true if OK
    863     * @throws IOException on error
    864     ***/
    865    public boolean selectArticle(ArticleInfo pointer) throws IOException
    866    {
    867        return selectArticle(null, pointer);
    868    }
    869
    870
    871    /***
    872     * Select an article in the currently selected newsgroup by its number.
    873     * and return its article number and id through the
    874     * pointer parameter.  This is achieved through the STAT command.
    875     * According to RFC 977, this WILL set the current article pointer
    876     * on the server.  Use this command to select an article before retrieving
    877     * it, or to obtain an article's unique identifier given its number.
    878     * <p>
    879     * @param articleNumber The number of the article to select from the
    880     *       currently selected newsgroup.
    881     * @param pointer    A parameter through which to return the article's
    882     *   number and unique id.  Although the articleId field cannot always
    883     *   be trusted because of server deviations from RFC 977 reply formats,
    884     *   we haven't found a server that misformats this information in response
    885     *   to this particular command.  You may set this parameter to null if
    886     *   you do not desire to retrieve the returned article information.
    887     * @return True if successful, false if not.
    888     * @throws NNTPConnectionClosedException
    889     *      If the NNTP server prematurely closes the connection as a result
    890     *      of the client being idle or some other reason causing the server
    891     *      to send NNTP reply code 400.  This exception may be caught either
    892     *      as an IOException or independently as itself.
    893     * @throws IOException  If an I/O error occurs while either sending a
    894     *      command to the server or receiving a reply from the server.
    895     ***/
    896    public boolean selectArticle(long articleNumber, ArticleInfo pointer)
    897    throws IOException
    898    {
    899        if (!NNTPReply.isPositiveCompletion(stat(articleNumber))) {
    900            return false;
    901        }
    902
    903        if (pointer != null) {
    904            __parseArticlePointer(getReplyString(), pointer);
    905        }
    906
    907        return true;
    908    }
    909
    910
    911    /*** Same as <code> selectArticle(articleNumber, null) </code>
    912     * @param articleNumber the numger
    913     * @return true if successful
    914     * @throws IOException on error ***/
    915    public boolean selectArticle(long articleNumber) throws IOException
    916    {
    917        return selectArticle(articleNumber, null);
    918    }
    919
    920
    921    /***
    922     * Select the article preceeding the currently selected article in the
    923     * currently selected newsgroup and return its number and unique id
    924     * through the pointer parameter.  Because of deviating server
    925     * implementations, the articleId information cannot be trusted.  To
    926     * obtain the article identifier, issue a
    927     * <code> selectArticle(pointer.articleNumber, pointer) </code> immediately
    928     * afterward.
    929     * <p>
    930     * @param pointer    A parameter through which to return the article's
    931     *   number and unique id.  The articleId field cannot always be trusted
    932     *   because of server deviations from RFC 977 reply formats.  You may
    933     *   set this parameter to null if you do not desire to retrieve the
    934     *   returned article information.
    935     * @return True if successful, false if not (e.g., there is no previous
    936     *     article).
    937     * @throws NNTPConnectionClosedException
    938     *      If the NNTP server prematurely closes the connection as a result
    939     *      of the client being idle or some other reason causing the server
    940     *      to send NNTP reply code 400.  This exception may be caught either
    941     *      as an IOException or independently as itself.
    942     * @throws IOException  If an I/O error occurs while either sending a
    943     *      command to the server or receiving a reply from the server.
    944     ***/
    945    public boolean selectPreviousArticle(ArticleInfo pointer)
    946    throws IOException
    947    {
    948        if (!NNTPReply.isPositiveCompletion(last())) {
    949            return false;
    950        }
    951
    952        if (pointer != null) {
    953            __parseArticlePointer(getReplyString(), pointer);
    954        }
    955
    956        return true;
    957    }
    958
    959    /*** Same as <code> selectPreviousArticle((ArticleInfo) null) </code>
    960     * @return true if successful
    961     * @throws IOException on error ***/
    962    public boolean selectPreviousArticle() throws IOException
    963    {
    964        return selectPreviousArticle((ArticleInfo) null);
    965    }
    966
    967
    968    /***
    969     * Select the article following the currently selected article in the
    970     * currently selected newsgroup and return its number and unique id
    971     * through the pointer parameter.  Because of deviating server
    972     * implementations, the articleId information cannot be trusted.  To
    973     * obtain the article identifier, issue a
    974     * <code> selectArticle(pointer.articleNumber, pointer) </code> immediately
    975     * afterward.
    976     * <p>
    977     * @param pointer    A parameter through which to return the article's
    978     *   number and unique id.  The articleId field cannot always be trusted
    979     *   because of server deviations from RFC 977 reply formats.  You may
    980     *   set this parameter to null if you do not desire to retrieve the
    981     *   returned article information.
    982     * @return True if successful, false if not (e.g., there is no following
    983     *         article).
    984     * @throws NNTPConnectionClosedException
    985     *      If the NNTP server prematurely closes the connection as a result
    986     *      of the client being idle or some other reason causing the server
    987     *      to send NNTP reply code 400.  This exception may be caught either
    988     *      as an IOException or independently as itself.
    989     * @throws IOException  If an I/O error occurs while either sending a
    990     *      command to the server or receiving a reply from the server.
    991     ***/
    992    public boolean selectNextArticle(ArticleInfo pointer) throws IOException
    993    {
    994        if (!NNTPReply.isPositiveCompletion(next())) {
    995            return false;
    996        }
    997
    998        if (pointer != null) {
    999            __parseArticlePointer(getReplyString(), pointer);
    1000        }
    1001
    1002        return true;
    1003    }
    1004
    1005
    1006    /*** Same as <code> selectNextArticle((ArticleInfo) null) </code>
    1007     * @return true if successful
    1008     * @throws IOException on error ***/
    1009    public boolean selectNextArticle() throws IOException
    1010    {
    1011        return selectNextArticle((ArticleInfo) null);
    1012    }
    1013
    1014
    1015    /***
    1016     * List all newsgroups served by the NNTP server.  If no newsgroups
    1017     * are served, a zero length array will be returned.  If the command
    1018     * fails, null will be returned.
    1019     * The method uses the "LIST" command.
    1020     * <p>
    1021     * @return An array of NewsgroupInfo instances containing the information
    1022     *    for each newsgroup served by the NNTP server.   If no newsgroups
    1023     *    are served, a zero length array will be returned.  If the command
    1024     *    fails, null will be returned.
    1025     * @throws NNTPConnectionClosedException
    1026     *      If the NNTP server prematurely closes the connection as a result
    1027     *      of the client being idle or some other reason causing the server
    1028     *      to send NNTP reply code 400.  This exception may be caught either
    1029     *      as an IOException or independently as itself.
    1030     * @throws IOException  If an I/O error occurs while either sending a
    1031     *      command to the server or receiving a reply from the server.
    1032     * @see #iterateNewsgroupListing()
    1033     * @see #iterateNewsgroups()
    1034     ***/
    1035    public NewsgroupInfo[] listNewsgroups() throws IOException
    1036    {
    1037        if (!NNTPReply.isPositiveCompletion(list())) {
    1038            return null;
    1039        }
    1040
    1041        return __readNewsgroupListing();
    1042    }
    1043
    1044    /**
    1045     * List all newsgroups served by the NNTP server.  If no newsgroups
    1046     * are served, no entries will be returned.
    1047     * The method uses the "LIST" command.
    1048     * <p>
    1049     * @return An iterable of NewsgroupInfo instances containing the information
    1050     *    for each newsgroup served by the NNTP server.   If no newsgroups
    1051     *    are served, no entries will be returned.
    1052     * @throws NNTPConnectionClosedException
    1053     *      If the NNTP server prematurely closes the connection as a result
    1054     *      of the client being idle or some other reason causing the server
    1055     *      to send NNTP reply code 400.  This exception may be caught either
    1056     *      as an IOException or independently as itself.
    1057     * @throws IOException  If an I/O error occurs while either sending a
    1058     *      command to the server or receiving a reply from the server.
    1059     * @since 3.0
    1060     */
    1061    public Iterable<String> iterateNewsgroupListing() throws IOException {
    1062        if (NNTPReply.isPositiveCompletion(list())) {
    1063            return new ReplyIterator(_reader_);
    1064        }
    1065        throw new IOException("LIST command failed: "+getReplyString());
    1066    }
    1067
    1068    /**
    1069     * List all newsgroups served by the NNTP server.  If no newsgroups
    1070     * are served, no entries will be returned.
    1071     * The method uses the "LIST" command.
    1072     * <p>
    1073     * @return An iterable of Strings containing the raw information
    1074     *    for each newsgroup served by the NNTP server.   If no newsgroups
    1075     *    are served, no entries will be returned.
    1076     * @throws NNTPConnectionClosedException
    1077     *      If the NNTP server prematurely closes the connection as a result
    1078     *      of the client being idle or some other reason causing the server
    1079     *      to send NNTP reply code 400.  This exception may be caught either
    1080     *      as an IOException or independently as itself.
    1081     * @throws IOException  If an I/O error occurs while either sending a
    1082     *      command to the server or receiving a reply from the server.
    1083     * @since 3.0
    1084     */
    1085    public Iterable<NewsgroupInfo> iterateNewsgroups() throws IOException {
    1086        return new NewsgroupIterator(iterateNewsgroupListing());
    1087    }
    1088
    1089    /**
    1090     * List the newsgroups that match a given pattern.
    1091     * Uses the "LIST ACTIVE" command.
    1092     * <p>
    1093     * @param wildmat a pseudo-regex pattern (cf. RFC 2980)
    1094     * @return An array of NewsgroupInfo instances containing the information
    1095     *    for each newsgroup served by the NNTP server corresponding to the
    1096     *    supplied pattern.   If no such newsgroups are served, a zero length
    1097     *    array will be returned.  If the command fails, null will be returned.
    1098     * @throws IOException on error
    1099     * @see #iterateNewsgroupListing(String)
    1100     * @see #iterateNewsgroups(String)
    1101     */
    1102    public NewsgroupInfo[] listNewsgroups(String wildmat) throws IOException
    1103    {
    1104        if(!NNTPReply.isPositiveCompletion(listActive(wildmat))) {
    1105            return null;
    1106        }
    1107        return __readNewsgroupListing();
    1108    }
    1109
    1110
    1111    /**
    1112     * List the newsgroups that match a given pattern.
    1113     * Uses the "LIST ACTIVE" command.
    1114     * <p>
    1115     * @param wildmat a pseudo-regex pattern (cf. RFC 2980)
    1116     * @return An iterable of Strings containing the raw information
    1117     *    for each newsgroup served by the NNTP server corresponding to the
    1118     *    supplied pattern.   If no such newsgroups are served, no entries
    1119     *    will be returned.
    1120     * @throws IOException on error
    1121     * @since 3.0
    1122     */
    1123    public Iterable<String> iterateNewsgroupListing(String wildmat) throws IOException {
    1124        if(NNTPReply.isPositiveCompletion(listActive(wildmat))) {
    1125            return new ReplyIterator(_reader_);
    1126        }
    1127        throw new IOException("LIST ACTIVE "+wildmat+" command failed: "+getReplyString());
    1128    }
    1129
    1130    /**
    1131     * List the newsgroups that match a given pattern.
    1132     * Uses the "LIST ACTIVE" command.
    1133     * <p>
    1134     * @param wildmat a pseudo-regex pattern (cf. RFC 2980)
    1135     * @return An iterable NewsgroupInfo instances containing the information
    1136     *    for each newsgroup served by the NNTP server corresponding to the
    1137     *    supplied pattern.   If no such newsgroups are served, no entries
    1138     *    will be returned.
    1139     * @throws IOException on error
    1140     * @since 3.0
    1141     */
    1142    public Iterable<NewsgroupInfo> iterateNewsgroups(String wildmat) throws IOException {
    1143        return new NewsgroupIterator(iterateNewsgroupListing(wildmat));
    1144    }
    1145
    1146    /***
    1147     * List all new newsgroups added to the NNTP server since a particular
    1148     * date subject to the conditions of the specified query.  If no new
    1149     * newsgroups were added, a zero length array will be returned.  If the
    1150     * command fails, null will be returned.
    1151     * This uses the "NEWGROUPS" command.
    1152     * <p>
    1153     * @param query  The query restricting how to search for new newsgroups.
    1154     * @return An array of NewsgroupInfo instances containing the information
    1155     *    for each new newsgroup added to the NNTP server.   If no newsgroups
    1156     *    were added, a zero length array will be returned.  If the command
    1157     *    fails, null will be returned.
    1158     * @throws NNTPConnectionClosedException
    1159     *      If the NNTP server prematurely closes the connection as a result
    1160     *      of the client being idle or some other reason causing the server
    1161     *      to send NNTP reply code 400.  This exception may be caught either
    1162     *      as an IOException or independently as itself.
    1163     * @throws IOException  If an I/O error occurs while either sending a
    1164     *      command to the server or receiving a reply from the server.
    1165     * @see #iterateNewNewsgroups(NewGroupsOrNewsQuery)
    1166     * @see #iterateNewNewsgroupListing(NewGroupsOrNewsQuery)
    1167     ***/
    1168    public NewsgroupInfo[] listNewNewsgroups(NewGroupsOrNewsQuery query)
    1169    throws IOException
    1170    {
    1171        if (!NNTPReply.isPositiveCompletion(newgroups(
    1172                                                query.getDate(), query.getTime(),
    1173                                                query.isGMT(), query.getDistributions())))
    1174        {
    1175            return null;
    1176        }
    1177
    1178        return __readNewsgroupListing();
    1179    }
    1180
    1181    /**
    1182     * List all new newsgroups added to the NNTP server since a particular
    1183     * date subject to the conditions of the specified query.  If no new
    1184     * newsgroups were added, no entries will be returned.
    1185     * This uses the "NEWGROUPS" command.
    1186     * <p>
    1187     * @param query  The query restricting how to search for new newsgroups.
    1188     * @return An iterable of Strings containing the raw information
    1189     *    for each new newsgroup added to the NNTP server.   If no newsgroups
    1190     *    were added, no entries will be returned.
    1191     * @throws NNTPConnectionClosedException
    1192     *      If the NNTP server prematurely closes the connection as a result
    1193     *      of the client being idle or some other reason causing the server
    1194     *      to send NNTP reply code 400.  This exception may be caught either
    1195     *      as an IOException or independently as itself.
    1196     * @throws IOException  If an I/O error occurs while either sending a
    1197     *      command to the server or receiving a reply from the server.
    1198     * @since 3.0
    1199     */
    1200    public Iterable<String> iterateNewNewsgroupListing(NewGroupsOrNewsQuery query) throws IOException {
    1201        if (NNTPReply.isPositiveCompletion(newgroups(
    1202                query.getDate(), query.getTime(),
    1203                query.isGMT(), query.getDistributions()))) {
    1204            return new ReplyIterator(_reader_);
    1205        }
    1206        throw new IOException("NEWGROUPS command failed: "+getReplyString());
    1207    }
    1208
    1209    /**
    1210     * List all new newsgroups added to the NNTP server since a particular
    1211     * date subject to the conditions of the specified query.  If no new
    1212     * newsgroups were added, no entries will be returned.
    1213     * This uses the "NEWGROUPS" command.
    1214     * <p>
    1215     * @param query  The query restricting how to search for new newsgroups.
    1216     * @return An iterable of NewsgroupInfo instances containing the information
    1217     *    for each new newsgroup added to the NNTP server.   If no newsgroups
    1218     *    were added, no entries will be returned.
    1219     * @throws NNTPConnectionClosedException
    1220     *      If the NNTP server prematurely closes the connection as a result
    1221     *      of the client being idle or some other reason causing the server
    1222     *      to send NNTP reply code 400.  This exception may be caught either
    1223     *      as an IOException or independently as itself.
    1224     * @throws IOException  If an I/O error occurs while either sending a
    1225     *      command to the server or receiving a reply from the server.
    1226     * @since 3.0
    1227     */
    1228    public Iterable<NewsgroupInfo> iterateNewNewsgroups(NewGroupsOrNewsQuery query) throws IOException {
    1229        return new NewsgroupIterator(iterateNewNewsgroupListing(query));
    1230    }
    1231
    1232    /***
    1233     * List all new articles added to the NNTP server since a particular
    1234     * date subject to the conditions of the specified query.  If no new
    1235     * new news is found, a zero length array will be returned.  If the
    1236     * command fails, null will be returned.  You must add at least one
    1237     * newsgroup to the query, else the command will fail.  Each String
    1238     * in the returned array is a unique message identifier including the
    1239     * enclosing &lt; and &gt;.
    1240     * This uses the "NEWNEWS" command.
    1241     * <p>
    1242     * @param query  The query restricting how to search for new news.  You
    1243     *    must add at least one newsgroup to the query.
    1244     * @return An array of String instances containing the unique message
    1245     *    identifiers for each new article added to the NNTP server.  If no
    1246     *    new news is found, a zero length array will be returned.  If the
    1247     *    command fails, null will be returned.
    1248     * @throws NNTPConnectionClosedException
    1249     *      If the NNTP server prematurely closes the connection as a result
    1250     *      of the client being idle or some other reason causing the server
    1251     *      to send NNTP reply code 400.  This exception may be caught either
    1252     *      as an IOException or independently as itself.
    1253     * @throws IOException  If an I/O error occurs while either sending a
    1254     *      command to the server or receiving a reply from the server.
    1255     *
    1256     * @see #iterateNewNews(NewGroupsOrNewsQuery)
    1257     ***/
    1258    public String[] listNewNews(NewGroupsOrNewsQuery query)
    1259    throws IOException
    1260    {
    1261        if (!NNTPReply.isPositiveCompletion(
    1262                newnews(query.getNewsgroups(), query.getDate(), query.getTime(),
    1263                        query.isGMT(), query.getDistributions()))) {
    1264            return null;
    1265        }
    1266
    1267        Vector<String> list = new Vector<String>();
    1268        BufferedReader reader = new DotTerminatedMessageReader(_reader_);
    1269
    1270        String line;
    1271        try {
    1272            while ((line = reader.readLine()) != null) {
    1273                list.addElement(line);
    1274            }
    1275        } finally {
    1276            reader.close();
    1277        }
    1278
    1279        int size = list.size();
    1280        if (size < 1) {
    1281            return new String[0];
    1282        }
    1283
    1284        String[] result = new String[size];
    1285        list.copyInto(result);
    1286
    1287        return result;
    1288    }
    1289
    1290    /**
    1291     * List all new articles added to the NNTP server since a particular
    1292     * date subject to the conditions of the specified query.  If no new
    1293     * new news is found, no entries will be returned.
    1294     * This uses the "NEWNEWS" command.
    1295     * You must add at least one newsgroup to the query, else the command will fail.
    1296     * Each String which is returned is a unique message identifier including the
    1297     * enclosing &lt; and &gt;.
    1298     * <p>
    1299     * @param query  The query restricting how to search for new news.  You
    1300     *    must add at least one newsgroup to the query.
    1301     * @return An iterator of String instances containing the unique message
    1302     *    identifiers for each new article added to the NNTP server.  If no
    1303     *    new news is found, no strings will be returned.
    1304     * @throws NNTPConnectionClosedException
    1305     *      If the NNTP server prematurely closes the connection as a result
    1306     *      of the client being idle or some other reason causing the server
    1307     *      to send NNTP reply code 400.  This exception may be caught either
    1308     *      as an IOException or independently as itself.
    1309     * @throws IOException  If an I/O error occurs while either sending a
    1310     *      command to the server or receiving a reply from the server.
    1311     * @since 3.0
    1312     */
    1313    public Iterable<String> iterateNewNews(NewGroupsOrNewsQuery query) throws IOException {
    1314        if (NNTPReply.isPositiveCompletion(newnews(
    1315                query.getNewsgroups(), query.getDate(), query.getTime(),
    1316                query.isGMT(), query.getDistributions()))) {
    1317            return new ReplyIterator(_reader_);
    1318        }
    1319        throw new IOException("NEWNEWS command failed: "+getReplyString());
    1320    }
    1321
    1322    /***
    1323     * There are a few NNTPClient methods that do not complete the
    1324     * entire sequence of NNTP commands to complete a transaction.  These
    1325     * commands require some action by the programmer after the reception
    1326     * of a positive preliminary command.  After the programmer's code
    1327     * completes its actions, it must call this method to receive
    1328     * the completion reply from the server and verify the success of the
    1329     * entire transaction.
    1330     * <p>
    1331     * For example
    1332     * <pre>
    1333     * writer = client.postArticle();
    1334     * if(writer == null) // failure
    1335     *   return false;
    1336     * header = new SimpleNNTPHeader("foobar@foo.com", "Just testing");
    1337     * header.addNewsgroup("alt.test");
    1338     * writer.write(header.toString());
    1339     * writer.write("This is just a test");
    1340     * writer.close();
    1341     * if(!client.completePendingCommand()) // failure
    1342     *   return false;
    1343     * </pre>
    1344     * <p>
    1345     * @return True if successfully completed, false if not.
    1346     * @throws NNTPConnectionClosedException
    1347     *      If the NNTP server prematurely closes the connection as a result
    1348     *      of the client being idle or some other reason causing the server
    1349     *      to send NNTP reply code 400.  This exception may be caught either
    1350     *      as an IOException or independently as itself.
    1351     * @throws IOException  If an I/O error occurs while either sending a
    1352     *      command to the server or receiving a reply from the server.
    1353     ***/
    1354    public boolean completePendingCommand() throws IOException
    1355    {
    1356        return NNTPReply.isPositiveCompletion(getReply());
    1357    }
    1358
    1359    /***
    1360     * Post an article to the NNTP server.  This method returns a
    1361     * DotTerminatedMessageWriter instance to which the article can be
    1362     * written.  Null is returned if the posting attempt fails.  You
    1363     * should check {@link NNTP#isAllowedToPost isAllowedToPost() }
    1364     *  before trying to post.  However, a posting
    1365     * attempt can fail due to malformed headers.
    1366     * <p>
    1367     * You must not issue any commands to the NNTP server (i.e., call any
    1368     * (other methods) until you finish writing to the returned Writer
    1369     * instance and close it.  The NNTP protocol uses the same stream for
    1370     * issuing commands as it does for returning results.  Therefore the
    1371     * returned Writer actually writes directly to the NNTP connection.
    1372     * After you close the writer, you can execute new commands.  If you
    1373     * do not follow these requirements your program will not work properly.
    1374     * <p>
    1375     * Different NNTP servers will require different header formats, but
    1376     * you can use the provided
    1377     * {@link org.apache.commons.net.nntp.SimpleNNTPHeader}
    1378     * class to construct the bare minimum acceptable header for most
    1379     * news readers.  To construct more complicated headers you should
    1380     * refer to RFC 822.  When the Java Mail API is finalized, you will be
    1381     * able to use it to compose fully compliant Internet text messages.
    1382     * The DotTerminatedMessageWriter takes care of doubling line-leading
    1383     * dots and ending the message with a single dot upon closing, so all
    1384     * you have to worry about is writing the header and the message.
    1385     * <p>
    1386     * Upon closing the returned Writer, you need to call
    1387     * {@link #completePendingCommand  completePendingCommand() }
    1388     * to finalize the posting and verify its success or failure from
    1389     * the server reply.
    1390     * <p>
    1391     * @return A DotTerminatedMessageWriter to which the article (including
    1392     *      header) can be written.  Returns null if the command fails.
    1393     * @throws IOException  If an I/O error occurs while either sending a
    1394     *      command to the server or receiving a reply from the server.
    1395     ***/
    1396
    1397    public Writer postArticle() throws IOException
    1398    {
    1399        if (!NNTPReply.isPositiveIntermediate(post())) {
    1400            return null;
    1401        }
    1402
    1403        return new DotTerminatedMessageWriter(_writer_);
    1404    }
    1405
    1406
    1407    public Writer forwardArticle(String articleId) throws IOException
    1408    {
    1409        if (!NNTPReply.isPositiveIntermediate(ihave(articleId))) {
    1410            return null;
    1411        }
    1412
    1413        return new DotTerminatedMessageWriter(_writer_);
    1414    }
    1415
    1416
    1417    /***
    1418     * Logs out of the news server gracefully by sending the QUIT command.
    1419     * However, you must still disconnect from the server before you can open
    1420     * a new connection.
    1421     * <p>
    1422     * @return True if successfully completed, false if not.
    1423     * @throws IOException  If an I/O error occurs while either sending a
    1424     *      command to the server or receiving a reply from the server.
    1425     ***/
    1426    public boolean logout() throws IOException
    1427    {
    1428        return NNTPReply.isPositiveCompletion(quit());
    1429    }
    1430
    1431
    1432    /**
    1433     * Log into a news server by sending the AUTHINFO USER/AUTHINFO
    1434     * PASS command sequence. This is usually sent in response to a
    1435     * 480 reply code from the NNTP server.
    1436     * <p>
    1437     * @param username a valid username
    1438     * @param password the corresponding password
    1439     * @return True for successful login, false for a failure
    1440     * @throws IOException on error
    1441     */
    1442    public boolean authenticate(String username, String password)
    1443        throws IOException
    1444    {
    1445        int replyCode = authinfoUser(username);
    1446
    1447        if (replyCode == NNTPReply.MORE_AUTH_INFO_REQUIRED)
    1448            {
    1449                replyCode = authinfoPass(password);
    1450
    1451                if (replyCode == NNTPReply.AUTHENTICATION_ACCEPTED)
    1452                    {
    1453                        _isAllowedToPost = true;
    1454                        return true;
    1455                    }
    1456            }
    1457        return false;
    1458    }
    1459
    1460    /***
    1461     * Private implementation of XOVER functionality.
    1462     *
    1463     * See {@link NNTP#xover}
    1464     * for legal agument formats. Alternatively, read RFC 2980 :-)
    1465     * <p>
    1466     * @param articleRange
    1467     * @return Returns a DotTerminatedMessageReader if successful, null
    1468     *         otherwise
    1469     * @throws IOException
    1470     */
    1471    private BufferedReader __retrieveArticleInfo(String articleRange)
    1472        throws IOException
    1473    {
    1474        if (!NNTPReply.isPositiveCompletion(xover(articleRange))) {
    1475            return null;
    1476        }
    1477
    1478        return new DotTerminatedMessageReader(_reader_);
    1479    }
    1480
    1481    /**
    1482     * Return article headers for a specified post.
    1483     * <p>
    1484     * @param articleNumber the article to retrieve headers for
    1485     * @return a DotTerminatedReader if successful, null otherwise
    1486     * @throws IOException on error
    1487     */
    1488    public BufferedReader retrieveArticleInfo(long articleNumber) throws IOException
    1489    {
    1490        return __retrieveArticleInfo(Long.toString(articleNumber));
    1491    }
    1492
    1493    /**
    1494     * Return article headers for all articles between lowArticleNumber
    1495     * and highArticleNumber, inclusively. Uses the XOVER command.
    1496     * <p>
    1497     * @param lowArticleNumber low number
    1498     * @param highArticleNumber high number
    1499     * @return a DotTerminatedReader if successful, null otherwise
    1500     * @throws IOException on error
    1501     */
    1502    public BufferedReader retrieveArticleInfo(long lowArticleNumber,
    1503            long highArticleNumber)
    1504        throws IOException
    1505    {
    1506        return
    1507            __retrieveArticleInfo(lowArticleNumber + "-" +
    1508                                             highArticleNumber);
    1509    }
    1510
    1511    /**
    1512     * Return article headers for all articles between lowArticleNumber
    1513     * and highArticleNumber, inclusively, using the XOVER command.
    1514     * <p>
    1515     * @param lowArticleNumber low
    1516     * @param highArticleNumber high
    1517     * @return an Iterable of Articles
    1518     * @throws IOException if the command failed
    1519     * @since 3.0
    1520     */
    1521    public Iterable<Article> iterateArticleInfo(long lowArticleNumber, long highArticleNumber)
    1522        throws IOException
    1523    {
    1524        BufferedReader info = retrieveArticleInfo(lowArticleNumber,highArticleNumber);
    1525        if (info == null) {
    1526            throw new IOException("XOVER command failed: "+getReplyString());
    1527        }
    1528        // N.B. info is already DotTerminated, so don't rewrap
    1529        return new ArticleIterator(new ReplyIterator(info, false));
    1530    }
    1531
    1532    /***
    1533     * Private implementation of XHDR functionality.
    1534     *
    1535     * See {@link NNTP#xhdr}
    1536     * for legal agument formats. Alternatively, read RFC 1036.
    1537     * <p>
    1538     * @param header
    1539     * @param articleRange
    1540     * @return Returns a DotTerminatedMessageReader if successful, null
    1541     *         otherwise
    1542     * @throws IOException
    1543     */
    1544    private BufferedReader __retrieveHeader(String header, String articleRange)
    1545        throws IOException
    1546    {
    1547        if (!NNTPReply.isPositiveCompletion(xhdr(header, articleRange))) {
    1548            return null;
    1549        }
    1550
    1551        return new DotTerminatedMessageReader(_reader_);
    1552    }
    1553
    1554    /**
    1555     * Return an article header for a specified post.
    1556     * <p>
    1557     * @param header the header to retrieve
    1558     * @param articleNumber the article to retrieve the header for
    1559     * @return a DotTerminatedReader if successful, null otherwise
    1560     * @throws IOException on error
    1561     */
    1562    public BufferedReader retrieveHeader(String header, long articleNumber)
    1563        throws IOException
    1564    {
    1565        return __retrieveHeader(header, Long.toString(articleNumber));
    1566    }
    1567
    1568    /**
    1569     * Return an article header for all articles between lowArticleNumber
    1570     * and highArticleNumber, inclusively.
    1571     * <p>
    1572     * @param header the header
    1573     * @param lowArticleNumber to fetch
    1574     * @param highArticleNumber to fetch
    1575     * @return a DotTerminatedReader if successful, null otherwise
    1576     * @throws IOException on error
    1577     */
    1578    public BufferedReader retrieveHeader(String header, long lowArticleNumber,
    1579                                 long highArticleNumber)
    1580        throws IOException
    1581    {
    1582        return
    1583            __retrieveHeader(header,lowArticleNumber + "-" + highArticleNumber);
    1584    }
    1585
    1586
    1587
    1588
    1589
    1590    // DEPRECATED METHODS - for API compatibility only - DO NOT USE
    1591    // ============================================================
    1592
    1593
    1594
    1595    /**
    1596     * @param header the header
    1597     * @param lowArticleNumber to fetch
    1598     * @param highArticleNumber to fetch
    1599     * @return a DotTerminatedReader if successful, null otherwise
    1600     * @throws IOException on error
    1601     * @deprecated 3.0 use {@link #retrieveHeader(String, long, long)} instead
    1602     */
    1603    @Deprecated
    1604    public Reader retrieveHeader(String header, int lowArticleNumber, int highArticleNumber)
    1605        throws IOException
    1606    {
    1607        return retrieveHeader(header, (long) lowArticleNumber, (long) highArticleNumber);
    1608    }
    1609
    1610    /**
    1611     * @param lowArticleNumber to fetch
    1612     * @param highArticleNumber to fetch
    1613     * @return a DotTerminatedReader if successful, null otherwise
    1614     * @throws IOException on error
    1615     * @deprecated 3.0 use {@link #retrieveArticleInfo(long, long)} instead
    1616     */
    1617    @Deprecated
    1618    public Reader retrieveArticleInfo(int lowArticleNumber, int highArticleNumber) throws IOException {
    1619        return retrieveArticleInfo((long) lowArticleNumber, (long) highArticleNumber);
    1620    }
    1621
    1622    /**
    1623     * @param a tba
    1624     * @param b  tba
    1625     * @return  tba
    1626     * @throws IOException tba
    1627     * @deprecated 3.0 use {@link #retrieveHeader(String, long)} instead
    1628     */
    1629    @Deprecated
    1630    public Reader retrieveHeader(String a, int b) throws IOException {
    1631        return retrieveHeader(a, (long) b);
    1632    }
    1633
    1634    /**
    1635     * @param a  tba
    1636     * @param ap  tba
    1637     * @return  tba
    1638     * @throws IOException tba
    1639     * @deprecated 3.0 use {@link #selectArticle(long, ArticleInfo)} instead
    1640     */
    1641    @Deprecated
    1642    public boolean selectArticle(int a, ArticlePointer ap) throws IOException {
    1643        ArticleInfo ai =  __ap2ai(ap);
    1644        boolean b = selectArticle(a, ai);
    1645        __ai2ap(ai, ap);
    1646        return b;
    1647    }
    1648
    1649    /**
    1650     * @param lowArticleNumber to fetch
    1651     * @return a DotTerminatedReader if successful, null otherwise
    1652     * @throws IOException  tba
    1653     * @deprecated 3.0 use {@link #retrieveArticleInfo(long)} instead
    1654     */
    1655    @Deprecated
    1656    public Reader retrieveArticleInfo(int lowArticleNumber) throws IOException {
    1657        return retrieveArticleInfo((long) lowArticleNumber);
    1658    }
    1659
    1660    /**
    1661     * @param a  tba
    1662     * @return  tba
    1663     * @throws IOException  tba
    1664     * @deprecated 3.0 use {@link #selectArticle(long)} instead
    1665     */
    1666    @Deprecated
    1667    public boolean selectArticle(int a) throws IOException {
    1668        return selectArticle((long) a);
    1669    }
    1670
    1671    /**
    1672     * @param a  tba
    1673     * @return  tba
    1674     * @throws IOException  tba
    1675     * @deprecated 3.0 use {@link #retrieveArticleHeader(long)} instead
    1676     */
    1677    @Deprecated
    1678    public Reader retrieveArticleHeader(int a) throws IOException {
    1679        return retrieveArticleHeader((long) a);
    1680    }
    1681
    1682    /**
    1683     * @param a  tba
    1684     * @param ap  tba
    1685     * @return  tba
    1686     * @throws IOException  tba
    1687     * @deprecated 3.0 use {@link #retrieveArticleHeader(long, ArticleInfo)} instead
    1688     */
    1689    @Deprecated
    1690    public Reader retrieveArticleHeader(int a, ArticlePointer ap) throws IOException {
    1691        ArticleInfo ai =  __ap2ai(ap);
    1692        Reader rdr = retrieveArticleHeader(a, ai);
    1693        __ai2ap(ai, ap);
    1694        return rdr;
    1695    }
    1696
    1697    /**
    1698     * @param a  tba
    1699     * @return  tba
    1700     * @throws IOException  tba
    1701     * @deprecated 3.0 use {@link #retrieveArticleBody(long)} instead
    1702     */
    1703    @Deprecated
    1704    public Reader retrieveArticleBody(int a) throws IOException {
    1705        return retrieveArticleBody((long) a);
    1706    }
    1707
    1708    /**
    1709     * @param articleNumber  The number of the the article to retrieve.
    1710     * @param pointer A parameter through which to return the article's number and unique id
    1711     * @return A DotTerminatedMessageReader instance from which the article
    1712     *         can be read.  null if the article does not exist.
    1713     * @throws IOException on error
    1714     * @deprecated 3.0 use {@link #retrieveArticle(long, ArticleInfo)} instead
    1715     */
    1716    @Deprecated
    1717    public Reader retrieveArticle(int articleNumber, ArticlePointer pointer) throws IOException {
    1718        ArticleInfo ai =  __ap2ai(pointer);
    1719        Reader rdr = retrieveArticle(articleNumber, ai);
    1720        __ai2ap(ai, pointer);
    1721        return rdr;
    1722    }
    1723
    1724    /**
    1725     * @param articleNumber The number of the the article to retrieve
    1726     * @return A DotTerminatedMessageReader instance from which the article
    1727     *         can be read.  null if the article does not exist.
    1728     * @throws IOException on error
    1729     * @deprecated 3.0 use {@link #retrieveArticle(long)} instead
    1730     */
    1731    @Deprecated
    1732    public Reader retrieveArticle(int articleNumber) throws IOException {
    1733        return retrieveArticle((long) articleNumber);
    1734    }
    1735
    1736    /**
    1737     * @param a  tba
    1738     * @param ap  tba
    1739     * @return  tba
    1740     * @throws IOException  tba
    1741     * @deprecated 3.0 use {@link #retrieveArticleBody(long, ArticleInfo)} instead
    1742     */
    1743    @Deprecated
    1744    public Reader retrieveArticleBody(int a, ArticlePointer ap) throws IOException {
    1745        ArticleInfo ai =  __ap2ai(ap);
    1746        Reader rdr = retrieveArticleBody(a, ai);
    1747        __ai2ap(ai, ap);
    1748        return rdr;
    1749    }
    1750
    1751    /**
    1752     * @param articleId The unique article identifier of the article to retrieve
    1753     * @param pointer A parameter through which to return the article's number and unique id
    1754     * @deprecated 3.0 use {@link #retrieveArticle(String, ArticleInfo)} instead
    1755     * @return A DotTerminatedMessageReader instance from which the article can be read.
    1756     * null if the article does not exist.
    1757     * @throws IOException on error
    1758     */
    1759    @Deprecated
    1760    public Reader retrieveArticle(String articleId, ArticlePointer pointer) throws IOException {
    1761        ArticleInfo ai =  __ap2ai(pointer);
    1762        Reader rdr = retrieveArticle(articleId, ai);
    1763        __ai2ap(ai, pointer);
    1764        return rdr;
    1765    }
    1766
    1767    /**
    1768     * @param articleId The unique article identifier of the article to retrieve
    1769     * @param pointer A parameter through which to return the article's number and unique id
    1770     * @return A DotTerminatedMessageReader instance from which the article
    1771     *         body can be read.  null if the article does not exist.
    1772     * @throws IOException on error
    1773     * @deprecated 3.0 use {@link #retrieveArticleBody(String, ArticleInfo)} instead
    1774     */
    1775    @Deprecated
    1776    public Reader retrieveArticleBody(String articleId, ArticlePointer pointer) throws IOException {
    1777        ArticleInfo ai =  __ap2ai(pointer);
    1778        Reader rdr = retrieveArticleBody(articleId, ai);
    1779        __ai2ap(ai, pointer);
    1780        return rdr;
    1781    }
    1782
    1783    /**
    1784     * @param articleId The unique article identifier of the article to retrieve
    1785     * @param pointer A parameter through which to return the article's number and unique id
    1786     * @return A DotTerminatedMessageReader instance from which the article
    1787     *         body can be read.  null if the article does not exist.
    1788     * @throws IOException on error
    1789     * @deprecated 3.0 use {@link #retrieveArticleHeader(String, ArticleInfo)} instead
    1790     */
    1791    @Deprecated
    1792    public Reader retrieveArticleHeader(String articleId, ArticlePointer pointer) throws IOException {
    1793        ArticleInfo ai =  __ap2ai(pointer);
    1794        Reader rdr = retrieveArticleHeader(articleId, ai);
    1795        __ai2ap(ai, pointer);
    1796        return rdr;
    1797    }
    1798
    1799    /**
    1800     * @param articleId The unique article identifier of the article to retrieve
    1801     * @param pointer A parameter through which to return the article's number and unique id
    1802     * @return A DotTerminatedMessageReader instance from which the article
    1803     *         body can be read.  null if the article does not exist.
    1804     * @throws IOException on error
    1805     * @deprecated 3.0 use {@link #selectArticle(String, ArticleInfo)} instead
    1806     */
    1807    @Deprecated
    1808    public boolean selectArticle(String articleId, ArticlePointer pointer) throws IOException {
    1809        ArticleInfo ai =  __ap2ai(pointer);
    1810        boolean b = selectArticle(articleId, ai);
    1811        __ai2ap(ai, pointer);
    1812        return b;
    1813
    1814    }
    1815
    1816    /**
    1817     * @param pointer A parameter through which to return the article's number and unique id
    1818     * @return True if successful, false if not.
    1819     * @throws IOException on error
    1820     * @deprecated 3.0 use {@link #selectArticle(ArticleInfo)} instead
    1821     */
    1822    @Deprecated
    1823    public boolean selectArticle(ArticlePointer pointer) throws IOException {
    1824        ArticleInfo ai =  __ap2ai(pointer);
    1825        boolean b = selectArticle(ai);
    1826        __ai2ap(ai, pointer);
    1827        return b;
    1828
    1829    }
    1830
    1831    /**
    1832     * @param pointer A parameter through which to return the article's number and unique id
    1833     * @return True if successful, false if not.
    1834     * @throws IOException on error
    1835     * @deprecated 3.0 use {@link #selectNextArticle(ArticleInfo)} instead
    1836     */
    1837    @Deprecated
    1838    public boolean selectNextArticle(ArticlePointer pointer) throws IOException {
    1839        ArticleInfo ai =  __ap2ai(pointer);
    1840        boolean b = selectNextArticle(ai);
    1841        __ai2ap(ai, pointer);
    1842        return b;
    1843
    1844    }
    1845
    1846    /**
    1847     * @param pointer A parameter through which to return the article's number and unique id
    1848     * @return True if successful, false if not.
    1849     * @throws IOException on error
    1850     * @deprecated 3.0 use {@link #selectPreviousArticle(ArticleInfo)} instead
    1851     */
    1852    @Deprecated
    1853    public boolean selectPreviousArticle(ArticlePointer pointer) throws IOException {
    1854        ArticleInfo ai =  __ap2ai(pointer);
    1855        boolean b = selectPreviousArticle(ai);
    1856        __ai2ap(ai, pointer);
    1857        return b;
    1858    }
    1859
    1860   // Helper methods
    1861
    1862    private ArticleInfo __ap2ai(@SuppressWarnings("deprecation") ArticlePointer ap) {
    1863        if (ap == null) {
    1864            return null;
    1865        }
    1866        ArticleInfo ai = new ArticleInfo();
    1867        return ai;
    1868    }
    1869
    1870    @SuppressWarnings("deprecation")
    1871    private void __ai2ap(ArticleInfo ai, ArticlePointer ap){
    1872        if (ap != null) { // ai cannot be null
    1873            ap.articleId = ai.articleId;
    1874            ap.articleNumber = (int) ai.articleNumber;
    1875        }
    1876    }
    1877}
    1878
    1879
    1880/* Emacs configuration
    1881 * Local variables:        **
    1882 * mode:             java  **
    1883 * c-basic-offset:   4     **
    1884 * indent-tabs-mode: nil   **
    1885 * End:                    **
    1886 */