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