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