    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 *      https://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017
    018package org.apache.commons.net.nntp;
    019
    020import java.io.BufferedReader;
    021import java.io.BufferedWriter;
    022import java.io.IOException;
    023import java.io.InputStreamReader;
    024import java.io.OutputStreamWriter;
    025import java.nio.charset.Charset;
    026import java.nio.charset.StandardCharsets;
    027
    028import org.apache.commons.net.MalformedServerReplyException;
    029import org.apache.commons.net.ProtocolCommandSupport;
    030import org.apache.commons.net.SocketClient;
    031import org.apache.commons.net.io.CRLFLineReader;
    032
    033/**
    034 * The NNTP class is not meant to be used by itself and is provided only so that you may easily implement your own NNTP client if you so desire. If you have no
    035 * need to perform your own implementation, you should use {@link org.apache.commons.net.nntp.NNTPClient}. The NNTP class is made public to provide access to
    036 * various NNTP constants and to make it easier for adventurous programmers (or those with special needs) to interact with the NNTP protocol and implement their
    037 * own clients. A set of methods with names corresponding to the NNTP command names are provided to facilitate this interaction.
    038 * <p>
    039 * You should keep in mind that the NNTP server may choose to prematurely close a connection if the client has been idle for longer than a given time period or
    040 * if the server is being shutdown by the operator or some other reason. The NNTP class will detect a premature NNTP server connection closing when it receives
    041 * a {@link org.apache.commons.net.nntp.NNTPReply#SERVICE_DISCONTINUED NNTPReply.SERVICE_DISCONTINUED} response to a command. When that occurs, the NNTP class
    042 * method encountering that reply will throw an {@link org.apache.commons.net.nntp.NNTPConnectionClosedException} . {@code NNTPConectionClosedException} is
    043 * a subclass of {@code IOException} and therefore need not be caught separately, but if you are going to catch it separately, its catch block must
    044 * appear before the more general {@code IOException} catch block. When you encounter an
    045 * {@link org.apache.commons.net.nntp.NNTPConnectionClosedException} , you must disconnect the connection with {@link #disconnect disconnect()} to properly
    046 * clean up the system resources used by NNTP. Before disconnecting, you may check the last reply code and text with {@link #getReplyCode getReplyCode} and
    047 * {@link #getReplyString getReplyString}.
    048 * </p>
    049 * <p>
    050 * Rather than list it separately for each method, we mention here that every method communicating with the server and throwing an IOException can also throw a
    051 * {@link org.apache.commons.net.MalformedServerReplyException} , which is a subclass of IOException. A MalformedServerReplyException will be thrown when the
    052 * reply received from the server deviates enough from the protocol specification that it cannot be interpreted in a useful manner despite attempts to be as
    053 * lenient as possible.
    054 * </p>
    055 *
    056 * @see NNTPClient
    057 * @see NNTPConnectionClosedException
    058 * @see org.apache.commons.net.MalformedServerReplyException
    059 */
    060
    061public class NNTP extends SocketClient {
    062
    063    /** The default NNTP port. Its value is 119 according to RFC 977. */
    064    public static final int DEFAULT_PORT = 119;
    065
    066    // We have to ensure that the protocol communication is in ASCII,
    067    // but we use ISO-8859-1 just in case 8-bit characters cross
    068    // the wire.
    069    private static final Charset DEFAULT_ENCODING = StandardCharsets.ISO_8859_1;
    070
    071    boolean _isAllowedToPost;
    072    private int replyCode;
    073    private String replyString;
    074
    075    /**
    076     * Wraps {@link SocketClient#_input_} to communicate with server. Initialized by {@link #_connectAction_}. All server reads should be done through this
    077     * variable.
    078     */
    079    protected BufferedReader _reader_;
    080
    081    /**
    082     * Wraps {@link SocketClient#_output_} to communicate with server. Initialized by {@link #_connectAction_}. All server reads should be done through this
    083     * variable.
    084     */
    085    protected BufferedWriter _writer_;
    086
    087    /**
    088     * A ProtocolCommandSupport object used to manage the registering of ProtocolCommandListeners and te firing of ProtocolCommandEvents.
    089     */
    090    protected ProtocolCommandSupport _commandSupport_;
    091
    092    /**
    093     * The default NNTP constructor. Sets the default port to {@code DEFAULT_PORT} and initializes internal data structures for saving NNTP reply
    094     * information.
    095     */
    096    public NNTP() {
    097        setDefaultPort(DEFAULT_PORT);
    098        replyString = null;
    099        _reader_ = null;
    100        _writer_ = null;
    101        _isAllowedToPost = false;
    102        _commandSupport_ = new ProtocolCommandSupport(this);
    103    }
    104
    105    /**
    106     * Initiates control connections and gets initial reply, determining if the client is allowed to post to the server. Initializes {@link #_reader_} and
    107     * {@link #_writer_} to wrap {@link SocketClient#_input_} and {@link SocketClient#_output_}.
    108     */
    109    @Override
    110    protected void _connectAction_() throws IOException {
    111        super._connectAction_();
    112        _reader_ = new CRLFLineReader(new InputStreamReader(_input_, DEFAULT_ENCODING));
    113        _writer_ = new BufferedWriter(new OutputStreamWriter(_output_, DEFAULT_ENCODING));
    114        getReply();
    115
    116        _isAllowedToPost = replyCode == NNTPReply.SERVER_READY_POSTING_ALLOWED;
    117    }
    118
    119    /**
    120     * A convenience method to send the NNTP ARTICLE command to the server, receive the initial reply, and return the reply code.
    121     *
    122     * @return The reply code received from the server.
    123     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    124     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    125     *                                       independently as itself.
    126     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    127     */
    128    public int article() throws IOException {
    129        return sendCommand(NNTPCommand.ARTICLE);
    130    }
    131
    132    /**
    133     * @param a article number
    134     * @return number
    135     * @throws IOException on error
    136     * @deprecated - for API compatibility only - DO NOT USE
    137     */
    138    @Deprecated
    139    public int article(final int a) throws IOException {
    140        return article((long) a);
    141    }
    142
    143    /**
    144     * A convenience method to send the NNTP ARTICLE command to the server, receive the initial reply, and return the reply code.
    145     *
    146     * @param articleNumber The number of the article to request from the currently selected newsgroup.
    147     * @return The reply code received from the server.
    148     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    149     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    150     *                                       independently as itself.
    151     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    152     */
    153    public int article(final long articleNumber) throws IOException {
    154        return sendCommand(NNTPCommand.ARTICLE, Long.toString(articleNumber));
    155    }
    156
    157    /**
    158     * A convenience method to send the NNTP ARTICLE command to the server, receive the initial reply, and return the reply code.
    159     *
    160     * @param messageId The message identifier of the requested article, including the encapsulating &lt; and &gt; characters.
    161     * @return The reply code received from the server.
    162     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    163     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    164     *                                       independently as itself.
    165     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    166     */
    167    public int article(final String messageId) throws IOException {
    168        return sendCommand(NNTPCommand.ARTICLE, messageId);
    169    }
    170
    171    /**
    172     * A convenience method to send the AUTHINFO PASS command to the server, receive the reply, and return the reply code. If this step is required, it should
    173     * immediately follow the AUTHINFO USER command (See RFC 2980)
    174     *
    175     * @param password a valid password.
    176     * @return The reply code received from the server. The server should return a 281 or 502 for this command.
    177     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    178     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    179     *                                       independently as itself.
    180     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    181     */
    182    public int authinfoPass(final String password) throws IOException {
    183        final String passParameter = "PASS " + password;
    184        return sendCommand(NNTPCommand.AUTHINFO, passParameter);
    185    }
    186
    187    /**
    188     * A convenience method to send the AUTHINFO USER command to the server, receive the reply, and return the reply code. (See RFC 2980)
    189     *
    190     * @param user A valid user name.
    191     * @return The reply code received from the server. The server should return a 381 or 281 for this command.
    192     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    193     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    194     *                                       independently as itself.
    195     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    196     */
    197    public int authinfoUser(final String user) throws IOException {
    198        final String userParameter = "USER " + user;
    199        return sendCommand(NNTPCommand.AUTHINFO, userParameter);
    200    }
    201
    202    /**
    203     * A convenience method to send the NNTP BODY command to the server, receive the initial reply, and return the reply code.
    204     *
    205     * @return The reply code received from the server.
    206     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    207     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    208     *                                       independently as itself.
    209     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    210     */
    211    public int body() throws IOException {
    212        return sendCommand(NNTPCommand.BODY);
    213    }
    214
    215    /**
    216     * @param a article number
    217     * @return number
    218     * @throws IOException on error
    219     * @deprecated - for API compatibility only - DO NOT USE
    220     */
    221    @Deprecated
    222    public int body(final int a) throws IOException {
    223        return body((long) a);
    224    }
    225
    226    /**
    227     * A convenience method to send the NNTP BODY command to the server, receive the initial reply, and return the reply code.
    228     *
    229     * @param articleNumber The number of the article to request from the currently selected newsgroup.
    230     * @return The reply code received from the server.
    231     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    232     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    233     *                                       independently as itself.
    234     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    235     */
    236    public int body(final long articleNumber) throws IOException {
    237        return sendCommand(NNTPCommand.BODY, Long.toString(articleNumber));
    238    }
    239
    240    /**
    241     * A convenience method to send the NNTP BODY command to the server, receive the initial reply, and return the reply code.
    242     *
    243     * @param messageId The message identifier of the requested article, including the encapsulating &lt; and &gt; characters.
    244     * @return The reply code received from the server.
    245     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    246     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    247     *                                       independently as itself.
    248     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    249     */
    250    public int body(final String messageId) throws IOException {
    251        return sendCommand(NNTPCommand.BODY, messageId);
    252    }
    253
    254    /**
    255     * Closes the connection to the NNTP server and sets to null some internal data so that the memory may be reclaimed by the garbage collector. The reply text
    256     * and code information from the last command is voided so that the memory it used may be reclaimed.
    257     *
    258     * @throws IOException If an error occurs while disconnecting.
    259     */
    260    @Override
    261    public void disconnect() throws IOException {
    262        super.disconnect();
    263        _reader_ = null;
    264        _writer_ = null;
    265        replyString = null;
    266        _isAllowedToPost = false;
    267    }
    268
    269    /**
    270     * Gets the command support.
    271     */
    272    @Override
    273    protected ProtocolCommandSupport getCommandSupport() {
    274        return _commandSupport_;
    275    }
    276
    277    /**
    278     * Gets a reply from the NNTP server and returns the integer reply code. After calling this method, the actual reply text can be accessed from
    279     * {@link #getReplyString getReplyString}. Only use this method if you are implementing your own NNTP client or if you need to fetch a secondary response
    280     * from the NNTP server.
    281     *
    282     * @return The integer value of the reply code of the fetched NNTP reply. in response to the command.
    283     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    284     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    285     *                                       independently as itself.
    286     * @throws IOException                   If an I/O error occurs while receiving the server reply.
    287     */
    288    public int getReply() throws IOException {
    289        replyString = _reader_.readLine();
    290
    291        if (replyString == null) {
    292            throw new NNTPConnectionClosedException("Connection closed without indication.");
    293        }
    294
    295        // In case we run into an anomaly we don't want fatal index exceptions
    296        // to be thrown.
    297        if (replyString.length() < 3) {
    298            throw new MalformedServerReplyException("Truncated server reply: " + replyString);
    299        }
    300
    301        try {
    302            replyCode = Integer.parseInt(replyString.substring(0, 3));
    303        } catch (final NumberFormatException e) {
    304            throw new MalformedServerReplyException("Could not parse response code.\nServer Reply: " + replyString);
    305        }
    306
    307        fireReplyReceived(replyCode, replyString + NETASCII_EOL);
    308
    309        if (replyCode == NNTPReply.SERVICE_DISCONTINUED) {
    310            throw new NNTPConnectionClosedException("NNTP response 400 received.  Server closed connection.");
    311        }
    312        return replyCode;
    313    }
    314
    315    /**
    316     * Gets the integer value of the reply code of the last NNTP reply. You will usually only use this method after you connect to the NNTP server to check
    317     * that the connection was successful since {@code connect} is of type void.
    318     *
    319     * @return The integer value of the reply code of the last NNTP reply.
    320     */
    321    public int getReplyCode() {
    322        return replyCode;
    323    }
    324
    325    /**
    326     * Gets the entire text of the last NNTP server response exactly as it was received, not including the end of line marker.
    327     *
    328     * @return The entire text from the last NNTP response as a String.
    329     */
    330    public String getReplyString() {
    331        return replyString;
    332    }
    333
    334    /**
    335     * A convenience method to send the NNTP GROUP command to the server, receive the reply, and return the reply code.
    336     *
    337     * @param newsgroup The name of the newsgroup to select.
    338     * @return The reply code received from the server.
    339     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    340     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    341     *                                       independently as itself.
    342     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    343     */
    344    public int group(final String newsgroup) throws IOException {
    345        return sendCommand(NNTPCommand.GROUP, newsgroup);
    346    }
    347
    348    /**
    349     * A convenience method to send the NNTP HEAD command to the server, receive the initial reply, and return the reply code.
    350     *
    351     * @return The reply code received from the server.
    352     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    353     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    354     *                                       independently as itself.
    355     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    356     */
    357    public int head() throws IOException {
    358        return sendCommand(NNTPCommand.HEAD);
    359    }
    360
    361    /**
    362     * @param a article number
    363     * @return number
    364     * @throws IOException on error
    365     * @deprecated - for API compatibility only - DO NOT USE
    366     */
    367    @Deprecated
    368    public int head(final int a) throws IOException {
    369        return head((long) a);
    370    }
    371
    372    /**
    373     * A convenience method to send the NNTP HEAD command to the server, receive the initial reply, and return the reply code.
    374     *
    375     * @param articleNumber The number of the article to request from the currently selected newsgroup.
    376     * @return The reply code received from the server.
    377     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    378     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    379     *                                       independently as itself.
    380     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    381     */
    382    public int head(final long articleNumber) throws IOException {
    383        return sendCommand(NNTPCommand.HEAD, Long.toString(articleNumber));
    384    }
    385
    386    /**
    387     * A convenience method to send the NNTP HEAD command to the server, receive the initial reply, and return the reply code.
    388     *
    389     * @param messageId The message identifier of the requested article, including the encapsulating &lt; and &gt; characters.
    390     * @return The reply code received from the server.
    391     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    392     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    393     *                                       independently as itself.
    394     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    395     */
    396    public int head(final String messageId) throws IOException {
    397        return sendCommand(NNTPCommand.HEAD, messageId);
    398    }
    399
    400    /**
    401     * A convenience method to send the NNTP HELP command to the server, receive the reply, and return the reply code.
    402     *
    403     * @return The reply code received from the server.
    404     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    405     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    406     *                                       independently as itself.
    407     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    408     */
    409    public int help() throws IOException {
    410        return sendCommand(NNTPCommand.HELP);
    411    }
    412
    413    /**
    414     * A convenience method to send the NNTP IHAVE command to the server, receive the reply, and return the reply code.
    415     *
    416     * @param messageId The article identifier, including the encapsulating &lt; and &gt; characters.
    417     * @return The reply code received from the server.
    418     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    419     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    420     *                                       independently as itself.
    421     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    422     */
    423    public int ihave(final String messageId) throws IOException {
    424        return sendCommand(NNTPCommand.IHAVE, messageId);
    425    }
    426
    427    /**
    428     * Indicates whether or not the client is allowed to post articles to the server it is currently connected to.
    429     *
    430     * @return True if the client can post articles to the server, false otherwise.
    431     */
    432    public boolean isAllowedToPost() {
    433        return _isAllowedToPost;
    434    }
    435
    436    /**
    437     * A convenience method to send the NNTP LAST command to the server, receive the reply, and return the reply code.
    438     *
    439     * @return The reply code received from the server.
    440     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    441     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    442     *                                       independently as itself.
    443     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    444     */
    445    public int last() throws IOException {
    446        return sendCommand(NNTPCommand.LAST);
    447    }
    448
    449    /**
    450     * A convenience method to send the NNTP LIST command to the server, receive the reply, and return the reply code.
    451     *
    452     * @return The reply code received from the server.
    453     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    454     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    455     *                                       independently as itself.
    456     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    457     */
    458    public int list() throws IOException {
    459        return sendCommand(NNTPCommand.LIST);
    460    }
    461
    462    /**
    463     * A convenience wrapper for the extended LIST command that takes an argument, allowing us to selectively list multiple groups.
    464     *
    465     * @param wildmat A wildmat (pseudo-regex) pattern. See RFC 2980 for details.
    466     * @return the reply code received from the server.
    467     * @throws IOException if the command fails
    468     */
    469    public int listActive(final String wildmat) throws IOException {
    470        final StringBuilder command = new StringBuilder("ACTIVE ");
    471        command.append(wildmat);
    472        return sendCommand(NNTPCommand.LIST, command.toString());
    473    }
    474
    475    /**
    476     * A convenience method to send the "NEWGROUPS" command to the server, receive the reply, and return the reply code.
    477     *
    478     * @param date          The date after which to check for new groups. Date format is YYMMDD
    479     * @param time          The time after which to check for new groups. Time format is HHMMSS using a 24-hour clock.
    480     * @param GMT           True if the time is in GMT, false if local server time.
    481     * @param distributions Comma-separated distribution list to check for new groups. Set to null if no distributions.
    482     * @return The reply code received from the server.
    483     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    484     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    485     *                                       independently as itself.
    486     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    487     */
    488    public int newgroups(final String date, final String time, final boolean GMT, final String distributions) throws IOException {
    489        final StringBuilder buffer = new StringBuilder();
    490
    491        buffer.append(date);
    492        buffer.append(' ');
    493        buffer.append(time);
    494
    495        if (GMT) {
    496            buffer.append(' ');
    497            buffer.append("GMT");
    498        }
    499
    500        if (distributions != null) {
    501            buffer.append(" <");
    502            buffer.append(distributions);
    503            buffer.append('>');
    504        }
    505
    506        return sendCommand(NNTPCommand.NEWGROUPS, buffer.toString());
    507    }
    508
    509    /**
    510     * A convenience method to send the "NEWNEWS" command to the server, receive the reply, and return the reply code.
    511     *
    512     * @param newsgroups    A comma-separated list of newsgroups to check for new news.
    513     * @param date          The date after which to check for new news. Date format is YYMMDD
    514     * @param time          The time after which to check for new news. Time format is HHMMSS using a 24-hour clock.
    515     * @param GMT           True if the time is in GMT, false if local server time.
    516     * @param distributions Comma-separated distribution list to check for new news. Set to null if no distributions.
    517     * @return The reply code received from the server.
    518     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    519     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    520     *                                       independently as itself.
    521     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    522     */
    523    public int newnews(final String newsgroups, final String date, final String time, final boolean GMT, final String distributions) throws IOException {
    524        final StringBuilder buffer = new StringBuilder();
    525
    526        buffer.append(newsgroups);
    527        buffer.append(' ');
    528        buffer.append(date);
    529        buffer.append(' ');
    530        buffer.append(time);
    531
    532        if (GMT) {
    533            buffer.append(' ');
    534            buffer.append("GMT");
    535        }
    536
    537        if (distributions != null) {
    538            buffer.append(" <");
    539            buffer.append(distributions);
    540            buffer.append('>');
    541        }
    542
    543        return sendCommand(NNTPCommand.NEWNEWS, buffer.toString());
    544    }
    545
    546    /**
    547     * A convenience method to send the NNTP NEXT command to the server, receive the reply, and return the reply code.
    548     *
    549     * @return The reply code received from the server.
    550     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    551     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    552     *                                       independently as itself.
    553     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    554     */
    555    public int next() throws IOException {
    556        return sendCommand(NNTPCommand.NEXT);
    557    }
    558
    559    /**
    560     * A convenience method to send the NNTP POST command to the server, receive the reply, and return the reply code.
    561     *
    562     * @return The reply code received from the server.
    563     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    564     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    565     *                                       independently as itself.
    566     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    567     */
    568    public int post() throws IOException {
    569        return sendCommand(NNTPCommand.POST);
    570    }
    571
    572    /**
    573     * A convenience method to send the NNTP QUIT command to the server, receive the reply, and return the reply code.
    574     *
    575     * @return The reply code received from the server.
    576     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    577     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    578     *                                       independently as itself.
    579     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    580     */
    581    public int quit() throws IOException {
    582        return sendCommand(NNTPCommand.QUIT);
    583    }
    584
    585    /**
    586     * Sends an NNTP command with no arguments to the server, waits for a reply and returns the numerical response code. After invocation, for more detailed
    587     * information, the actual reply text can be accessed by calling {@link #getReplyString getReplyString}.
    588     *
    589     * @param command The NNTPCommand constant corresponding to the NNTP command to send.
    590     * @return The integer value of the NNTP reply code returned by the server in response to the command. in response to the command.
    591     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    592     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    593     *                                       independently as itself.
    594     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    595     */
    596    public int sendCommand(final int command) throws IOException {
    597        return sendCommand(command, null);
    598    }
    599
    600    /**
    601     * Sends an NNTP command to the server, waits for a reply and returns the numerical response code. After invocation, for more detailed information, the
    602     * actual reply text can be accessed by calling {@link #getReplyString getReplyString}.
    603     *
    604     * @param command The NNTPCommand constant corresponding to the NNTP command to send.
    605     * @param args    The arguments to the NNTP command. If this parameter is set to null, then the command is sent with no argument.
    606     * @return The integer value of the NNTP reply code returned by the server in response to the command. in response to the command.
    607     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    608     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    609     *                                       independently as itself.
    610     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    611     */
    612    public int sendCommand(final int command, final String args) throws IOException {
    613        return sendCommand(NNTPCommand.getCommand(command), args);
    614    }
    615
    616    /**
    617     * Sends an NNTP command with no arguments to the server, waits for a reply and returns the numerical response code. After invocation, for more detailed
    618     * information, the actual reply text can be accessed by calling {@link #getReplyString getReplyString}.
    619     *
    620     * @param command The text representation of the NNTP command to send.
    621     * @return The integer value of the NNTP reply code returned by the server in response to the command. in response to the command.
    622     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    623     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    624     *                                       independently as itself.
    625     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    626     */
    627    public int sendCommand(final String command) throws IOException {
    628        return sendCommand(command, null);
    629    }
    630
    631    /**
    632     * Sends an NNTP command to the server, waits for a reply and returns the numerical response code. After invocation, for more detailed information, the
    633     * actual reply text can be accessed by calling {@link #getReplyString getReplyString}.
    634     *
    635     * @param command The text representation of the NNTP command to send.
    636     * @param args    The arguments to the NNTP command. If this parameter is set to null, then the command is sent with no argument.
    637     * @return The integer value of the NNTP reply code returned by the server in response to the command.
    638     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    639     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    640     *                                       independently as itself.
    641     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    642     */
    643    public int sendCommand(final String command, final String args) throws IOException {
    644        final StringBuilder builder = new StringBuilder(command);
    645        if (args != null) {
    646            builder.append(' ');
    647            builder.append(args);
    648        }
    649        builder.append(NETASCII_EOL);
    650        final String message;
    651        _writer_.write(message = builder.toString());
    652        _writer_.flush();
    653        fireCommandSent(command, message);
    654        return getReply();
    655    }
    656
    657    /**
    658     * A convenience method to send the NNTP STAT command to the server, receive the initial reply, and return the reply code.
    659     *
    660     * @return The reply code received from the server.
    661     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    662     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    663     *                                       independently as itself.
    664     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    665     */
    666    public int stat() throws IOException {
    667        return sendCommand(NNTPCommand.STAT);
    668    }
    669
    670    /**
    671     * @param a article number
    672     * @return number
    673     * @throws IOException on error
    674     * @deprecated - for API compatibility only - DO NOT USE
    675     */
    676    @Deprecated
    677    public int stat(final int a) throws IOException {
    678        return stat((long) a);
    679    }
    680
    681    /**
    682     * A convenience method to send the NNTP STAT command to the server, receive the initial reply, and return the reply code.
    683     *
    684     * @param articleNumber The number of the article to request from the currently selected newsgroup.
    685     * @return The reply code received from the server.
    686     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    687     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    688     *                                       independently as itself.
    689     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    690     */
    691    public int stat(final long articleNumber) throws IOException {
    692        return sendCommand(NNTPCommand.STAT, Long.toString(articleNumber));
    693    }
    694
    695    /**
    696     * A convenience method to send the NNTP STAT command to the server, receive the initial reply, and return the reply code.
    697     *
    698     * @param messageId The message identifier of the requested article, including the encapsulating &lt; and &gt; characters.
    699     * @return The reply code received from the server.
    700     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    701     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    702     *                                       independently as itself.
    703     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    704     */
    705    public int stat(final String messageId) throws IOException {
    706        return sendCommand(NNTPCommand.STAT, messageId);
    707    }
    708
    709    /**
    710     * A convenience method to send the NNTP XHDR command to the server, receive the reply, and return the reply code.
    711     *
    712     * @param header           a String naming a header line (e.g., "subject"). See RFC-1036 for a list of valid header lines.
    713     * @param selectedArticles a String representation of the range of article headers required. This may be an article number, or a range of article numbers in
    714     *                         the form "XXXX-YYYY", where XXXX and YYYY are valid article numbers in the current group. It also may be of the form "XXX-",
    715     *                         meaning "return XXX and all following articles" In this revision, the last format is not possible (yet).
    716     * @return The reply code received from the server.
    717     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    718     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    719     *                                       independently as itself.
    720     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    721     */
    722    public int xhdr(final String header, final String selectedArticles) throws IOException {
    723        final StringBuilder command = new StringBuilder(header);
    724        command.append(" ");
    725        command.append(selectedArticles);
    726        return sendCommand(NNTPCommand.XHDR, command.toString());
    727    }
    728
    729    /**
    730     * A convenience method to send the NNTP XOVER command to the server, receive the reply, and return the reply code.
    731     *
    732     * @param selectedArticles a String representation of the range of article headers required. This may be an article number, or a range of article numbers in
    733     *                         the form "XXXX-YYYY", where XXXX and YYYY are valid article numbers in the current group. It also may be of the form "XXX-",
    734     *                         meaning "return XXX and all following articles" In this revision, the last format is not possible (yet).
    735     * @return The reply code received from the server.
    736     * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason
    737     *                                       causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or
    738     *                                       independently as itself.
    739     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
    740     */
    741    public int xover(final String selectedArticles) throws IOException {
    742        return sendCommand(NNTPCommand.XOVER, selectedArticles);
    743    }
    744}