001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
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
035 * only so that you may easily implement your own NNTP client if
036 * you so desire.  If you have no need to perform your own implementation,
037 * you should use {@link org.apache.commons.net.nntp.NNTPClient}.
038 * The NNTP class is made public to provide access to various NNTP constants
039 * and to make it easier for adventurous programmers (or those with special
040 * needs) to interact with the NNTP protocol and implement their own clients.
041 * A set of methods with names corresponding to the NNTP command names are
042 * provided to facilitate this interaction.
043 * <p>
044 * You should keep in mind that the NNTP server may choose to prematurely
045 * close a connection if the client has been idle for longer than a
046 * given time period or if the server is being shutdown by the operator or
047 * some other reason.  The NNTP class will detect a
048 * premature NNTP server connection closing when it receives a
049 * {@link org.apache.commons.net.nntp.NNTPReply#SERVICE_DISCONTINUED NNTPReply.SERVICE_DISCONTINUED }
050 *  response to a command.
051 * When that occurs, the NNTP class method encountering that reply will throw
052 * an {@link org.apache.commons.net.nntp.NNTPConnectionClosedException}
053 * .
054 * <code>NNTPConectionClosedException</code>
055 * is a subclass of <code> IOException </code> and therefore need not be
056 * caught separately, but if you are going to catch it separately, its
057 * catch block must appear before the more general <code> IOException </code>
058 * catch block.  When you encounter an
059 * {@link org.apache.commons.net.nntp.NNTPConnectionClosedException}
060 * , you must disconnect the connection with
061 * {@link #disconnect  disconnect() } to properly clean up the
062 * system resources used by NNTP.  Before disconnecting, you may check the
063 * last reply code and text with
064 * {@link #getReplyCode  getReplyCode } and
065 * {@link #getReplyString  getReplyString }.
066 * <p>
067 * Rather than list it separately for each method, we mention here that
068 * every method communicating with the server and throwing an IOException
069 * can also throw a
070 * {@link org.apache.commons.net.MalformedServerReplyException}
071 * , which is a subclass
072 * of IOException.  A MalformedServerReplyException will be thrown when
073 * the reply received from the server deviates enough from the protocol
074 * specification that it cannot be interpreted in a useful manner despite
075 * attempts to be as lenient as possible.
076 *
077 * @see NNTPClient
078 * @see NNTPConnectionClosedException
079 * @see org.apache.commons.net.MalformedServerReplyException
080 */
081
082public class NNTP extends SocketClient
083{
084    /** The default NNTP port.  Its value is 119 according to RFC 977. */
085    public static final int DEFAULT_PORT = 119;
086
087    // We have to ensure that the protocol communication is in ASCII
088    // but we use ISO-8859-1 just in case 8-bit characters cross
089    // the wire.
090    private static final Charset DEFAULT_ENCODING = StandardCharsets.ISO_8859_1;
091
092    boolean _isAllowedToPost;
093    private int replyCode;
094    private String replyString;
095
096    /**
097     * Wraps {@link SocketClient#_input_}
098     * to communicate with server.  Initialized by {@link #_connectAction_}.
099     * All server reads should be done through this variable.
100     */
101    protected BufferedReader _reader_;
102
103    /**
104     * Wraps {@link SocketClient#_output_}
105     * to communicate with server.  Initialized by {@link #_connectAction_}.
106     * All server reads should be done through this variable.
107     */
108    protected BufferedWriter _writer_;
109
110    /**
111     * A ProtocolCommandSupport object used to manage the registering of
112     * ProtocolCommandListeners and te firing of ProtocolCommandEvents.
113     */
114    protected ProtocolCommandSupport _commandSupport_;
115
116    /**
117     * The default NNTP constructor.  Sets the default port to
118     * <code>DEFAULT_PORT</code> and initializes internal data structures
119     * for saving NNTP reply information.
120     */
121    public NNTP()
122    {
123        setDefaultPort(DEFAULT_PORT);
124        replyString = null;
125        _reader_ = null;
126        _writer_ = null;
127        _isAllowedToPost = false;
128        _commandSupport_ = new ProtocolCommandSupport(this);
129    }
130
131    /**
132     * Initiates control connections and gets initial reply, determining
133     * if the client is allowed to post to the server.  Initializes
134     * {@link #_reader_} and {@link #_writer_} to wrap
135     * {@link SocketClient#_input_} and {@link SocketClient#_output_}.
136     */
137    @Override
138    protected void _connectAction_() throws IOException
139    {
140        super._connectAction_();
141        _reader_ =
142            new CRLFLineReader(new InputStreamReader(_input_,
143                                                     DEFAULT_ENCODING));
144        _writer_ =
145            new BufferedWriter(new OutputStreamWriter(_output_,
146                                                      DEFAULT_ENCODING));
147        getReply();
148
149        _isAllowedToPost = replyCode == NNTPReply.SERVER_READY_POSTING_ALLOWED;
150    }
151
152    /**
153     * Closes the connection to the NNTP server and sets to null
154     * some internal data so that the memory may be reclaimed by the
155     * garbage collector.  The reply text and code information from the
156     * last command is voided so that the memory it used may be reclaimed.
157     * <p>
158     * @throws IOException If an error occurs while disconnecting.
159     */
160    @Override
161    public void disconnect() throws IOException
162    {
163        super.disconnect();
164        _reader_ = null;
165        _writer_ = null;
166        replyString = null;
167        _isAllowedToPost = false;
168    }
169
170
171    /**
172     * Indicates whether or not the client is allowed to post articles to
173     * the server it is currently connected to.
174     * <p>
175     * @return True if the client can post articles to the server, false
176     *         otherwise.
177     */
178    public boolean isAllowedToPost()
179    {
180        return _isAllowedToPost;
181    }
182
183
184    /**
185     * Sends an NNTP command to the server, waits for a reply and returns the
186     * numerical response code.  After invocation, for more detailed
187     * information, the actual reply text can be accessed by calling
188     * {@link #getReplyString  getReplyString }.
189     * <p>
190     * @param command  The text representation of the  NNTP command to send.
191     * @param args The arguments to the NNTP command.  If this parameter is
192     *             set to null, then the command is sent with no argument.
193     * @return The integer value of the NNTP reply code returned by the server
194     *         in response to the command.
195     * @throws NNTPConnectionClosedException
196     *      If the NNTP server prematurely closes the connection as a result
197     *      of the client being idle or some other reason causing the server
198     *      to send NNTP reply code 400.  This exception may be caught either
199     *      as an IOException or independently as itself.
200     * @throws IOException  If an I/O error occurs while either sending the
201     *      command or receiving the server reply.
202     */
203    public int sendCommand(final String command, final String args) throws IOException
204    {
205        final StringBuilder __commandBuffer = new StringBuilder();
206        __commandBuffer.append(command);
207
208        if (args != null)
209        {
210            __commandBuffer.append(' ');
211            __commandBuffer.append(args);
212        }
213        __commandBuffer.append(SocketClient.NETASCII_EOL);
214
215        final String message;
216        _writer_.write(message = __commandBuffer.toString());
217        _writer_.flush();
218
219        fireCommandSent(command, message);
220
221        return getReply();
222    }
223
224
225    /**
226     * Sends an NNTP command to the server, waits for a reply and returns the
227     * numerical response code.  After invocation, for more detailed
228     * information, the actual reply text can be accessed by calling
229     * {@link #getReplyString  getReplyString }.
230     * <p>
231     * @param command  The NNTPCommand constant corresponding to the NNTP command
232     *                 to send.
233     * @param args The arguments to the NNTP command.  If this parameter is
234     *             set to null, then the command is sent with no argument.
235     * @return The integer value of the NNTP reply code returned by the server
236     *         in response to the command.
237     *         in response to the command.
238     * @throws NNTPConnectionClosedException
239     *      If the NNTP server prematurely closes the connection as a result
240     *      of the client being idle or some other reason causing the server
241     *      to send NNTP reply code 400.  This exception may be caught either
242     *      as an IOException or independently as itself.
243     * @throws IOException  If an I/O error occurs while either sending the
244     *      command or receiving the server reply.
245     */
246    public int sendCommand(final int command, final String args) throws IOException
247    {
248        return sendCommand(NNTPCommand.getCommand(command), args);
249    }
250
251
252    /**
253     * Sends an NNTP command with no arguments to the server, waits for a
254     * reply and returns the numerical response code.  After invocation, for
255     * more detailed information, the actual reply text can be accessed by
256     * calling {@link #getReplyString  getReplyString }.
257     * <p>
258     * @param command  The text representation of the  NNTP command to send.
259     * @return The integer value of the NNTP reply code returned by the server
260     *         in response to the command.
261     *         in response to the command.
262     * @throws NNTPConnectionClosedException
263     *      If the NNTP server prematurely closes the connection as a result
264     *      of the client being idle or some other reason causing the server
265     *      to send NNTP reply code 400.  This exception may be caught either
266     *      as an IOException or independently as itself.
267     * @throws IOException  If an I/O error occurs while either sending the
268     *      command or receiving the server reply.
269     */
270    public int sendCommand(final String command) throws IOException
271    {
272        return sendCommand(command, null);
273    }
274
275
276    /**
277     * Sends an NNTP command with no arguments to the server, waits for a
278     * reply and returns the numerical response code.  After invocation, for
279     * more detailed information, the actual reply text can be accessed by
280     * calling {@link #getReplyString  getReplyString }.
281     * <p>
282     * @param command  The NNTPCommand constant corresponding to the NNTP command
283     *                 to send.
284     * @return The integer value of the NNTP reply code returned by the server
285     *         in response to the command.
286     *         in response to the command.
287     * @throws NNTPConnectionClosedException
288     *      If the NNTP server prematurely closes the connection as a result
289     *      of the client being idle or some other reason causing the server
290     *      to send NNTP reply code 400.  This exception may be caught either
291     *      as an IOException or independently as itself.
292     * @throws IOException  If an I/O error occurs while either sending the
293     *      command or receiving the server reply.
294     */
295    public int sendCommand(final int command) throws IOException
296    {
297        return sendCommand(command, null);
298    }
299
300
301    /**
302     * Returns the integer value of the reply code of the last NNTP reply.
303     * You will usually only use this method after you connect to the
304     * NNTP server to check that the connection was successful since
305     * <code> connect </code> is of type void.
306     * <p>
307     * @return The integer value of the reply code of the last NNTP reply.
308     */
309    public int getReplyCode()
310    {
311        return replyCode;
312    }
313
314    /**
315     * Fetches a reply from the NNTP server and returns the integer reply
316     * code.  After calling this method, the actual reply text can be accessed
317     * from {@link #getReplyString  getReplyString }.  Only use this
318     * method if you are implementing your own NNTP client or if you need to
319     * fetch a secondary response from the NNTP server.
320     * <p>
321     * @return The integer value of the reply code of the fetched NNTP reply.
322     *         in response to the command.
323     * @throws NNTPConnectionClosedException
324     *      If the NNTP server prematurely closes the connection as a result
325     *      of the client being idle or some other reason causing the server
326     *      to send NNTP reply code 400.  This exception may be caught either
327     *      as an IOException or independently as itself.
328     * @throws IOException  If an I/O error occurs while
329     *      receiving the server reply.
330     */
331    public int getReply() throws IOException
332    {
333        replyString = _reader_.readLine();
334
335        if (replyString == null) {
336            throw new NNTPConnectionClosedException(
337                    "Connection closed without indication.");
338        }
339
340        // In case we run into an anomaly we don't want fatal index exceptions
341        // to be thrown.
342        if (replyString.length() < 3) {
343            throw new MalformedServerReplyException(
344                "Truncated server reply: " + replyString);
345        }
346
347        try
348        {
349            replyCode = Integer.parseInt(replyString.substring(0, 3));
350        }
351        catch (final NumberFormatException e)
352        {
353            throw new MalformedServerReplyException(
354                "Could not parse response code.\nServer Reply: " + replyString);
355        }
356
357        fireReplyReceived(replyCode, replyString + SocketClient.NETASCII_EOL);
358
359        if (replyCode == NNTPReply.SERVICE_DISCONTINUED) {
360            throw new NNTPConnectionClosedException(
361                "NNTP response 400 received.  Server closed connection.");
362        }
363        return replyCode;
364    }
365
366
367    /**
368     * Returns the entire text of the last NNTP server response exactly
369     * as it was received, not including the end of line marker.
370     * <p>
371     * @return The entire text from the last NNTP response as a String.
372     */
373    public String getReplyString()
374    {
375        return replyString;
376    }
377
378
379    /**
380     * A convenience method to send the NNTP ARTICLE command to the server,
381     * receive the initial reply, and return the reply code.
382     * <p>
383     * @param messageId  The message identifier of the requested article,
384     *                   including the encapsulating &lt; and &gt; characters.
385     * @return The reply code received from the server.
386     * @throws NNTPConnectionClosedException
387     *      If the NNTP server prematurely closes the connection as a result
388     *      of the client being idle or some other reason causing the server
389     *      to send NNTP reply code 400.  This exception may be caught either
390     *      as an IOException or independently as itself.
391     * @throws IOException  If an I/O error occurs while either sending the
392     *      command or receiving the server reply.
393     */
394    public int article(final String messageId) throws IOException
395    {
396        return sendCommand(NNTPCommand.ARTICLE, messageId);
397    }
398
399    /**
400     * A convenience method to send the NNTP ARTICLE command to the server,
401     * receive the initial reply, and return the reply code.
402     * <p>
403     * @param articleNumber The number of the article to request from the
404     *                      currently selected newsgroup.
405     * @return The reply code received from the server.
406     * @throws NNTPConnectionClosedException
407     *      If the NNTP server prematurely closes the connection as a result
408     *      of the client being idle or some other reason causing the server
409     *      to send NNTP reply code 400.  This exception may be caught either
410     *      as an IOException or independently as itself.
411     * @throws IOException  If an I/O error occurs while either sending the
412     *      command or receiving the server reply.
413     */
414    public int article(final long articleNumber) throws IOException
415    {
416        return sendCommand(NNTPCommand.ARTICLE, Long.toString(articleNumber));
417    }
418
419    /**
420     * A convenience method to send the NNTP ARTICLE command to the server,
421     * receive the initial reply, and return the reply code.
422     * <p>
423     * @return The reply code received from the server.
424     * @throws NNTPConnectionClosedException
425     *      If the NNTP server prematurely closes the connection as a result
426     *      of the client being idle or some other reason causing the server
427     *      to send NNTP reply code 400.  This exception may be caught either
428     *      as an IOException or independently as itself.
429     * @throws IOException  If an I/O error occurs while either sending the
430     *      command or receiving the server reply.
431     */
432    public int article() throws IOException
433    {
434        return sendCommand(NNTPCommand.ARTICLE);
435    }
436
437
438
439    /**
440     * A convenience method to send the NNTP BODY command to the server,
441     * receive the initial reply, and return the reply code.
442     * <p>
443     * @param messageId  The message identifier of the requested article,
444     *                   including the encapsulating &lt; and &gt; characters.
445     * @return The reply code received from the server.
446     * @throws NNTPConnectionClosedException
447     *      If the NNTP server prematurely closes the connection as a result
448     *      of the client being idle or some other reason causing the server
449     *      to send NNTP reply code 400.  This exception may be caught either
450     *      as an IOException or independently as itself.
451     * @throws IOException  If an I/O error occurs while either sending the
452     *      command or receiving the server reply.
453     */
454    public int body(final String messageId) throws IOException
455    {
456        return sendCommand(NNTPCommand.BODY, messageId);
457    }
458
459    /**
460     * A convenience method to send the NNTP BODY command to the server,
461     * receive the initial reply, and return the reply code.
462     * <p>
463     * @param articleNumber The number of the article to request from the
464     *                      currently selected newsgroup.
465     * @return The reply code received from the server.
466     * @throws NNTPConnectionClosedException
467     *      If the NNTP server prematurely closes the connection as a result
468     *      of the client being idle or some other reason causing the server
469     *      to send NNTP reply code 400.  This exception may be caught either
470     *      as an IOException or independently as itself.
471     * @throws IOException  If an I/O error occurs while either sending the
472     *      command or receiving the server reply.
473     */
474    public int body(final long articleNumber) throws IOException
475    {
476        return sendCommand(NNTPCommand.BODY, Long.toString(articleNumber));
477    }
478
479    /**
480     * A convenience method to send the NNTP BODY command to the server,
481     * receive the initial reply, and return the reply code.
482     * <p>
483     * @return The reply code received from the server.
484     * @throws NNTPConnectionClosedException
485     *      If the NNTP server prematurely closes the connection as a result
486     *      of the client being idle or some other reason causing the server
487     *      to send NNTP reply code 400.  This exception may be caught either
488     *      as an IOException or independently as itself.
489     * @throws IOException  If an I/O error occurs while either sending the
490     *      command or receiving the server reply.
491     */
492    public int body() throws IOException
493    {
494        return sendCommand(NNTPCommand.BODY);
495    }
496
497
498
499    /**
500     * A convenience method to send the NNTP HEAD command to the server,
501     * receive the initial reply, and return the reply code.
502     * <p>
503     * @param messageId  The message identifier of the requested article,
504     *                   including the encapsulating &lt; and &gt; characters.
505     * @return The reply code received from the server.
506     * @throws NNTPConnectionClosedException
507     *      If the NNTP server prematurely closes the connection as a result
508     *      of the client being idle or some other reason causing the server
509     *      to send NNTP reply code 400.  This exception may be caught either
510     *      as an IOException or independently as itself.
511     * @throws IOException  If an I/O error occurs while either sending the
512     *      command or receiving the server reply.
513     */
514    public int head(final String messageId) throws IOException
515    {
516        return sendCommand(NNTPCommand.HEAD, messageId);
517    }
518
519    /**
520     * A convenience method to send the NNTP HEAD command to the server,
521     * receive the initial reply, and return the reply code.
522     * <p>
523     * @param articleNumber The number of the article to request from the
524     *                      currently selected newsgroup.
525     * @return The reply code received from the server.
526     * @throws NNTPConnectionClosedException
527     *      If the NNTP server prematurely closes the connection as a result
528     *      of the client being idle or some other reason causing the server
529     *      to send NNTP reply code 400.  This exception may be caught either
530     *      as an IOException or independently as itself.
531     * @throws IOException  If an I/O error occurs while either sending the
532     *      command or receiving the server reply.
533     */
534    public int head(final long articleNumber) throws IOException
535    {
536        return sendCommand(NNTPCommand.HEAD, Long.toString(articleNumber));
537    }
538
539    /**
540     * A convenience method to send the NNTP HEAD command to the server,
541     * receive the initial reply, and return the reply code.
542     * <p>
543     * @return The reply code received from the server.
544     * @throws NNTPConnectionClosedException
545     *      If the NNTP server prematurely closes the connection as a result
546     *      of the client being idle or some other reason causing the server
547     *      to send NNTP reply code 400.  This exception may be caught either
548     *      as an IOException or independently as itself.
549     * @throws IOException  If an I/O error occurs while either sending the
550     *      command or receiving the server reply.
551     */
552    public int head() throws IOException
553    {
554        return sendCommand(NNTPCommand.HEAD);
555    }
556
557
558
559    /**
560     * A convenience method to send the NNTP STAT command to the server,
561     * receive the initial reply, and return the reply code.
562     * <p>
563     * @param messageId  The message identifier of the requested article,
564     *                   including the encapsulating &lt; and &gt; characters.
565     * @return The reply code received from the server.
566     * @throws NNTPConnectionClosedException
567     *      If the NNTP server prematurely closes the connection as a result
568     *      of the client being idle or some other reason causing the server
569     *      to send NNTP reply code 400.  This exception may be caught either
570     *      as an IOException or independently as itself.
571     * @throws IOException  If an I/O error occurs while either sending the
572     *      command or receiving the server reply.
573     */
574    public int stat(final String messageId) throws IOException
575    {
576        return sendCommand(NNTPCommand.STAT, messageId);
577    }
578
579    /**
580     * A convenience method to send the NNTP STAT command to the server,
581     * receive the initial reply, and return the reply code.
582     * <p>
583     * @param articleNumber The number of the article to request from the
584     *                      currently selected newsgroup.
585     * @return The reply code received from the server.
586     * @throws NNTPConnectionClosedException
587     *      If the NNTP server prematurely closes the connection as a result
588     *      of the client being idle or some other reason causing the server
589     *      to send NNTP reply code 400.  This exception may be caught either
590     *      as an IOException or independently as itself.
591     * @throws IOException  If an I/O error occurs while either sending the
592     *      command or receiving the server reply.
593     */
594    public int stat(final long articleNumber) throws IOException
595    {
596        return sendCommand(NNTPCommand.STAT, Long.toString(articleNumber));
597    }
598
599    /**
600     * A convenience method to send the NNTP STAT command to the server,
601     * receive the initial reply, and return the reply code.
602     * <p>
603     * @return The reply code received from the server.
604     * @throws NNTPConnectionClosedException
605     *      If the NNTP server prematurely closes the connection as a result
606     *      of the client being idle or some other reason causing the server
607     *      to send NNTP reply code 400.  This exception may be caught either
608     *      as an IOException or independently as itself.
609     * @throws IOException  If an I/O error occurs while either sending the
610     *      command or receiving the server reply.
611     */
612    public int stat() throws IOException
613    {
614        return sendCommand(NNTPCommand.STAT);
615    }
616
617
618    /**
619     * A convenience method to send the NNTP GROUP command to the server,
620     * receive the reply, and return the reply code.
621     * <p>
622     * @param newsgroup  The name of the newsgroup to select.
623     * @return The reply code received from the server.
624     * @throws NNTPConnectionClosedException
625     *      If the NNTP server prematurely closes the connection as a result
626     *      of the client being idle or some other reason causing the server
627     *      to send NNTP reply code 400.  This exception may be caught either
628     *      as an IOException or independently as itself.
629     * @throws IOException  If an I/O error occurs while either sending the
630     *      command or receiving the server reply.
631     */
632    public int group(final String newsgroup) throws IOException
633    {
634        return sendCommand(NNTPCommand.GROUP, newsgroup);
635    }
636
637
638    /**
639     * A convenience method to send the NNTP HELP command to the server,
640     * receive the reply, and return the reply code.
641     * <p>
642     * @return The reply code received from the server.
643     * @throws NNTPConnectionClosedException
644     *      If the NNTP server prematurely closes the connection as a result
645     *      of the client being idle or some other reason causing the server
646     *      to send NNTP reply code 400.  This exception may be caught either
647     *      as an IOException or independently as itself.
648     * @throws IOException  If an I/O error occurs while either sending the
649     *      command or receiving the server reply.
650     */
651    public int help() throws IOException
652    {
653        return sendCommand(NNTPCommand.HELP);
654    }
655
656
657    /**
658     * A convenience method to send the NNTP IHAVE command to the server,
659     * receive the reply, and return the reply code.
660     * <p>
661     * @param messageId  The article identifier,
662     *                   including the encapsulating &lt; and &gt; characters.
663     * @return The reply code received from the server.
664     * @throws NNTPConnectionClosedException
665     *      If the NNTP server prematurely closes the connection as a result
666     *      of the client being idle or some other reason causing the server
667     *      to send NNTP reply code 400.  This exception may be caught either
668     *      as an IOException or independently as itself.
669     * @throws IOException  If an I/O error occurs while either sending the
670     *      command or receiving the server reply.
671     */
672    public int ihave(final String messageId) throws IOException
673    {
674        return sendCommand(NNTPCommand.IHAVE, messageId);
675    }
676
677
678    /**
679     * A convenience method to send the NNTP LAST command to the server,
680     * receive the reply, and return the reply code.
681     * <p>
682     * @return The reply code received from the server.
683     * @throws NNTPConnectionClosedException
684     *      If the NNTP server prematurely closes the connection as a result
685     *      of the client being idle or some other reason causing the server
686     *      to send NNTP reply code 400.  This exception may be caught either
687     *      as an IOException or independently as itself.
688     * @throws IOException  If an I/O error occurs while either sending the
689     *      command or receiving the server reply.
690     */
691    public int last() throws IOException
692    {
693        return sendCommand(NNTPCommand.LAST);
694    }
695
696
697
698    /**
699     * A convenience method to send the NNTP LIST command to the server,
700     * receive the reply, and return the reply code.
701     * <p>
702     * @return The reply code received from the server.
703     * @throws NNTPConnectionClosedException
704     *      If the NNTP server prematurely closes the connection as a result
705     *      of the client being idle or some other reason causing the server
706     *      to send NNTP reply code 400.  This exception may be caught either
707     *      as an IOException or independently as itself.
708     * @throws IOException  If an I/O error occurs while either sending the
709     *      command or receiving the server reply.
710     */
711    public int list() throws IOException
712    {
713        return sendCommand(NNTPCommand.LIST);
714    }
715
716
717
718    /**
719     * A convenience method to send the NNTP NEXT command to the server,
720     * receive the reply, and return the reply code.
721     * <p>
722     * @return The reply code received from the server.
723     * @throws NNTPConnectionClosedException
724     *      If the NNTP server prematurely closes the connection as a result
725     *      of the client being idle or some other reason causing the server
726     *      to send NNTP reply code 400.  This exception may be caught either
727     *      as an IOException or independently as itself.
728     * @throws IOException  If an I/O error occurs while either sending the
729     *      command or receiving the server reply.
730     */
731    public int next() throws IOException
732    {
733        return sendCommand(NNTPCommand.NEXT);
734    }
735
736
737    /**
738     * A convenience method to send the "NEWGROUPS" command to the server,
739     * receive the reply, and return the reply code.
740     * <p>
741     * @param date The date after which to check for new groups.
742     *             Date format is YYMMDD
743     * @param time The time after which to check for new groups.
744     *             Time format is HHMMSS using a 24-hour clock.
745     * @param GMT  True if the time is in GMT, false if local server time.
746     * @param distributions  Comma-separated distribution list to check for
747     *            new groups. Set to null if no distributions.
748     * @return The reply code received from the server.
749     * @throws NNTPConnectionClosedException
750     *      If the NNTP server prematurely closes the connection as a result
751     *      of the client being idle or some other reason causing the server
752     *      to send NNTP reply code 400.  This exception may be caught either
753     *      as an IOException or independently as itself.
754     * @throws IOException  If an I/O error occurs while either sending the
755     *      command or receiving the server reply.
756     */
757    public int newgroups(final String date, final String time, final boolean GMT,
758                         final String distributions) throws IOException
759    {
760        final StringBuilder buffer = new StringBuilder();
761
762        buffer.append(date);
763        buffer.append(' ');
764        buffer.append(time);
765
766        if (GMT)
767        {
768            buffer.append(' ');
769            buffer.append("GMT");
770        }
771
772        if (distributions != null)
773        {
774            buffer.append(" <");
775            buffer.append(distributions);
776            buffer.append('>');
777        }
778
779        return sendCommand(NNTPCommand.NEWGROUPS, buffer.toString());
780    }
781
782
783    /**
784     * A convenience method to send the "NEWNEWS" command to the server,
785     * receive the reply, and return the reply code.
786     * <p>
787     * @param newsgroups A comma-separated list of newsgroups to check for new
788     *             news.
789     * @param date The date after which to check for new news.
790     *             Date format is YYMMDD
791     * @param time The time after which to check for new news.
792     *             Time format is HHMMSS using a 24-hour clock.
793     * @param GMT  True if the time is in GMT, false if local server time.
794     * @param distributions  Comma-separated distribution list to check for
795     *            new news. Set to null if no distributions.
796     * @return The reply code received from the server.
797     * @throws NNTPConnectionClosedException
798     *      If the NNTP server prematurely closes the connection as a result
799     *      of the client being idle or some other reason causing the server
800     *      to send NNTP reply code 400.  This exception may be caught either
801     *      as an IOException or independently as itself.
802     * @throws IOException  If an I/O error occurs while either sending the
803     *      command or receiving the server reply.
804     */
805    public int newnews(final String newsgroups, final String date, final String time, final boolean GMT,
806                       final String distributions) throws IOException
807    {
808        final StringBuilder buffer = new StringBuilder();
809
810        buffer.append(newsgroups);
811        buffer.append(' ');
812        buffer.append(date);
813        buffer.append(' ');
814        buffer.append(time);
815
816        if (GMT)
817        {
818            buffer.append(' ');
819            buffer.append("GMT");
820        }
821
822        if (distributions != null)
823        {
824            buffer.append(" <");
825            buffer.append(distributions);
826            buffer.append('>');
827        }
828
829        return sendCommand(NNTPCommand.NEWNEWS, buffer.toString());
830    }
831
832
833
834    /**
835     * A convenience method to send the NNTP POST command to the server,
836     * receive the reply, and return the reply code.
837     * <p>
838     * @return The reply code received from the server.
839     * @throws NNTPConnectionClosedException
840     *      If the NNTP server prematurely closes the connection as a result
841     *      of the client being idle or some other reason causing the server
842     *      to send NNTP reply code 400.  This exception may be caught either
843     *      as an IOException or independently as itself.
844     * @throws IOException  If an I/O error occurs while either sending the
845     *      command or receiving the server reply.
846     */
847    public int post() throws IOException
848    {
849        return sendCommand(NNTPCommand.POST);
850    }
851
852
853
854    /**
855     * A convenience method to send the NNTP QUIT command to the server,
856     * receive the reply, and return the reply code.
857     * <p>
858     * @return The reply code received from the server.
859     * @throws NNTPConnectionClosedException
860     *      If the NNTP server prematurely closes the connection as a result
861     *      of the client being idle or some other reason causing the server
862     *      to send NNTP reply code 400.  This exception may be caught either
863     *      as an IOException or independently as itself.
864     * @throws IOException  If an I/O error occurs while either sending the
865     *      command or receiving the server reply.
866     */
867    public int quit() throws IOException
868    {
869        return sendCommand(NNTPCommand.QUIT);
870    }
871
872    /**
873     * A convenience method to send the AUTHINFO USER command to the server,
874     *  receive the reply, and return the reply code. (See RFC 2980)
875     * <p>
876     * @param username A valid username.
877     * @return The reply code received from the server. The server should
878     *          return a 381 or 281 for this command.
879     * @throws NNTPConnectionClosedException
880     *      If the NNTP server prematurely closes the connection as a result
881     *      of the client being idle or some other reason causing the server
882     *      to send NNTP reply code 400.  This exception may be caught either
883     *      as an IOException or independently as itself.
884     * @throws IOException  If an I/O error occurs while either sending the
885     *      command or receiving the server reply.
886     */
887    public int authinfoUser(final String username) throws IOException {
888        final String userParameter = "USER " + username;
889        return sendCommand(NNTPCommand.AUTHINFO, userParameter);
890    }
891
892    /**
893     * A convenience method to send the AUTHINFO PASS command to the server,
894     * receive the reply, and return the reply code.  If this step is
895     * required, it should immediately follow the AUTHINFO USER command
896     * (See RFC 2980)
897     * <p>
898     * @param password a valid password.
899     * @return The reply code received from the server. The server should
900     *         return a 281 or 502 for this command.
901     * @throws NNTPConnectionClosedException
902     *      If the NNTP server prematurely closes the connection as a result
903     *      of the client being idle or some other reason causing the server
904     *      to send NNTP reply code 400.  This exception may be caught either
905     *      as an IOException or independently as itself.
906     * @throws IOException  If an I/O error occurs while either sending the
907     *      command or receiving the server reply.
908     */
909    public int authinfoPass(final String password) throws IOException {
910        final String passParameter = "PASS " + password;
911        return sendCommand(NNTPCommand.AUTHINFO, passParameter);
912    }
913
914    /**
915     * A convenience method to send the NNTP XOVER command to the server,
916     * receive the reply, and return the reply code.
917     * <p>
918     * @param selectedArticles a String representation of the range of
919     * article headers required. This may be an article number, or a
920     * range of article numbers in the form "XXXX-YYYY", where XXXX
921     * and YYYY are valid article numbers in the current group.  It
922     * also may be of the form "XXX-", meaning "return XXX and all
923     * following articles" In this revision, the last format is not
924     * possible (yet).
925     * @return The reply code received from the server.
926     * @throws NNTPConnectionClosedException
927     *      If the NNTP server prematurely closes the connection as a result
928     *      of the client being idle or some other reason causing the server
929     *      to send NNTP reply code 400.  This exception may be caught either
930     *      as an IOException or independently as itself.
931     * @throws IOException  If an I/O error occurs while either sending the
932     *      command or receiving the server reply.
933     */
934    public int xover(final String selectedArticles) throws IOException {
935        return sendCommand(NNTPCommand.XOVER, selectedArticles);
936    }
937
938    /**
939     * A convenience method to send the NNTP XHDR command to the server,
940     * receive the reply, and return the reply code.
941     * <p>
942     * @param header a String naming a header line (e.g., "subject").  See
943     * RFC-1036 for a list of valid header lines.
944     * @param selectedArticles a String representation of the range of
945     * article headers required. This may be an article number, or a
946     * range of article numbers in the form "XXXX-YYYY", where XXXX
947     * and YYYY are valid article numbers in the current group.  It
948     * also may be of the form "XXX-", meaning "return XXX and all
949     * following articles" In this revision, the last format is not
950     * possible (yet).
951     * @return The reply code received from the server.
952     * @throws NNTPConnectionClosedException
953     *      If the NNTP server prematurely closes the connection as a result
954     *      of the client being idle or some other reason causing the server
955     *      to send NNTP reply code 400.  This exception may be caught either
956     *      as an IOException or independently as itself.
957     * @throws IOException  If an I/O error occurs while either sending the
958     *      command or receiving the server reply.
959     */
960    public int xhdr(final String header, final String selectedArticles) throws IOException {
961        final StringBuilder command = new StringBuilder(header);
962        command.append(" ");
963        command.append(selectedArticles);
964        return sendCommand(NNTPCommand.XHDR, command.toString());
965    }
966
967    /**
968     * A convenience wrapper for the extended LIST command that takes
969     * an argument, allowing us to selectively list multiple groups.
970     * <p>
971     * @param wildmat A wildmat (pseudo-regex) pattern. See RFC 2980 for
972     *                details.
973     * @return the reply code received from the server.
974     * @throws IOException if the command fails
975     */
976    public int listActive(final String wildmat) throws IOException {
977        final StringBuilder command = new StringBuilder("ACTIVE ");
978        command.append(wildmat);
979        return sendCommand(NNTPCommand.LIST, command.toString());
980    }
981
982    // DEPRECATED METHODS - for API compatibility only - DO NOT USE
983
984    /**
985     * @param a article number
986     * @return number
987     * @throws IOException on error
988     * @deprecated - for API compatibility only - DO NOT USE
989     */
990    @Deprecated
991    public int article(final int a) throws IOException
992    {
993        return article((long) a);
994    }
995
996    /**
997     * @param a article number
998     * @return number
999     * @throws IOException on error
1000     * @deprecated - for API compatibility only - DO NOT USE
1001     */
1002    @Deprecated
1003    public int body(final int a) throws IOException
1004    {
1005        return body((long) a);
1006    }
1007
1008    /**
1009     * @param a article number
1010     * @return number
1011     * @throws IOException on error
1012     * @deprecated - for API compatibility only - DO NOT USE
1013     */
1014    @Deprecated
1015    public int head(final int a) throws IOException
1016    {
1017        return head((long) a);
1018    }
1019
1020    /**
1021     * @param a article number
1022     * @return number
1023     * @throws IOException on error
1024     * @deprecated - for API compatibility only - DO NOT USE
1025     */
1026    @Deprecated
1027    public int stat(final int a) throws IOException
1028    {
1029        return stat((long) a);
1030    }
1031
1032    /**
1033     * Provide command support to super-class
1034     */
1035    @Override
1036    protected ProtocolCommandSupport getCommandSupport() {
1037        return _commandSupport_;
1038    }
1039}