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