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