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