    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.StringTokenizer;
    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 Daniel F. Savarese
    083     * @author Rory Winston
    084     * @author Ted Wise
    085     * @see NNTP
    086     * @see NNTPConnectionClosedException
    087     * @see org.apache.commons.net.MalformedServerReplyException
    088     ***/
    089
    090    public class NNTPClient extends NNTP
    091    {
    092
    093        private void __parseArticlePointer(String reply, ArticlePointer pointer)
    094        throws MalformedServerReplyException
    095        {
    096            StringTokenizer tokenizer;
    097
    098            // Do loop is a kluge to simulate goto
    099            do
    100            {
    101                tokenizer = new StringTokenizer(reply);
    102
    103                if (tokenizer.countTokens() < 3)
    104                    break;
    105
    106                // Skip numeric response value
    107                tokenizer.nextToken();
    108                // Get article number
    109                try
    110                {
    111                    pointer.articleNumber = Integer.parseInt(tokenizer.nextToken());
    112                }
    113                catch (NumberFormatException e)
    114                {
    115                    break;
    116                }
    117
    118                // Get article id
    119                pointer.articleId = tokenizer.nextToken();
    120                return ;
    121            }
    122            while (false);
    123
    124            throw new MalformedServerReplyException(
    125                "Could not parse article pointer.\nServer reply: " + reply);
    126        }
    127
    128
    129        private void __parseGroupReply(String reply, NewsgroupInfo info)
    130        throws MalformedServerReplyException
    131        {
    132            String count, first, last;
    133            StringTokenizer tokenizer;
    134
    135            // Do loop is a kluge to simulate goto
    136            do
    137            {
    138                tokenizer = new StringTokenizer(reply);
    139
    140                if (tokenizer.countTokens() < 5)
    141                    break;
    142
    143                // Skip numeric response value
    144                tokenizer.nextToken();
    145                // Get estimated article count
    146                count = tokenizer.nextToken();
    147                // Get first article number
    148                first = tokenizer.nextToken();
    149                // Get last article number
    150                last = tokenizer.nextToken();
    151                // Get newsgroup name
    152                info._setNewsgroup(tokenizer.nextToken());
    153
    154                try
    155                {
    156                    info._setArticleCount(Integer.parseInt(count));
    157                    info._setFirstArticle(Integer.parseInt(first));
    158                    info._setLastArticle(Integer.parseInt(last));
    159                }
    160                catch (NumberFormatException e)
    161                {
    162                    break;
    163                }
    164
    165                info._setPostingPermission(NewsgroupInfo.UNKNOWN_POSTING_PERMISSION);
    166                return ;
    167            }
    168            while (false);
    169
    170            throw new MalformedServerReplyException(
    171                "Could not parse newsgroup info.\nServer reply: " + reply);
    172        }
    173
    174
    175        private NewsgroupInfo __parseNewsgroupListEntry(String entry)
    176        {
    177            NewsgroupInfo result;
    178            StringTokenizer tokenizer;
    179            int lastNum, firstNum;
    180            String last, first, permission;
    181
    182            result = new NewsgroupInfo();
    183            tokenizer = new StringTokenizer(entry);
    184
    185            if (tokenizer.countTokens() < 4)
    186                return null;
    187
    188            result._setNewsgroup(tokenizer.nextToken());
    189            last = tokenizer.nextToken();
    190            first = tokenizer.nextToken();
    191            permission = tokenizer.nextToken();
    192
    193            try
    194            {
    195                lastNum = Integer.parseInt(last);
    196                firstNum = Integer.parseInt(first);
    197                result._setFirstArticle(firstNum);
    198                result._setLastArticle(lastNum);
    199
    200            if((firstNum == 0) && (lastNum == 0))
    201                result._setArticleCount(0);
    202            else
    203                result._setArticleCount(lastNum - firstNum + 1);
    204            }
    205            catch (NumberFormatException e)
    206            {
    207                return null;
    208            }
    209
    210            switch (permission.charAt(0))
    211            {
    212            case 'y':
    213            case 'Y':
    214                result._setPostingPermission(
    215                    NewsgroupInfo.PERMITTED_POSTING_PERMISSION);
    216                break;
    217            case 'n':
    218            case 'N':
    219                result._setPostingPermission(
    220                    NewsgroupInfo.PROHIBITED_POSTING_PERMISSION);
    221                break;
    222            case 'm':
    223            case 'M':
    224                result._setPostingPermission(
    225                    NewsgroupInfo.MODERATED_POSTING_PERMISSION);
    226                break;
    227            default:
    228                result._setPostingPermission(
    229                    NewsgroupInfo.UNKNOWN_POSTING_PERMISSION);
    230                break;
    231            }
    232
    233            return result;
    234        }
    235
    236        private NewsgroupInfo[] __readNewsgroupListing() throws IOException
    237        {
    238            int size;
    239            String line;
    240            Vector<NewsgroupInfo> list;
    241            BufferedReader reader;
    242            NewsgroupInfo tmp, info[];
    243
    244            reader = new BufferedReader(new DotTerminatedMessageReader(_reader_));
    245            // Start of with a big vector because we may be reading a very large
    246            // amount of groups.
    247            list = new Vector<NewsgroupInfo>(2048);
    248
    249            while ((line = reader.readLine()) != null)
    250            {
    251                tmp = __parseNewsgroupListEntry(line);
    252                if (tmp != null)
    253                    list.addElement(tmp);
    254                else
    255                    throw new MalformedServerReplyException(line);
    256            }
    257
    258            if ((size = list.size()) < 1)
    259                return new NewsgroupInfo[0];
    260
    261            info = new NewsgroupInfo[size];
    262            list.copyInto(info);
    263
    264            return info;
    265        }
    266
    267
    268        private Reader __retrieve(int command,
    269                                  String articleId, ArticlePointer pointer)
    270        throws IOException
    271        {
    272            Reader reader;
    273
    274            if (articleId != null)
    275            {
    276                if (!NNTPReply.isPositiveCompletion(sendCommand(command, articleId)))
    277                    return null;
    278            }
    279            else
    280            {
    281                if (!NNTPReply.isPositiveCompletion(sendCommand(command)))
    282                    return null;
    283            }
    284
    285
    286            if (pointer != null)
    287                __parseArticlePointer(getReplyString(), pointer);
    288
    289            reader = new DotTerminatedMessageReader(_reader_);
    290            return reader;
    291        }
    292
    293
    294        private Reader __retrieve(int command,
    295                                  int articleNumber, ArticlePointer pointer)
    296        throws IOException
    297        {
    298            Reader reader;
    299
    300            if (!NNTPReply.isPositiveCompletion(sendCommand(command,
    301                                                Integer.toString(articleNumber))))
    302                return null;
    303
    304            if (pointer != null)
    305                __parseArticlePointer(getReplyString(), pointer);
    306
    307            reader = new DotTerminatedMessageReader(_reader_);
    308            return reader;
    309        }
    310
    311
    312
    313        /***
    314         * Retrieves an article from the NNTP server.  The article is referenced
    315         * by its unique article identifier (including the enclosing &lt and &gt).
    316         * The article number and identifier contained in the server reply
    317         * are returned through an ArticlePointer.  The <code> articleId </code>
    318         * field of the ArticlePointer cannot always be trusted because some
    319         * NNTP servers do not correctly follow the RFC 977 reply format.
    320         * <p>
    321         * A DotTerminatedMessageReader is returned from which the article can
    322         * be read.  If the article does not exist, null is returned.
    323         * <p>
    324         * You must not issue any commands to the NNTP server (i.e., call any
    325         * other methods) until you finish reading the message from the returned
    326         * Reader instance.
    327         * The NNTP protocol uses the same stream for issuing commands as it does
    328         * for returning results.  Therefore the returned Reader actually reads
    329         * directly from the NNTP connection.  After the end of message has been
    330         * reached, new commands can be executed and their replies read.  If
    331         * you do not follow these requirements, your program will not work
    332         * properly.
    333         * <p>
    334         * @param articleId  The unique article identifier of the article to
    335         *     retrieve.  If this parameter is null, the currently selected
    336         *     article is retrieved.
    337         * @param pointer    A parameter through which to return the article's
    338         *   number and unique id.  The articleId field cannot always be trusted
    339         *   because of server deviations from RFC 977 reply formats.  You may
    340         *   set this parameter to null if you do not desire to retrieve the
    341         *   returned article information.
    342         * @return A DotTerminatedMessageReader instance from which the article
    343         *         be read.  null if the article does not exist.
    344         * @exception NNTPConnectionClosedException
    345         *      If the NNTP server prematurely closes the connection as a result
    346         *      of the client being idle or some other reason causing the server
    347         *      to send NNTP reply code 400.  This exception may be caught either
    348         *      as an IOException or independently as itself.
    349         * @exception IOException  If an I/O error occurs while either sending a
    350         *      command to the server or receiving a reply from the server.
    351         ***/
    352        public Reader retrieveArticle(String articleId, ArticlePointer pointer)
    353        throws IOException
    354        {
    355            return __retrieve(NNTPCommand.ARTICLE, articleId, pointer);
    356
    357        }
    358
    359        /*** Same as <code> retrieveArticle(articleId, null) </code> ***/
    360        public Reader retrieveArticle(String articleId) throws IOException
    361        {
    362            return retrieveArticle(articleId, null);
    363        }
    364
    365        /*** Same as <code> retrieveArticle(null) </code> ***/
    366        public Reader retrieveArticle() throws IOException
    367        {
    368            return retrieveArticle(null);
    369        }
    370
    371
    372        /***
    373         * Retrieves an article from the currently selected newsgroup.  The
    374         * article is referenced by its article number.
    375         * The article number and identifier contained in the server reply
    376         * are returned through an ArticlePointer.  The <code> articleId </code>
    377         * field of the ArticlePointer cannot always be trusted because some
    378         * NNTP servers do not correctly follow the RFC 977 reply format.
    379         * <p>
    380         * A DotTerminatedMessageReader is returned from which the article can
    381         * be read.  If the article does not exist, null is returned.
    382         * <p>
    383         * You must not issue any commands to the NNTP server (i.e., call any
    384         * other methods) until you finish reading the message from the returned
    385         * Reader instance.
    386         * The NNTP protocol uses the same stream for issuing commands as it does
    387         * for returning results.  Therefore the returned Reader actually reads
    388         * directly from the NNTP connection.  After the end of message has been
    389         * reached, new commands can be executed and their replies read.  If
    390         * you do not follow these requirements, your program will not work
    391         * properly.
    392         * <p>
    393         * @param articleNumber  The number of the the article to
    394         *     retrieve.
    395         * @param pointer    A parameter through which to return the article's
    396         *   number and unique id.  The articleId field cannot always be trusted
    397         *   because of server deviations from RFC 977 reply formats.  You may
    398         *   set this parameter to null if you do not desire to retrieve the
    399         *   returned article information.
    400         * @return A DotTerminatedMessageReader instance from which the article
    401         *         be read.  null if the article does not exist.
    402         * @exception NNTPConnectionClosedException
    403         *      If the NNTP server prematurely closes the connection as a result
    404         *      of the client being idle or some other reason causing the server
    405         *      to send NNTP reply code 400.  This exception may be caught either
    406         *      as an IOException or independently as itself.
    407         * @exception IOException  If an I/O error occurs while either sending a
    408         *      command to the server or receiving a reply from the server.
    409         ***/
    410        public Reader retrieveArticle(int articleNumber, ArticlePointer pointer)
    411        throws IOException
    412        {
    413            return __retrieve(NNTPCommand.ARTICLE, articleNumber, pointer);
    414        }
    415
    416        /*** Same as <code> retrieveArticle(articleNumber, null) </code> ***/
    417        public Reader retrieveArticle(int articleNumber) throws IOException
    418        {
    419            return retrieveArticle(articleNumber, null);
    420        }
    421
    422
    423
    424        /***
    425         * Retrieves an article header from the NNTP server.  The article is
    426         * referenced
    427         * by its unique article identifier (including the enclosing &lt and &gt).
    428         * The article number and identifier contained in the server reply
    429         * are returned through an ArticlePointer.  The <code> articleId </code>
    430         * field of the ArticlePointer cannot always be trusted because some
    431         * NNTP servers do not correctly follow the RFC 977 reply format.
    432         * <p>
    433         * A DotTerminatedMessageReader is returned from which the article can
    434         * be read.  If the article does not exist, null is returned.
    435         * <p>
    436         * You must not issue any commands to the NNTP server (i.e., call any
    437         * other methods) until you finish reading the message from the returned
    438         * Reader instance.
    439         * The NNTP protocol uses the same stream for issuing commands as it does
    440         * for returning results.  Therefore the returned Reader actually reads
    441         * directly from the NNTP connection.  After the end of message has been
    442         * reached, new commands can be executed and their replies read.  If
    443         * you do not follow these requirements, your program will not work
    444         * properly.
    445         * <p>
    446         * @param articleId  The unique article identifier of the article whose
    447         *    header is being retrieved.  If this parameter is null, the
    448         *    header of the currently selected article is retrieved.
    449         * @param pointer    A parameter through which to return the article's
    450         *   number and unique id.  The articleId field cannot always be trusted
    451         *   because of server deviations from RFC 977 reply formats.  You may
    452         *   set this parameter to null if you do not desire to retrieve the
    453         *   returned article information.
    454         * @return A DotTerminatedMessageReader instance from which the article
    455         *         header can be read.  null if the article does not exist.
    456         * @exception NNTPConnectionClosedException
    457         *      If the NNTP server prematurely closes the connection as a result
    458         *      of the client being idle or some other reason causing the server
    459         *      to send NNTP reply code 400.  This exception may be caught either
    460         *      as an IOException or independently as itself.
    461         * @exception IOException  If an I/O error occurs while either sending a
    462         *      command to the server or receiving a reply from the server.
    463         ***/
    464        public Reader retrieveArticleHeader(String articleId, ArticlePointer pointer)
    465        throws IOException
    466        {
    467            return __retrieve(NNTPCommand.HEAD, articleId, pointer);
    468
    469        }
    470
    471        /*** Same as <code> retrieveArticleHeader(articleId, null) </code> ***/
    472        public Reader retrieveArticleHeader(String articleId) throws IOException
    473        {
    474            return retrieveArticleHeader(articleId, null);
    475        }
    476
    477        /*** Same as <code> retrieveArticleHeader(null) </code> ***/
    478        public Reader retrieveArticleHeader() throws IOException
    479        {
    480            return retrieveArticleHeader(null);
    481        }
    482
    483
    484        /***
    485         * Retrieves an article header from the currently selected newsgroup.  The
    486         * article is referenced by its article number.
    487         * The article number and identifier contained in the server reply
    488         * are returned through an ArticlePointer.  The <code> articleId </code>
    489         * field of the ArticlePointer cannot always be trusted because some
    490         * NNTP servers do not correctly follow the RFC 977 reply format.
    491         * <p>
    492         * A DotTerminatedMessageReader is returned from which the article can
    493         * be read.  If the article does not exist, null is returned.
    494         * <p>
    495         * You must not issue any commands to the NNTP server (i.e., call any
    496         * other methods) until you finish reading the message from the returned
    497         * Reader instance.
    498         * The NNTP protocol uses the same stream for issuing commands as it does
    499         * for returning results.  Therefore the returned Reader actually reads
    500         * directly from the NNTP connection.  After the end of message has been
    501         * reached, new commands can be executed and their replies read.  If
    502         * you do not follow these requirements, your program will not work
    503         * properly.
    504         * <p>
    505         * @param articleNumber  The number of the the article whose header is
    506         *     being retrieved.
    507         * @param pointer    A parameter through which to return the article's
    508         *   number and unique id.  The articleId field cannot always be trusted
    509         *   because of server deviations from RFC 977 reply formats.  You may
    510         *   set this parameter to null if you do not desire to retrieve the
    511         *   returned article information.
    512         * @return A DotTerminatedMessageReader instance from which the article
    513         *         header can be read.  null if the article does not exist.
    514         * @exception NNTPConnectionClosedException
    515         *      If the NNTP server prematurely closes the connection as a result
    516         *      of the client being idle or some other reason causing the server
    517         *      to send NNTP reply code 400.  This exception may be caught either
    518         *      as an IOException or independently as itself.
    519         * @exception IOException  If an I/O error occurs while either sending a
    520         *      command to the server or receiving a reply from the server.
    521         ***/
    522        public Reader retrieveArticleHeader(int articleNumber,
    523                                            ArticlePointer pointer)
    524        throws IOException
    525        {
    526            return __retrieve(NNTPCommand.HEAD, articleNumber, pointer);
    527        }
    528
    529
    530        /*** Same as <code> retrieveArticleHeader(articleNumber, null) </code> ***/
    531        public Reader retrieveArticleHeader(int articleNumber) throws IOException
    532        {
    533            return retrieveArticleHeader(articleNumber, null);
    534        }
    535
    536
    537
    538        /***
    539         * Retrieves an article body from the NNTP server.  The article is
    540         * referenced
    541         * by its unique article identifier (including the enclosing &lt and &gt).
    542         * The article number and identifier contained in the server reply
    543         * are returned through an ArticlePointer.  The <code> articleId </code>
    544         * field of the ArticlePointer cannot always be trusted because some
    545         * NNTP servers do not correctly follow the RFC 977 reply format.
    546         * <p>
    547         * A DotTerminatedMessageReader is returned from which the article can
    548         * be read.  If the article does not exist, null is returned.
    549         * <p>
    550         * You must not issue any commands to the NNTP server (i.e., call any
    551         * other methods) until you finish reading the message from the returned
    552         * Reader instance.
    553         * The NNTP protocol uses the same stream for issuing commands as it does
    554         * for returning results.  Therefore the returned Reader actually reads
    555         * directly from the NNTP connection.  After the end of message has been
    556         * reached, new commands can be executed and their replies read.  If
    557         * you do not follow these requirements, your program will not work
    558         * properly.
    559         * <p>
    560         * @param articleId  The unique article identifier of the article whose
    561         *    body is being retrieved.  If this parameter is null, the
    562         *    body of the currently selected article is retrieved.
    563         * @param pointer    A parameter through which to return the article's
    564         *   number and unique id.  The articleId field cannot always be trusted
    565         *   because of server deviations from RFC 977 reply formats.  You may
    566         *   set this parameter to null if you do not desire to retrieve the
    567         *   returned article information.
    568         * @return A DotTerminatedMessageReader instance from which the article
    569         *         body can be read.  null if the article does not exist.
    570         * @exception NNTPConnectionClosedException
    571         *      If the NNTP server prematurely closes the connection as a result
    572         *      of the client being idle or some other reason causing the server
    573         *      to send NNTP reply code 400.  This exception may be caught either
    574         *      as an IOException or independently as itself.
    575         * @exception IOException  If an I/O error occurs while either sending a
    576         *      command to the server or receiving a reply from the server.
    577         ***/
    578        public Reader retrieveArticleBody(String articleId, ArticlePointer pointer)
    579        throws IOException
    580        {
    581            return __retrieve(NNTPCommand.BODY, articleId, pointer);
    582
    583        }
    584
    585        /*** Same as <code> retrieveArticleBody(articleId, null) </code> ***/
    586        public Reader retrieveArticleBody(String articleId) throws IOException
    587        {
    588            return retrieveArticleBody(articleId, null);
    589        }
    590
    591        /*** Same as <code> retrieveArticleBody(null) </code> ***/
    592        public Reader retrieveArticleBody() throws IOException
    593        {
    594            return retrieveArticleBody(null);
    595        }
    596
    597
    598        /***
    599         * Retrieves an article body from the currently selected newsgroup.  The
    600         * article is referenced by its article number.
    601         * The article number and identifier contained in the server reply
    602         * are returned through an ArticlePointer.  The <code> articleId </code>
    603         * field of the ArticlePointer cannot always be trusted because some
    604         * NNTP servers do not correctly follow the RFC 977 reply format.
    605         * <p>
    606         * A DotTerminatedMessageReader is returned from which the article can
    607         * be read.  If the article does not exist, null is returned.
    608         * <p>
    609         * You must not issue any commands to the NNTP server (i.e., call any
    610         * other methods) until you finish reading the message from the returned
    611         * Reader instance.
    612         * The NNTP protocol uses the same stream for issuing commands as it does
    613         * for returning results.  Therefore the returned Reader actually reads
    614         * directly from the NNTP connection.  After the end of message has been
    615         * reached, new commands can be executed and their replies read.  If
    616         * you do not follow these requirements, your program will not work
    617         * properly.
    618         * <p>
    619         * @param articleNumber  The number of the the article whose body is
    620         *     being retrieved.
    621         * @param pointer    A parameter through which to return the article's
    622         *   number and unique id.  The articleId field cannot always be trusted
    623         *   because of server deviations from RFC 977 reply formats.  You may
    624         *   set this parameter to null if you do not desire to retrieve the
    625         *   returned article information.
    626         * @return A DotTerminatedMessageReader instance from which the article
    627         *         body can be read.  null if the article does not exist.
    628         * @exception NNTPConnectionClosedException
    629         *      If the NNTP server prematurely closes the connection as a result
    630         *      of the client being idle or some other reason causing the server
    631         *      to send NNTP reply code 400.  This exception may be caught either
    632         *      as an IOException or independently as itself.
    633         * @exception IOException  If an I/O error occurs while either sending a
    634         *      command to the server or receiving a reply from the server.
    635         ***/
    636        public Reader retrieveArticleBody(int articleNumber,
    637                                          ArticlePointer pointer)
    638        throws IOException
    639        {
    640            return __retrieve(NNTPCommand.BODY, articleNumber, pointer);
    641        }
    642
    643
    644        /*** Same as <code> retrieveArticleBody(articleNumber, null) </code> ***/
    645        public Reader retrieveArticleBody(int articleNumber) throws IOException
    646        {
    647            return retrieveArticleBody(articleNumber, null);
    648        }
    649
    650
    651        /***
    652         * Select the specified newsgroup to be the target of for future article
    653         * retrieval and posting operations.  Also return the newsgroup
    654         * information contained in the server reply through the info parameter.
    655         * <p>
    656         * @param newsgroup  The newsgroup to select.
    657         * @param info  A parameter through which the newsgroup information of
    658         *      the selected newsgroup contained in the server reply is returned.
    659         *      Set this to null if you do not desire this information.
    660         * @return True if the newsgroup exists and was selected, false otherwise.
    661         * @exception NNTPConnectionClosedException
    662         *      If the NNTP server prematurely closes the connection as a result
    663         *      of the client being idle or some other reason causing the server
    664         *      to send NNTP reply code 400.  This exception may be caught either
    665         *      as an IOException or independently as itself.
    666         * @exception IOException  If an I/O error occurs while either sending a
    667         *      command to the server or receiving a reply from the server.
    668         ***/
    669        public boolean selectNewsgroup(String newsgroup, NewsgroupInfo info)
    670        throws IOException
    671        {
    672            if (!NNTPReply.isPositiveCompletion(group(newsgroup)))
    673                return false;
    674
    675            if (info != null)
    676                __parseGroupReply(getReplyString(), info);
    677
    678            return true;
    679        }
    680
    681        /*** Same as <code> selectNewsgroup(newsgroup, null) </code> ***/
    682        public boolean selectNewsgroup(String newsgroup) throws IOException
    683        {
    684            return selectNewsgroup(newsgroup, null);
    685        }
    686
    687        /***
    688         * List the command help from the server.
    689         * <p>
    690         * @return The sever help information.
    691         * @exception NNTPConnectionClosedException
    692         *      If the NNTP server prematurely closes the connection as a result
    693         *      of the client being idle or some other reason causing the server
    694         *      to send NNTP reply code 400.  This exception may be caught either
    695         *      as an IOException or independently as itself.
    696         * @exception IOException  If an I/O error occurs while either sending a
    697         *      command to the server or receiving a reply from the server.
    698         ***/
    699        public String listHelp() throws IOException
    700        {
    701            StringWriter help;
    702            Reader reader;
    703
    704            if (!NNTPReply.isInformational(help()))
    705                return null;
    706
    707            help = new StringWriter();
    708            reader = new DotTerminatedMessageReader(_reader_);
    709            Util.copyReader(reader, help);
    710            reader.close();
    711            help.close();
    712            return help.toString();
    713        }
    714
    715
    716        /***
    717         * Select an article by its unique identifier (including enclosing
    718         * &lt and &gt) and return its article number and id through the
    719         * pointer parameter.  This is achieved through the STAT command.
    720         * According to RFC 977, this will NOT set the current article pointer
    721         * on the server.  To do that, you must reference the article by its
    722         * number.
    723         * <p>
    724         * @param articleId  The unique article identifier of the article that
    725         *    is being selectedd.  If this parameter is null, the
    726         *    body of the current article is selected
    727         * @param pointer    A parameter through which to return the article's
    728         *   number and unique id.  The articleId field cannot always be trusted
    729         *   because of server deviations from RFC 977 reply formats.  You may
    730         *   set this parameter to null if you do not desire to retrieve the
    731         *   returned article information.
    732         * @return True if successful, false if not.
    733         * @exception NNTPConnectionClosedException
    734         *      If the NNTP server prematurely closes the connection as a result
    735         *      of the client being idle or some other reason causing the server
    736         *      to send NNTP reply code 400.  This exception may be caught either
    737         *      as an IOException or independently as itself.
    738         * @exception IOException  If an I/O error occurs while either sending a
    739         *      command to the server or receiving a reply from the server.
    740         ***/
    741        public boolean selectArticle(String articleId, ArticlePointer pointer)
    742        throws IOException
    743        {
    744            if (articleId != null)
    745            {
    746                if (!NNTPReply.isPositiveCompletion(stat(articleId)))
    747                    return false;
    748            }
    749            else
    750            {
    751                if (!NNTPReply.isPositiveCompletion(stat()))
    752                    return false;
    753            }
    754
    755            if (pointer != null)
    756                __parseArticlePointer(getReplyString(), pointer);
    757
    758            return true;
    759        }
    760
    761        /**** Same as <code> selectArticle(articleId, null) </code> ***/
    762        public boolean selectArticle(String articleId) throws IOException
    763        {
    764            return selectArticle(articleId, null);
    765        }
    766
    767        /****
    768         * Same as <code> selectArticle(null, articleId) </code>.  Useful
    769         * for retrieving the current article number.
    770         ***/
    771        public boolean selectArticle(ArticlePointer pointer) throws IOException
    772        {
    773            return selectArticle(null, pointer);
    774        }
    775
    776
    777        /***
    778         * Select an article in the currently selected newsgroup by its number.
    779         * and return its article number and id through the
    780         * pointer parameter.  This is achieved through the STAT command.
    781         * According to RFC 977, this WILL set the current article pointer
    782         * on the server.  Use this command to select an article before retrieving
    783         * it, or to obtain an article's unique identifier given its number.
    784         * <p>
    785         * @param articleNumber The number of the article to select from the
    786         *       currently selected newsgroup.
    787         * @param pointer    A parameter through which to return the article's
    788         *   number and unique id.  Although the articleId field cannot always
    789         *   be trusted because of server deviations from RFC 977 reply formats,
    790         *   we haven't found a server that misformats this information in response
    791         *   to this particular command.  You may set this parameter to null if
    792         *   you do not desire to retrieve the returned article information.
    793         * @return True if successful, false if not.
    794         * @exception NNTPConnectionClosedException
    795         *      If the NNTP server prematurely closes the connection as a result
    796         *      of the client being idle or some other reason causing the server
    797         *      to send NNTP reply code 400.  This exception may be caught either
    798         *      as an IOException or independently as itself.
    799         * @exception IOException  If an I/O error occurs while either sending a
    800         *      command to the server or receiving a reply from the server.
    801         ***/
    802        public boolean selectArticle(int articleNumber, ArticlePointer pointer)
    803        throws IOException
    804        {
    805            if (!NNTPReply.isPositiveCompletion(stat(articleNumber)))
    806                return false;
    807
    808            if (pointer != null)
    809                __parseArticlePointer(getReplyString(), pointer);
    810
    811            return true;
    812        }
    813
    814
    815        /*** Same as <code> selectArticle(articleNumber, null) </code> ***/
    816        public boolean selectArticle(int articleNumber) throws IOException
    817        {
    818            return selectArticle(articleNumber, null);
    819        }
    820
    821
    822        /***
    823         * Select the article preceeding the currently selected article in the
    824         * currently selected newsgroup and return its number and unique id
    825         * through the pointer parameter.  Because of deviating server
    826         * implementations, the articleId information cannot be trusted.  To
    827         * obtain the article identifier, issue a
    828         * <code> selectArticle(pointer.articleNumber, pointer) </code> immediately
    829         * afterward.
    830         * <p>
    831         * @param pointer    A parameter through which to return the article's
    832         *   number and unique id.  The articleId field cannot always be trusted
    833         *   because of server deviations from RFC 977 reply formats.  You may
    834         *   set this parameter to null if you do not desire to retrieve the
    835         *   returned article information.
    836         * @return True if successful, false if not (e.g., there is no previous
    837         *     article).
    838         * @exception NNTPConnectionClosedException
    839         *      If the NNTP server prematurely closes the connection as a result
    840         *      of the client being idle or some other reason causing the server
    841         *      to send NNTP reply code 400.  This exception may be caught either
    842         *      as an IOException or independently as itself.
    843         * @exception IOException  If an I/O error occurs while either sending a
    844         *      command to the server or receiving a reply from the server.
    845         ***/
    846        public boolean selectPreviousArticle(ArticlePointer pointer)
    847        throws IOException
    848        {
    849            if (!NNTPReply.isPositiveCompletion(last()))
    850                return false;
    851
    852            if (pointer != null)
    853                __parseArticlePointer(getReplyString(), pointer);
    854
    855            return true;
    856        }
    857
    858        /*** Same as <code> selectPreviousArticle(null) </code> ***/
    859        public boolean selectPreviousArticle() throws IOException
    860        {
    861            return selectPreviousArticle(null);
    862        }
    863
    864
    865        /***
    866         * Select the article following the currently selected article in the
    867         * currently selected newsgroup and return its number and unique id
    868         * through the pointer parameter.  Because of deviating server
    869         * implementations, the articleId information cannot be trusted.  To
    870         * obtain the article identifier, issue a
    871         * <code> selectArticle(pointer.articleNumber, pointer) </code> immediately
    872         * afterward.
    873         * <p>
    874         * @param pointer    A parameter through which to return the article's
    875         *   number and unique id.  The articleId field cannot always be trusted
    876         *   because of server deviations from RFC 977 reply formats.  You may
    877         *   set this parameter to null if you do not desire to retrieve the
    878         *   returned article information.
    879         * @return True if successful, false if not (e.g., there is no following
    880         *         article).
    881         * @exception NNTPConnectionClosedException
    882         *      If the NNTP server prematurely closes the connection as a result
    883         *      of the client being idle or some other reason causing the server
    884         *      to send NNTP reply code 400.  This exception may be caught either
    885         *      as an IOException or independently as itself.
    886         * @exception IOException  If an I/O error occurs while either sending a
    887         *      command to the server or receiving a reply from the server.
    888         ***/
    889        public boolean selectNextArticle(ArticlePointer pointer) throws IOException
    890        {
    891            if (!NNTPReply.isPositiveCompletion(next()))
    892                return false;
    893
    894            if (pointer != null)
    895                __parseArticlePointer(getReplyString(), pointer);
    896
    897            return true;
    898        }
    899
    900
    901        /*** Same as <code> selectNextArticle(null) </code> ***/
    902        public boolean selectNextArticle() throws IOException
    903        {
    904            return selectNextArticle(null);
    905        }
    906
    907
    908        /***
    909         * List all newsgroups served by the NNTP server.  If no newsgroups
    910         * are served, a zero length array will be returned.  If the command
    911         * fails, null will be returned.
    912         * <p>
    913         * @return An array of NewsgroupInfo instances containing the information
    914         *    for each newsgroup served by the NNTP server.   If no newsgroups
    915         *    are served, a zero length array will be returned.  If the command
    916         *    fails, null will be returned.
    917         * @exception NNTPConnectionClosedException
    918         *      If the NNTP server prematurely closes the connection as a result
    919         *      of the client being idle or some other reason causing the server
    920         *      to send NNTP reply code 400.  This exception may be caught either
    921         *      as an IOException or independently as itself.
    922         * @exception IOException  If an I/O error occurs while either sending a
    923         *      command to the server or receiving a reply from the server.
    924         ***/
    925        public NewsgroupInfo[] listNewsgroups() throws IOException
    926        {
    927            if (!NNTPReply.isPositiveCompletion(list()))
    928                return null;
    929
    930            return __readNewsgroupListing();
    931        }
    932
    933        /**
    934         * An overloaded listNewsgroups() command that allows us to
    935         * specify with a pattern what groups we want to list. Wraps the
    936         * LIST ACTIVE command.
    937         * <p>
    938         * @param wildmat a pseudo-regex pattern (cf. RFC 2980)
    939         * @return An array of NewsgroupInfo instances containing the information
    940         *    for each newsgroup served by the NNTP server corresponding to the
    941         *    supplied pattern.   If no such newsgroups are served, a zero length
    942         *    array will be returned.  If the command fails, null will be returned.
    943         * @throws IOException
    944         */
    945        public NewsgroupInfo[] listNewsgroups(String wildmat) throws IOException
    946        {
    947            if(!NNTPReply.isPositiveCompletion(listActive(wildmat)))
    948                return null;
    949            return __readNewsgroupListing();
    950        }
    951
    952
    953        /***
    954         * List all new newsgroups added to the NNTP server since a particular
    955         * date subject to the conditions of the specified query.  If no new
    956         * newsgroups were added, a zero length array will be returned.  If the
    957         * command fails, null will be returned.
    958         * <p>
    959         * @param query  The query restricting how to search for new newsgroups.
    960         * @return An array of NewsgroupInfo instances containing the information
    961         *    for each new newsgroup added to the NNTP server.   If no newsgroups
    962         *    were added, a zero length array will be returned.  If the command
    963         *    fails, null will be returned.
    964         * @exception NNTPConnectionClosedException
    965         *      If the NNTP server prematurely closes the connection as a result
    966         *      of the client being idle or some other reason causing the server
    967         *      to send NNTP reply code 400.  This exception may be caught either
    968         *      as an IOException or independently as itself.
    969         * @exception IOException  If an I/O error occurs while either sending a
    970         *      command to the server or receiving a reply from the server.
    971         ***/
    972        public NewsgroupInfo[] listNewNewsgroups(NewGroupsOrNewsQuery query)
    973        throws IOException
    974        {
    975            if (!NNTPReply.isPositiveCompletion(newgroups(
    976                                                    query.getDate(), query.getTime(),
    977                                                    query.isGMT(), query.getDistributions())))
    978                return null;
    979
    980            return __readNewsgroupListing();
    981        }
    982
    983
    984        /***
    985         * List all new articles added to the NNTP server since a particular
    986         * date subject to the conditions of the specified query.  If no new
    987         * new news is found, a zero length array will be returned.  If the
    988         * command fails, null will be returned.  You must add at least one
    989         * newsgroup to the query, else the command will fail.  Each String
    990         * in the returned array is a unique message identifier including the
    991         * enclosing &lt and &gt.
    992         * <p>
    993         * @param query  The query restricting how to search for new news.  You
    994         *    must add at least one newsgroup to the query.
    995         * @return An array of String instances containing the unique message
    996         *    identifiers for each new article added to the NNTP server.  If no
    997         *    new news is found, a zero length array will be returned.  If the
    998         *    command fails, null 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         ***/
    1007        public String[] listNewNews(NewGroupsOrNewsQuery query)
    1008        throws IOException
    1009        {
    1010            int size;
    1011            String line;
    1012            Vector<String> list;
    1013            String[] result;
    1014            BufferedReader reader;
    1015
    1016            if (!NNTPReply.isPositiveCompletion(newnews(
    1017                                                    query.getNewsgroups(), query.getDate(), query.getTime(),
    1018                                                    query.isGMT(), query.getDistributions())))
    1019                return null;
    1020
    1021            list = new Vector<String>();
    1022            reader = new BufferedReader(new DotTerminatedMessageReader(_reader_));
    1023
    1024            while ((line = reader.readLine()) != null)
    1025                list.addElement(line);
    1026
    1027            size = list.size();
    1028
    1029            if (size < 1)
    1030                return new String[0];
    1031
    1032            result = new String[size];
    1033            list.copyInto(result);
    1034
    1035            return result;
    1036        }
    1037
    1038        /***
    1039         * There are a few NNTPClient methods that do not complete the
    1040         * entire sequence of NNTP commands to complete a transaction.  These
    1041         * commands require some action by the programmer after the reception
    1042         * of a positive preliminary command.  After the programmer's code
    1043         * completes its actions, it must call this method to receive
    1044         * the completion reply from the server and verify the success of the
    1045         * entire transaction.
    1046         * <p>
    1047         * For example
    1048         * <pre>
    1049         * writer = client.postArticle();
    1050         * if(writer == null) // failure
    1051         *   return false;
    1052         * header = new SimpleNNTPHeader("foobar@foo.com", "Just testing");
    1053         * header.addNewsgroup("alt.test");
    1054         * writer.write(header.toString());
    1055         * writer.write("This is just a test");
    1056         * writer.close();
    1057         * if(!client.completePendingCommand()) // failure
    1058         *   return false;
    1059         * </pre>
    1060         * <p>
    1061         * @return True if successfully completed, false if not.
    1062         * @exception NNTPConnectionClosedException
    1063         *      If the NNTP server prematurely closes the connection as a result
    1064         *      of the client being idle or some other reason causing the server
    1065         *      to send NNTP reply code 400.  This exception may be caught either
    1066         *      as an IOException or independently as itself.
    1067         * @exception IOException  If an I/O error occurs while either sending a
    1068         *      command to the server or receiving a reply from the server.
    1069         ***/
    1070        public boolean completePendingCommand() throws IOException
    1071        {
    1072            return NNTPReply.isPositiveCompletion(getReply());
    1073        }
    1074
    1075        /***
    1076         * Post an article to the NNTP server.  This method returns a
    1077         * DotTerminatedMessageWriter instance to which the article can be
    1078         * written.  Null is returned if the posting attempt fails.  You
    1079         * should check {@link NNTP#isAllowedToPost isAllowedToPost() }
    1080         *  before trying to post.  However, a posting
    1081         * attempt can fail due to malformed headers.
    1082         * <p>
    1083         * You must not issue any commands to the NNTP server (i.e., call any
    1084         * (other methods) until you finish writing to the returned Writer
    1085         * instance and close it.  The NNTP protocol uses the same stream for
    1086         * issuing commands as it does for returning results.  Therefore the
    1087         * returned Writer actually writes directly to the NNTP connection.
    1088         * After you close the writer, you can execute new commands.  If you
    1089         * do not follow these requirements your program will not work properly.
    1090         * <p>
    1091         * Different NNTP servers will require different header formats, but
    1092         * you can use the provided
    1093         * {@link org.apache.commons.net.nntp.SimpleNNTPHeader}
    1094         * class to construct the bare minimum acceptable header for most
    1095         * news readers.  To construct more complicated headers you should
    1096         * refer to RFC 822.  When the Java Mail API is finalized, you will be
    1097         * able to use it to compose fully compliant Internet text messages.
    1098         * The DotTerminatedMessageWriter takes care of doubling line-leading
    1099         * dots and ending the message with a single dot upon closing, so all
    1100         * you have to worry about is writing the header and the message.
    1101         * <p>
    1102         * Upon closing the returned Writer, you need to call
    1103         * {@link #completePendingCommand  completePendingCommand() }
    1104         * to finalize the posting and verify its success or failure from
    1105         * the server reply.
    1106         * <p>
    1107         * @return A DotTerminatedMessageWriter to which the article (including
    1108         *      header) can be written.  Returns null if the command fails.
    1109         * @exception IOException  If an I/O error occurs while either sending a
    1110         *      command to the server or receiving a reply from the server.
    1111         ***/
    1112
    1113        public Writer postArticle() throws IOException
    1114        {
    1115            if (!NNTPReply.isPositiveIntermediate(post()))
    1116                return null;
    1117
    1118            return new DotTerminatedMessageWriter(_writer_);
    1119        }
    1120
    1121
    1122        public Writer forwardArticle(String articleId) throws IOException
    1123        {
    1124            if (!NNTPReply.isPositiveIntermediate(ihave(articleId)))
    1125                return null;
    1126
    1127            return new DotTerminatedMessageWriter(_writer_);
    1128        }
    1129
    1130
    1131        /***
    1132         * Logs out of the news server gracefully by sending the QUIT command.
    1133         * However, you must still disconnect from the server before you can open
    1134         * a new connection.
    1135         * <p>
    1136         * @return True if successfully completed, false if not.
    1137         * @exception IOException  If an I/O error occurs while either sending a
    1138         *      command to the server or receiving a reply from the server.
    1139         ***/
    1140        public boolean logout() throws IOException
    1141        {
    1142            return NNTPReply.isPositiveCompletion(quit());
    1143        }
    1144
    1145
    1146        /**
    1147         * Log into a news server by sending the AUTHINFO USER/AUTHINFO
    1148         * PASS command sequence. This is usually sent in response to a
    1149         * 480 reply code from the NNTP server.
    1150         * <p>
    1151         * @param username a valid username
    1152         * @param password the corresponding password
    1153         * @return True for successful login, false for a failure
    1154         * @throws IOException
    1155         */
    1156        public boolean authenticate(String username, String password)
    1157            throws IOException
    1158        {
    1159            int replyCode = authinfoUser(username);
    1160
    1161            if (replyCode == NNTPReply.MORE_AUTH_INFO_REQUIRED)
    1162                {
    1163                    replyCode = authinfoPass(password);
    1164
    1165                    if (replyCode == NNTPReply.AUTHENTICATION_ACCEPTED)
    1166                        {
    1167                            _isAllowedToPost = true;
    1168                            return true;
    1169                        }
    1170                }
    1171            return false;
    1172        }
    1173
    1174        /***
    1175         * Private implementation of XOVER functionality.
    1176         *
    1177         * See {@link NNTP#xover}
    1178         * for legal agument formats. Alternatively, read RFC 2980 :-)
    1179         * <p>
    1180         * @param articleRange
    1181         * @return Returns a DotTerminatedMessageReader if successful, null
    1182         *         otherwise
    1183         * @exception IOException
    1184         */
    1185        private Reader __retrieveArticleInfo(String articleRange)
    1186            throws IOException
    1187        {
    1188            if (!NNTPReply.isPositiveCompletion(xover(articleRange)))
    1189                return null;
    1190
    1191            return new DotTerminatedMessageReader(_reader_);
    1192        }
    1193
    1194        /**
    1195         * Return article headers for a specified post.
    1196         * <p>
    1197         * @param articleNumber the article to retrieve headers for
    1198         * @return a DotTerminatedReader if successful, null otherwise
    1199         * @throws IOException
    1200         */
    1201        public Reader retrieveArticleInfo(int articleNumber) throws IOException
    1202        {
    1203            return __retrieveArticleInfo(Integer.toString(articleNumber));
    1204        }
    1205
    1206        /**
    1207         * Return article headers for all articles between lowArticleNumber
    1208         * and highArticleNumber, inclusively.
    1209         * <p>
    1210         * @param lowArticleNumber
    1211         * @param highArticleNumber
    1212         * @return a DotTerminatedReader if successful, null otherwise
    1213         * @throws IOException
    1214         */
    1215        public Reader retrieveArticleInfo(int lowArticleNumber,
    1216                                          int highArticleNumber)
    1217            throws IOException
    1218        {
    1219            return
    1220                __retrieveArticleInfo(lowArticleNumber + "-" +
    1221                                                 highArticleNumber);
    1222        }
    1223
    1224        /***
    1225         * Private implementation of XHDR functionality.
    1226         *
    1227         * See {@link NNTP#xhdr}
    1228         * for legal agument formats. Alternatively, read RFC 1036.
    1229         * <p>
    1230         * @param header
    1231         * @param articleRange
    1232         * @return Returns a DotTerminatedMessageReader if successful, null
    1233         *         otherwise
    1234         * @exception IOException
    1235         */
    1236        private Reader __retrieveHeader(String header, String articleRange)
    1237            throws IOException
    1238        {
    1239            if (!NNTPReply.isPositiveCompletion(xhdr(header, articleRange)))
    1240                return null;
    1241
    1242            return new DotTerminatedMessageReader(_reader_);
    1243        }
    1244
    1245        /**
    1246         * Return an article header for a specified post.
    1247         * <p>
    1248         * @param header the header to retrieve
    1249         * @param articleNumber the article to retrieve the header for
    1250         * @return a DotTerminatedReader if successful, null otherwise
    1251         * @throws IOException
    1252         */
    1253        public Reader retrieveHeader(String header, int articleNumber)
    1254            throws IOException
    1255        {
    1256            return __retrieveHeader(header, Integer.toString(articleNumber));
    1257        }
    1258
    1259        /**
    1260         * Return an article header for all articles between lowArticleNumber
    1261         * and highArticleNumber, inclusively.
    1262         * <p>
    1263         * @param header
    1264         * @param lowArticleNumber
    1265         * @param highArticleNumber
    1266         * @return a DotTerminatedReader if successful, null otherwise
    1267         * @throws IOException
    1268         */
    1269        public Reader retrieveHeader(String header, int lowArticleNumber,
    1270                                     int highArticleNumber)
    1271            throws IOException
    1272        {
    1273            return
    1274                __retrieveHeader(header,lowArticleNumber + "-" + highArticleNumber);
    1275        }
    1276    }
    1277
    1278
    1279    /* Emacs configuration
    1280     * Local variables:        **
    1281     * mode:             java  **
    1282     * c-basic-offset:   4     **
    1283     * indent-tabs-mode: nil   **
    1284     * End:                    **
    1285     */