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    *      http://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.IOException;
22  import java.io.Reader;
23  import java.io.StringWriter;
24  import java.io.Writer;
25  import java.util.ArrayList;
26  import java.util.Vector;
27  
28  import org.apache.commons.net.MalformedServerReplyException;
29  import org.apache.commons.net.io.DotTerminatedMessageReader;
30  import org.apache.commons.net.io.DotTerminatedMessageWriter;
31  import org.apache.commons.net.io.Util;
32  
33  /***
34   * NNTPClient encapsulates all the functionality necessary to post and
35   * retrieve articles from an NNTP server.  As with all classes derived
36   * from {@link org.apache.commons.net.SocketClient},
37   * you must first connect to the server with
38   * {@link org.apache.commons.net.SocketClient#connect  connect }
39   * before doing anything, and finally
40   * {@link org.apache.commons.net.nntp.NNTP#disconnect  disconnect() }
41   * after you're completely finished interacting with the server.
42   * Remember that the
43   * {@link org.apache.commons.net.nntp.NNTP#isAllowedToPost isAllowedToPost()}
44   *  method is defined in
45   * {@link org.apache.commons.net.nntp.NNTP}.
46   * <p>
47   * You should keep in mind that the NNTP server may choose to prematurely
48   * close a connection if the client has been idle for longer than a
49   * given time period or if the server is being shutdown by the operator or
50   * some other reason.  The NNTP class will detect a
51   * premature NNTP server connection closing when it receives a
52   * {@link org.apache.commons.net.nntp.NNTPReply#SERVICE_DISCONTINUED NNTPReply.SERVICE_DISCONTINUED }
53   *  response to a command.
54   * When that occurs, the NNTP class method encountering that reply will throw
55   * an {@link org.apache.commons.net.nntp.NNTPConnectionClosedException}
56   * .
57   * <code>NNTPConectionClosedException</code>
58   * is a subclass of <code> IOException </code> and therefore need not be
59   * caught separately, but if you are going to catch it separately, its
60   * catch block must appear before the more general <code> IOException </code>
61   * catch block.  When you encounter an
62   * {@link org.apache.commons.net.nntp.NNTPConnectionClosedException}
63   * , you must disconnect the connection with
64   * {@link org.apache.commons.net.nntp.NNTP#disconnect  disconnect() }
65   *  to properly clean up the
66   * system resources used by NNTP.  Before disconnecting, you may check the
67   * last reply code and text with
68   * {@link org.apache.commons.net.nntp.NNTP#getReplyCode  getReplyCode } and
69   * {@link org.apache.commons.net.nntp.NNTP#getReplyString  getReplyString }.
70   * <p>
71   * Rather than list it separately for each method, we mention here that
72   * every method communicating with the server and throwing an IOException
73   * can also throw a
74   * {@link org.apache.commons.net.MalformedServerReplyException}
75   * , which is a subclass
76   * of IOException.  A MalformedServerReplyException will be thrown when
77   * the reply received from the server deviates enough from the protocol
78   * specification that it cannot be interpreted in a useful manner despite
79   * attempts to be as lenient as possible.
80   *
81   * @see NNTP
82   * @see NNTPConnectionClosedException
83   * @see org.apache.commons.net.MalformedServerReplyException
84   ***/
85  
86  public class NNTPClient extends NNTP
87  {
88  
89      /**
90       * Parse the reply and store the id and number in the pointer.
91       *
92       * @param reply the reply to parse "22n nnn <aaa>"
93       * @param pointer the pointer to update
94       *
95       * @throws MalformedServerReplyException if response could not be parsed
96       */
97      private void __parseArticlePointer(String reply, ArticleInfo pointer)
98      throws MalformedServerReplyException
99      {
100         String tokens[] = reply.split(" ");
101         if (tokens.length >= 3) { // OK, we can parset the line
102             int i = 1; // skip reply code
103             try
104             {
105                 // Get article number
106                 pointer.articleNumber = Long.parseLong(tokens[i++]);
107                 // Get article id
108                 pointer.articleId = tokens[i++];
109                 return; // done
110             }
111             catch (NumberFormatException e)
112             {
113                 // drop through and raise exception
114             }
115         }
116         throw new MalformedServerReplyException(
117             "Could not parse article pointer.\nServer reply: " + reply);
118     }
119 
120     /*
121      * 211 n f l s group selected
122      *     (n = estimated number of articles in group,
123      *     f = first article number in the group,
124      *     l = last article number in the group,
125      *     s = name of the group.)
126      */
127 
128     private static void __parseGroupReply(String reply, NewsgroupInfo info)
129     throws MalformedServerReplyException
130     {
131         String tokens[] = reply.split(" ");
132         if (tokens.length >= 5) {
133             int i = 1;  // Skip numeric response value
134             try
135             {
136                 // Get estimated article count
137                 info._setArticleCount(Long.parseLong(tokens[i++]));
138                 // Get first article number
139                 info._setFirstArticle(Long.parseLong(tokens[i++]));
140                 // Get last article number
141                 info._setLastArticle(Long.parseLong(tokens[i++]));
142                 // Get newsgroup name
143                 info._setNewsgroup(tokens[i++]);
144 
145                 info._setPostingPermission(NewsgroupInfo.UNKNOWN_POSTING_PERMISSION);
146                 return ;
147             } catch (NumberFormatException e)
148             {
149                // drop through to report error
150             }
151         }
152 
153         throw new MalformedServerReplyException(
154             "Could not parse newsgroup info.\nServer reply: " + reply);
155     }
156 
157 
158     // Format: group last first p
159     static NewsgroupInfo __parseNewsgroupListEntry(String entry)
160     {
161         String tokens[] = entry.split(" ");
162         if (tokens.length < 4) {
163             return null;
164         }
165         NewsgroupInfo result = new NewsgroupInfo();
166 
167         int i = 0;
168 
169         result._setNewsgroup(tokens[i++]);
170 
171         try
172         {
173             long lastNum = Long.parseLong(tokens[i++]);
174             long firstNum = Long.parseLong(tokens[i++]);
175             result._setFirstArticle(firstNum);
176             result._setLastArticle(lastNum);
177             if ((firstNum == 0) && (lastNum == 0)) {
178                 result._setArticleCount(0);
179             } else {
180                 result._setArticleCount(lastNum - firstNum + 1);
181             }
182         } catch (NumberFormatException e) {
183             return null;
184         }
185 
186         switch (tokens[i++].charAt(0))
187         {
188         case 'y':
189         case 'Y':
190             result._setPostingPermission(
191                 NewsgroupInfo.PERMITTED_POSTING_PERMISSION);
192             break;
193         case 'n':
194         case 'N':
195             result._setPostingPermission(
196                 NewsgroupInfo.PROHIBITED_POSTING_PERMISSION);
197             break;
198         case 'm':
199         case 'M':
200             result._setPostingPermission(
201                 NewsgroupInfo.MODERATED_POSTING_PERMISSION);
202             break;
203         default:
204             result._setPostingPermission(
205                 NewsgroupInfo.UNKNOWN_POSTING_PERMISSION);
206             break;
207         }
208 
209         return result;
210     }
211 
212     /**
213      * Parse a response line from {@link #retrieveArticleInfo(long, long)}.
214      *
215      * @param line a response line
216      * @return the parsed {@link Article}, if unparseable then isDummy()
217      * will be true, and the subject will contain the raw info.
218      * @since 3.0
219      */
220     static Article __parseArticleEntry(String line) {
221         // Extract the article information
222         // Mandatory format (from NNTP RFC 2980) is :
223         // articleNumber\tSubject\tAuthor\tDate\tID\tReference(s)\tByte Count\tLine Count
224 
225         Article article = new Article();
226         article.setSubject(line); // in case parsing fails
227         String parts[] = line.split("\t");
228         if (parts.length > 6) {
229             int i = 0;
230             try {
231                 article.setArticleNumber(Long.parseLong(parts[i++]));
232                 article.setSubject(parts[i++]);
233                 article.setFrom(parts[i++]);
234                 article.setDate(parts[i++]);
235                 article.setArticleId(parts[i++]);
236                 article.addReference(parts[i++]);
237             } catch (NumberFormatException e) {
238                 // ignored, already handled
239             }
240         }
241         return article;
242     }
243 
244     private NewsgroupInfo[] __readNewsgroupListing() throws IOException
245     {
246 
247         BufferedReader reader = new DotTerminatedMessageReader(_reader_);
248         // Start of with a big vector because we may be reading a very large
249         // amount of groups.
250         Vector<NewsgroupInfo> list = new Vector<NewsgroupInfo>(2048);
251 
252         String line;
253         try {
254             while ((line = reader.readLine()) != null) {
255                 NewsgroupInfo tmp = __parseNewsgroupListEntry(line);
256                 if (tmp != null) {
257                     list.addElement(tmp);
258                 } else {
259                     throw new MalformedServerReplyException(line);
260                 }
261             }
262         } finally {
263             reader.close();
264         }
265         int size;
266         if ((size = list.size()) < 1) {
267             return new NewsgroupInfo[0];
268         }
269 
270         NewsgroupInfo[] info = new NewsgroupInfo[size];
271         list.copyInto(info);
272 
273         return info;
274     }
275 
276 
277     private BufferedReader __retrieve(int command, String articleId, ArticleInfo pointer)
278     throws IOException
279     {
280         if (articleId != null)
281         {
282             if (!NNTPReply.isPositiveCompletion(sendCommand(command, articleId))) {
283                 return null;
284             }
285         }
286         else
287         {
288             if (!NNTPReply.isPositiveCompletion(sendCommand(command))) {
289                 return null;
290             }
291         }
292 
293 
294         if (pointer != null) {
295             __parseArticlePointer(getReplyString(), pointer);
296         }
297 
298         return new DotTerminatedMessageReader(_reader_);
299     }
300 
301 
302     private BufferedReader __retrieve(int command, long articleNumber, ArticleInfo pointer)
303     throws IOException
304     {
305         if (!NNTPReply.isPositiveCompletion(sendCommand(command,
306                                             Long.toString(articleNumber)))) {
307             return null;
308         }
309 
310         if (pointer != null) {
311             __parseArticlePointer(getReplyString(), pointer);
312         }
313 
314         return new DotTerminatedMessageReader(_reader_);
315     }
316 
317 
318 
319     /***
320      * Retrieves an article from the NNTP server.  The article is referenced
321      * by its unique article identifier (including the enclosing &lt; and &gt;).
322      * The article number and identifier contained in the server reply
323      * are returned through an ArticleInfo.  The <code> articleId </code>
324      * field of the ArticleInfo cannot always be trusted because some
325      * NNTP servers do not correctly follow the RFC 977 reply format.
326      * <p>
327      * A DotTerminatedMessageReader is returned from which the article can
328      * be read.  If the article does not exist, null is returned.
329      * <p>
330      * You must not issue any commands to the NNTP server (i.e., call any
331      * other methods) until you finish reading the message from the returned
332      * BufferedReader instance.
333      * The NNTP protocol uses the same stream for issuing commands as it does
334      * for returning results.  Therefore the returned BufferedReader actually reads
335      * directly from the NNTP connection.  After the end of message has been
336      * reached, new commands can be executed and their replies read.  If
337      * you do not follow these requirements, your program will not work
338      * properly.
339      * <p>
340      * @param articleId  The unique article identifier of the article to
341      *     retrieve.  If this parameter is null, the currently selected
342      *     article is retrieved.
343      * @param pointer    A parameter through which to return the article's
344      *   number and unique id.  The articleId field cannot always be trusted
345      *   because of server deviations from RFC 977 reply formats.  You may
346      *   set this parameter to null if you do not desire to retrieve the
347      *   returned article information.
348      * @return A DotTerminatedMessageReader instance from which the article
349      *         can be read.  null if the article does not exist.
350      * @exception NNTPConnectionClosedException
351      *      If the NNTP server prematurely closes the connection as a result
352      *      of the client being idle or some other reason causing the server
353      *      to send NNTP reply code 400.  This exception may be caught either
354      *      as an IOException or independently as itself.
355      * @exception IOException  If an I/O error occurs while either sending a
356      *      command to the server or receiving a reply from the server.
357      ***/
358     public BufferedReader retrieveArticle(String articleId, ArticleInfo pointer)
359     throws IOException
360     {
361         return __retrieve(NNTPCommand.ARTICLE, articleId, pointer);
362 
363     }
364 
365     /**
366      * Same as <code> retrieveArticle(articleId, (ArticleInfo) null) </code>
367      * Note: the return can be cast to a {@link BufferedReader}
368      * @param articleId the article id to retrieve
369      * @return A DotTerminatedMessageReader instance from which the article can be read.
370      * null if the article does not exist.
371      * @throws IOException if an IO error occurs
372      */
373     public Reader retrieveArticle(String articleId) throws IOException
374     {
375         return retrieveArticle(articleId, (ArticleInfo) null);
376     }
377 
378     /**
379      * Same as <code> retrieveArticle((String) null) </code>
380      * Note: the return can be cast to a {@link BufferedReader}
381      * @return A DotTerminatedMessageReader instance from which the article can be read.
382      * null if the article does not exist.
383      * @throws IOException if an IO error occurs
384      */
385     public Reader retrieveArticle() throws IOException
386     {
387         return retrieveArticle((String) null);
388     }
389 
390 
391     /***
392      * Retrieves an article from the currently selected newsgroup.  The
393      * article is referenced by its article number.
394      * The article number and identifier contained in the server reply
395      * are returned through an ArticleInfo.  The <code> articleId </code>
396      * field of the ArticleInfo cannot always be trusted because some
397      * NNTP servers do not correctly follow the RFC 977 reply format.
398      * <p>
399      * A DotTerminatedMessageReader is returned from which the article can
400      * be read.  If the article does not exist, null is returned.
401      * <p>
402      * You must not issue any commands to the NNTP server (i.e., call any
403      * other methods) until you finish reading the message from the returned
404      * BufferedReader instance.
405      * The NNTP protocol uses the same stream for issuing commands as it does
406      * for returning results.  Therefore the returned BufferedReader actually reads
407      * directly from the NNTP connection.  After the end of message has been
408      * reached, new commands can be executed and their replies read.  If
409      * you do not follow these requirements, your program will not work
410      * properly.
411      * <p>
412      * @param articleNumber  The number of the the article to
413      *     retrieve.
414      * @param pointer    A parameter through which to return the article's
415      *   number and unique id.  The articleId field cannot always be trusted
416      *   because of server deviations from RFC 977 reply formats.  You may
417      *   set this parameter to null if you do not desire to retrieve the
418      *   returned article information.
419      * @return A DotTerminatedMessageReader instance from which the article
420      *         can be read.  null if the article does not exist.
421      * @exception NNTPConnectionClosedException
422      *      If the NNTP server prematurely closes the connection as a result
423      *      of the client being idle or some other reason causing the server
424      *      to send NNTP reply code 400.  This exception may be caught either
425      *      as an IOException or independently as itself.
426      * @exception IOException  If an I/O error occurs while either sending a
427      *      command to the server or receiving a reply from the server.
428      ***/
429     public BufferedReader retrieveArticle(long articleNumber, ArticleInfo pointer)
430     throws IOException
431     {
432         return __retrieve(NNTPCommand.ARTICLE, articleNumber, pointer);
433     }
434 
435     /**
436      * Same as <code> retrieveArticle(articleNumber, null) </code>
437      * @param articleNumber the article number to fetch
438      * @return A DotTerminatedMessageReader instance from which the article
439      *         can be read.  null if the article does not exist.
440      * @throws IOException if an IO error occurs
441      */
442     public BufferedReader retrieveArticle(long articleNumber) throws IOException
443     {
444         return retrieveArticle(articleNumber, null);
445     }
446 
447 
448 
449     /***
450      * Retrieves an article header from the NNTP server.  The article is
451      * referenced
452      * by its unique article identifier (including the enclosing &lt; and &gt;).
453      * The article number and identifier contained in the server reply
454      * are returned through an ArticleInfo.  The <code> articleId </code>
455      * field of the ArticleInfo cannot always be trusted because some
456      * NNTP servers do not correctly follow the RFC 977 reply format.
457      * <p>
458      * A DotTerminatedMessageReader is returned from which the article can
459      * be read.  If the article does not exist, null is returned.
460      * <p>
461      * You must not issue any commands to the NNTP server (i.e., call any
462      * other methods) until you finish reading the message from the returned
463      * BufferedReader instance.
464      * The NNTP protocol uses the same stream for issuing commands as it does
465      * for returning results.  Therefore the returned BufferedReader actually reads
466      * directly from the NNTP connection.  After the end of message has been
467      * reached, new commands can be executed and their replies read.  If
468      * you do not follow these requirements, your program will not work
469      * properly.
470      * <p>
471      * @param articleId  The unique article identifier of the article whose
472      *    header is being retrieved.  If this parameter is null, the
473      *    header of the currently selected article is retrieved.
474      * @param pointer    A parameter through which to return the article's
475      *   number and unique id.  The articleId field cannot always be trusted
476      *   because of server deviations from RFC 977 reply formats.  You may
477      *   set this parameter to null if you do not desire to retrieve the
478      *   returned article information.
479      * @return A DotTerminatedMessageReader instance from which the article
480      *         header can be read.  null if the article does not exist.
481      * @exception NNTPConnectionClosedException
482      *      If the NNTP server prematurely closes the connection as a result
483      *      of the client being idle or some other reason causing the server
484      *      to send NNTP reply code 400.  This exception may be caught either
485      *      as an IOException or independently as itself.
486      * @exception IOException  If an I/O error occurs while either sending a
487      *      command to the server or receiving a reply from the server.
488      ***/
489     public BufferedReader retrieveArticleHeader(String articleId, ArticleInfo pointer)
490     throws IOException
491     {
492         return __retrieve(NNTPCommand.HEAD, articleId, pointer);
493 
494     }
495 
496     /**
497      * Same as <code> retrieveArticleHeader(articleId, (ArticleInfo) null) </code>
498      *  Note: the return can be cast to a {@link BufferedReader}
499      * @param articleId the article id  to fetch
500      * @return the reader
501      * @throws IOException if an error occurs
502      */
503     public Reader retrieveArticleHeader(String articleId) throws IOException
504     {
505         return retrieveArticleHeader(articleId, (ArticleInfo) null);
506     }
507 
508     /**
509      * Same as <code> retrieveArticleHeader((String) null) </code>
510      *  Note: the return can be cast to a {@link BufferedReader}
511      * @return the reader
512      * @throws IOException if an error occurs
513      */
514     public Reader retrieveArticleHeader() throws IOException
515     {
516         return retrieveArticleHeader((String) null);
517     }
518 
519 
520     /***
521      * Retrieves an article header from the currently selected newsgroup.  The
522      * article is referenced by its article number.
523      * The article number and identifier contained in the server reply
524      * are returned through an ArticleInfo.  The <code> articleId </code>
525      * field of the ArticleInfo cannot always be trusted because some
526      * NNTP servers do not correctly follow the RFC 977 reply format.
527      * <p>
528      * A DotTerminatedMessageReader is returned from which the article can
529      * be read.  If the article does not exist, null is returned.
530      * <p>
531      * You must not issue any commands to the NNTP server (i.e., call any
532      * other methods) until you finish reading the message from the returned
533      * BufferedReader instance.
534      * The NNTP protocol uses the same stream for issuing commands as it does
535      * for returning results.  Therefore the returned BufferedReader actually reads
536      * directly from the NNTP connection.  After the end of message has been
537      * reached, new commands can be executed and their replies read.  If
538      * you do not follow these requirements, your program will not work
539      * properly.
540      * <p>
541      * @param articleNumber  The number of the the article whose header is
542      *     being retrieved.
543      * @param pointer    A parameter through which to return the article's
544      *   number and unique id.  The articleId field cannot always be trusted
545      *   because of server deviations from RFC 977 reply formats.  You may
546      *   set this parameter to null if you do not desire to retrieve the
547      *   returned article information.
548      * @return A DotTerminatedMessageReader instance from which the article
549      *         header can be read.  null if the article does not exist.
550      * @exception NNTPConnectionClosedException
551      *      If the NNTP server prematurely closes the connection as a result
552      *      of the client being idle or some other reason causing the server
553      *      to send NNTP reply code 400.  This exception may be caught either
554      *      as an IOException or independently as itself.
555      * @exception IOException  If an I/O error occurs while either sending a
556      *      command to the server or receiving a reply from the server.
557      ***/
558     public BufferedReader retrieveArticleHeader(long articleNumber,
559                                         ArticleInfo pointer)
560     throws IOException
561     {
562         return __retrieve(NNTPCommand.HEAD, articleNumber, pointer);
563     }
564 
565 
566     /**
567      * Same as <code> retrieveArticleHeader(articleNumber, null) </code>
568      *
569      * @param articleNumber the article number
570      * @return the reader
571      * @throws IOException if an error occurs
572      */
573     public BufferedReader retrieveArticleHeader(long articleNumber) throws IOException
574     {
575         return retrieveArticleHeader(articleNumber, null);
576     }
577 
578 
579 
580     /***
581      * Retrieves an article body from the NNTP server.  The article is
582      * referenced
583      * by its unique article identifier (including the enclosing &lt; and &gt;).
584      * The article number and identifier contained in the server reply
585      * are returned through an ArticleInfo.  The <code> articleId </code>
586      * field of the ArticleInfo cannot always be trusted because some
587      * NNTP servers do not correctly follow the RFC 977 reply format.
588      * <p>
589      * A DotTerminatedMessageReader is returned from which the article can
590      * be read.  If the article does not exist, null is returned.
591      * <p>
592      * You must not issue any commands to the NNTP server (i.e., call any
593      * other methods) until you finish reading the message from the returned
594      * BufferedReader instance.
595      * The NNTP protocol uses the same stream for issuing commands as it does
596      * for returning results.  Therefore the returned BufferedReader actually reads
597      * directly from the NNTP connection.  After the end of message has been
598      * reached, new commands can be executed and their replies read.  If
599      * you do not follow these requirements, your program will not work
600      * properly.
601      * <p>
602      * @param articleId  The unique article identifier of the article whose
603      *    body is being retrieved.  If this parameter is null, the
604      *    body of the currently selected article is retrieved.
605      * @param pointer    A parameter through which to return the article's
606      *   number and unique id.  The articleId field cannot always be trusted
607      *   because of server deviations from RFC 977 reply formats.  You may
608      *   set this parameter to null if you do not desire to retrieve the
609      *   returned article information.
610      * @return A DotTerminatedMessageReader instance from which the article
611      *         body can be read.  null if the article does not exist.
612      * @exception NNTPConnectionClosedException
613      *      If the NNTP server prematurely closes the connection as a result
614      *      of the client being idle or some other reason causing the server
615      *      to send NNTP reply code 400.  This exception may be caught either
616      *      as an IOException or independently as itself.
617      * @exception IOException  If an I/O error occurs while either sending a
618      *      command to the server or receiving a reply from the server.
619      ***/
620     public BufferedReader retrieveArticleBody(String articleId, ArticleInfo pointer)
621     throws IOException
622     {
623         return __retrieve(NNTPCommand.BODY, articleId, pointer);
624 
625     }
626 
627     /**
628      * Same as <code> retrieveArticleBody(articleId, (ArticleInfo) null) </code>
629      *  Note: the return can be cast to a {@link BufferedReader}
630      * @param articleId the article id
631      * @return A DotTerminatedMessageReader instance from which the article
632      *         body can be read.  null if the article does not exist.
633      * @throws IOException if an error occurs
634      */
635     public Reader retrieveArticleBody(String articleId) throws IOException
636     {
637         return retrieveArticleBody(articleId, (ArticleInfo) null);
638     }
639 
640     /**
641      * Same as <code> retrieveArticleBody(null) </code>
642      *  Note: the return can be cast to a {@link BufferedReader}
643      * @return A DotTerminatedMessageReader instance from which the article
644      *         body can be read.  null if the article does not exist.
645      * @throws IOException if an error occurs
646      */
647     public Reader retrieveArticleBody() throws IOException
648     {
649         return retrieveArticleBody(null);
650     }
651 
652 
653     /***
654      * Retrieves an article body from the currently selected newsgroup.  The
655      * article is referenced by its article number.
656      * The article number and identifier contained in the server reply
657      * are returned through an ArticleInfo.  The <code> articleId </code>
658      * field of the ArticleInfo cannot always be trusted because some
659      * NNTP servers do not correctly follow the RFC 977 reply format.
660      * <p>
661      * A DotTerminatedMessageReader is returned from which the article can
662      * be read.  If the article does not exist, null is returned.
663      * <p>
664      * You must not issue any commands to the NNTP server (i.e., call any
665      * other methods) until you finish reading the message from the returned
666      * BufferedReader instance.
667      * The NNTP protocol uses the same stream for issuing commands as it does
668      * for returning results.  Therefore the returned BufferedReader actually reads
669      * directly from the NNTP connection.  After the end of message has been
670      * reached, new commands can be executed and their replies read.  If
671      * you do not follow these requirements, your program will not work
672      * properly.
673      * <p>
674      * @param articleNumber  The number of the the article whose body is
675      *     being retrieved.
676      * @param pointer    A parameter through which to return the article's
677      *   number and unique id.  The articleId field cannot always be trusted
678      *   because of server deviations from RFC 977 reply formats.  You may
679      *   set this parameter to null if you do not desire to retrieve the
680      *   returned article information.
681      * @return A DotTerminatedMessageReader instance from which the article
682      *         body can be read.  null if the article does not exist.
683      * @exception 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      * @exception IOException  If an I/O error occurs while either sending a
689      *      command to the server or receiving a reply from the server.
690      ***/
691     public BufferedReader retrieveArticleBody(long articleNumber,
692                                       ArticleInfo pointer)
693     throws IOException
694     {
695         return __retrieve(NNTPCommand.BODY, articleNumber, pointer);
696     }
697 
698 
699     /**
700      * Same as <code> retrieveArticleBody(articleNumber, null) </code>
701      * @param articleNumber the article number
702      * @return the reader
703      * @throws IOException if an error occurs
704      */
705     public BufferedReader retrieveArticleBody(long articleNumber) throws IOException
706     {
707         return retrieveArticleBody(articleNumber, null);
708     }
709 
710 
711     /***
712      * Select the specified newsgroup to be the target of for future article
713      * retrieval and posting operations.  Also return the newsgroup
714      * information contained in the server reply through the info parameter.
715      * <p>
716      * @param newsgroup  The newsgroup to select.
717      * @param info  A parameter through which the newsgroup information of
718      *      the selected newsgroup contained in the server reply is returned.
719      *      Set this to null if you do not desire this information.
720      * @return True if the newsgroup exists and was selected, false otherwise.
721      * @exception NNTPConnectionClosedException
722      *      If the NNTP server prematurely closes the connection as a result
723      *      of the client being idle or some other reason causing the server
724      *      to send NNTP reply code 400.  This exception may be caught either
725      *      as an IOException or independently as itself.
726      * @exception IOException  If an I/O error occurs while either sending a
727      *      command to the server or receiving a reply from the server.
728      ***/
729     public boolean selectNewsgroup(String newsgroup, NewsgroupInfo info)
730     throws IOException
731     {
732         if (!NNTPReply.isPositiveCompletion(group(newsgroup))) {
733             return false;
734         }
735 
736         if (info != null) {
737             __parseGroupReply(getReplyString(), info);
738         }
739 
740         return true;
741     }
742 
743     /**
744      * Same as <code> selectNewsgroup(newsgroup, null) </code>
745      * @param newsgroup the newsgroup name
746      * @return true if newsgroup exist and was selected
747      * @throws IOException if an error occurs
748      */
749     public boolean selectNewsgroup(String newsgroup) throws IOException
750     {
751         return selectNewsgroup(newsgroup, null);
752     }
753 
754     /***
755      * List the command help from the server.
756      * <p>
757      * @return The sever help information.
758      * @exception NNTPConnectionClosedException
759      *      If the NNTP server prematurely closes the connection as a result
760      *      of the client being idle or some other reason causing the server
761      *      to send NNTP reply code 400.  This exception may be caught either
762      *      as an IOException or independently as itself.
763      * @exception IOException  If an I/O error occurs while either sending a
764      *      command to the server or receiving a reply from the server.
765      ***/
766     public String listHelp() throws IOException
767     {
768         if (!NNTPReply.isInformational(help())) {
769             return null;
770         }
771 
772         StringWriter help = new StringWriter();
773         BufferedReader reader = new DotTerminatedMessageReader(_reader_);
774         Util.copyReader(reader, help);
775         reader.close();
776         help.close();
777         return help.toString();
778     }
779 
780     /**
781      * Send a "LIST OVERVIEW.FMT" command to the server.
782      *
783      * @return the contents of the Overview format, of {@code null} if the command failed
784      * @throws IOException on error
785      */
786     public String[] listOverviewFmt() throws IOException
787     {
788         if (!NNTPReply.isPositiveCompletion(sendCommand("LIST", "OVERVIEW.FMT"))){
789             return null;
790         }
791 
792         BufferedReader reader = new DotTerminatedMessageReader(_reader_);
793         String line;
794         ArrayList<String> list = new ArrayList<String>();
795         while((line=reader.readLine()) != null) {
796             list.add(line);
797         }
798         reader.close();
799         return list.toArray(new String[list.size()]);
800     }
801 
802     /***
803      * Select an article by its unique identifier (including enclosing
804      * &lt; and &gt;) and return its article number and id through the
805      * pointer parameter.  This is achieved through the STAT command.
806      * According to RFC 977, this will NOT set the current article pointer
807      * on the server.  To do that, you must reference the article by its
808      * number.
809      * <p>
810      * @param articleId  The unique article identifier of the article that
811      *    is being selectedd.  If this parameter is null, the
812      *    body of the current article is selected
813      * @param pointer    A parameter through which to return the article's
814      *   number and unique id.  The articleId field cannot always be trusted
815      *   because of server deviations from RFC 977 reply formats.  You may
816      *   set this parameter to null if you do not desire to retrieve the
817      *   returned article information.
818      * @return True if successful, false if not.
819      * @exception NNTPConnectionClosedException
820      *      If the NNTP server prematurely closes the connection as a result
821      *      of the client being idle or some other reason causing the server
822      *      to send NNTP reply code 400.  This exception may be caught either
823      *      as an IOException or independently as itself.
824      * @exception IOException  If an I/O error occurs while either sending a
825      *      command to the server or receiving a reply from the server.
826      ***/
827     public boolean selectArticle(String articleId, ArticleInfo pointer)
828     throws IOException
829     {
830         if (articleId != null) {
831             if (!NNTPReply.isPositiveCompletion(stat(articleId))) {
832                 return false;
833             }
834         } else {
835             if (!NNTPReply.isPositiveCompletion(stat())) {
836                 return false;
837             }
838         }
839 
840         if (pointer != null) {
841             __parseArticlePointer(getReplyString(), pointer);
842         }
843 
844         return true;
845     }
846 
847     /**
848      * Same as <code> selectArticle(articleId, (ArticleInfo) null) </code>
849      * @param articleId the article Id
850      * @return true if successful
851      * @throws IOException on error
852      */
853     public boolean selectArticle(String articleId) throws IOException
854     {
855         return selectArticle(articleId, (ArticleInfo) null);
856     }
857 
858     /****
859      * Same as <code> selectArticle((String) null, articleId) </code>.  Useful
860      * for retrieving the current article number.
861      * @param pointer to the article
862      * @return true if OK
863      * @throws IOException on error
864      ***/
865     public boolean selectArticle(ArticleInfo pointer) throws IOException
866     {
867         return selectArticle(null, pointer);
868     }
869 
870 
871     /***
872      * Select an article in the currently selected newsgroup by its number.
873      * and return its article number and id through the
874      * pointer parameter.  This is achieved through the STAT command.
875      * According to RFC 977, this WILL set the current article pointer
876      * on the server.  Use this command to select an article before retrieving
877      * it, or to obtain an article's unique identifier given its number.
878      * <p>
879      * @param articleNumber The number of the article to select from the
880      *       currently selected newsgroup.
881      * @param pointer    A parameter through which to return the article's
882      *   number and unique id.  Although the articleId field cannot always
883      *   be trusted because of server deviations from RFC 977 reply formats,
884      *   we haven't found a server that misformats this information in response
885      *   to this particular command.  You may set this parameter to null if
886      *   you do not desire to retrieve the returned article information.
887      * @return True if successful, false if not.
888      * @exception NNTPConnectionClosedException
889      *      If the NNTP server prematurely closes the connection as a result
890      *      of the client being idle or some other reason causing the server
891      *      to send NNTP reply code 400.  This exception may be caught either
892      *      as an IOException or independently as itself.
893      * @exception IOException  If an I/O error occurs while either sending a
894      *      command to the server or receiving a reply from the server.
895      ***/
896     public boolean selectArticle(long articleNumber, ArticleInfo pointer)
897     throws IOException
898     {
899         if (!NNTPReply.isPositiveCompletion(stat(articleNumber))) {
900             return false;
901         }
902 
903         if (pointer != null) {
904             __parseArticlePointer(getReplyString(), pointer);
905         }
906 
907         return true;
908     }
909 
910 
911     /*** Same as <code> selectArticle(articleNumber, null) </code>
912      * @param articleNumber the numger
913      * @return true if successful
914      * @throws IOException on error ***/
915     public boolean selectArticle(long articleNumber) throws IOException
916     {
917         return selectArticle(articleNumber, null);
918     }
919 
920 
921     /***
922      * Select the article preceeding the currently selected article in the
923      * currently selected newsgroup and return its number and unique id
924      * through the pointer parameter.  Because of deviating server
925      * implementations, the articleId information cannot be trusted.  To
926      * obtain the article identifier, issue a
927      * <code> selectArticle(pointer.articleNumber, pointer) </code> immediately
928      * afterward.
929      * <p>
930      * @param pointer    A parameter through which to return the article's
931      *   number and unique id.  The articleId field cannot always be trusted
932      *   because of server deviations from RFC 977 reply formats.  You may
933      *   set this parameter to null if you do not desire to retrieve the
934      *   returned article information.
935      * @return True if successful, false if not (e.g., there is no previous
936      *     article).
937      * @exception NNTPConnectionClosedException
938      *      If the NNTP server prematurely closes the connection as a result
939      *      of the client being idle or some other reason causing the server
940      *      to send NNTP reply code 400.  This exception may be caught either
941      *      as an IOException or independently as itself.
942      * @exception IOException  If an I/O error occurs while either sending a
943      *      command to the server or receiving a reply from the server.
944      ***/
945     public boolean selectPreviousArticle(ArticleInfo pointer)
946     throws IOException
947     {
948         if (!NNTPReply.isPositiveCompletion(last())) {
949             return false;
950         }
951 
952         if (pointer != null) {
953             __parseArticlePointer(getReplyString(), pointer);
954         }
955 
956         return true;
957     }
958 
959     /*** Same as <code> selectPreviousArticle((ArticleInfo) null) </code>
960      * @return true if successful
961      * @throws IOException on error ***/
962     public boolean selectPreviousArticle() throws IOException
963     {
964         return selectPreviousArticle((ArticleInfo) null);
965     }
966 
967 
968     /***
969      * Select the article following the currently selected article in the
970      * currently selected newsgroup and return its number and unique id
971      * through the pointer parameter.  Because of deviating server
972      * implementations, the articleId information cannot be trusted.  To
973      * obtain the article identifier, issue a
974      * <code> selectArticle(pointer.articleNumber, pointer) </code> immediately
975      * afterward.
976      * <p>
977      * @param pointer    A parameter through which to return the article's
978      *   number and unique id.  The articleId field cannot always be trusted
979      *   because of server deviations from RFC 977 reply formats.  You may
980      *   set this parameter to null if you do not desire to retrieve the
981      *   returned article information.
982      * @return True if successful, false if not (e.g., there is no following
983      *         article).
984      * @exception NNTPConnectionClosedException
985      *      If the NNTP server prematurely closes the connection as a result
986      *      of the client being idle or some other reason causing the server
987      *      to send NNTP reply code 400.  This exception may be caught either
988      *      as an IOException or independently as itself.
989      * @exception IOException  If an I/O error occurs while either sending a
990      *      command to the server or receiving a reply from the server.
991      ***/
992     public boolean selectNextArticle(ArticleInfo pointer) throws IOException
993     {
994         if (!NNTPReply.isPositiveCompletion(next())) {
995             return false;
996         }
997 
998         if (pointer != null) {
999             __parseArticlePointer(getReplyString(), pointer);
1000         }
1001 
1002         return true;
1003     }
1004 
1005 
1006     /*** Same as <code> selectNextArticle((ArticleInfo) null) </code>
1007      * @return true if successful
1008      * @throws IOException on error ***/
1009     public boolean selectNextArticle() throws IOException
1010     {
1011         return selectNextArticle((ArticleInfo) null);
1012     }
1013 
1014 
1015     /***
1016      * List all newsgroups served by the NNTP server.  If no newsgroups
1017      * are served, a zero length array will be returned.  If the command
1018      * fails, null will be returned.
1019      * The method uses the "LIST" command.
1020      * <p>
1021      * @return An array of NewsgroupInfo instances containing the information
1022      *    for each newsgroup served by the NNTP server.   If no newsgroups
1023      *    are served, a zero length array will be returned.  If the command
1024      *    fails, null will be returned.
1025      * @exception NNTPConnectionClosedException
1026      *      If the NNTP server prematurely closes the connection as a result
1027      *      of the client being idle or some other reason causing the server
1028      *      to send NNTP reply code 400.  This exception may be caught either
1029      *      as an IOException or independently as itself.
1030      * @exception IOException  If an I/O error occurs while either sending a
1031      *      command to the server or receiving a reply from the server.
1032      * @see #iterateNewsgroupListing()
1033      * @see #iterateNewsgroups()
1034      ***/
1035     public NewsgroupInfo[] listNewsgroups() throws IOException
1036     {
1037         if (!NNTPReply.isPositiveCompletion(list())) {
1038             return null;
1039         }
1040 
1041         return __readNewsgroupListing();
1042     }
1043 
1044     /**
1045      * List all newsgroups served by the NNTP server.  If no newsgroups
1046      * are served, no entries will be returned.
1047      * The method uses the "LIST" command.
1048      * <p>
1049      * @return An iterable of NewsgroupInfo instances containing the information
1050      *    for each newsgroup served by the NNTP server.   If no newsgroups
1051      *    are served, no entries will be returned.
1052      * @exception NNTPConnectionClosedException
1053      *      If the NNTP server prematurely closes the connection as a result
1054      *      of the client being idle or some other reason causing the server
1055      *      to send NNTP reply code 400.  This exception may be caught either
1056      *      as an IOException or independently as itself.
1057      * @exception IOException  If an I/O error occurs while either sending a
1058      *      command to the server or receiving a reply from the server.
1059      * @since 3.0
1060      */
1061     public Iterable<String> iterateNewsgroupListing() throws IOException {
1062         if (NNTPReply.isPositiveCompletion(list())) {
1063             return new ReplyIterator(_reader_);
1064         }
1065         throw new IOException("LIST command failed: "+getReplyString());
1066     }
1067 
1068     /**
1069      * List all newsgroups served by the NNTP server.  If no newsgroups
1070      * are served, no entries will be returned.
1071      * The method uses the "LIST" command.
1072      * <p>
1073      * @return An iterable of Strings containing the raw information
1074      *    for each newsgroup served by the NNTP server.   If no newsgroups
1075      *    are served, no entries will be returned.
1076      * @exception NNTPConnectionClosedException
1077      *      If the NNTP server prematurely closes the connection as a result
1078      *      of the client being idle or some other reason causing the server
1079      *      to send NNTP reply code 400.  This exception may be caught either
1080      *      as an IOException or independently as itself.
1081      * @exception IOException  If an I/O error occurs while either sending a
1082      *      command to the server or receiving a reply from the server.
1083      * @since 3.0
1084      */
1085     public Iterable<NewsgroupInfo> iterateNewsgroups() throws IOException {
1086         return new NewsgroupIterator(iterateNewsgroupListing());
1087     }
1088 
1089     /**
1090      * List the newsgroups that match a given pattern.
1091      * Uses the "LIST ACTIVE" command.
1092      * <p>
1093      * @param wildmat a pseudo-regex pattern (cf. RFC 2980)
1094      * @return An array of NewsgroupInfo instances containing the information
1095      *    for each newsgroup served by the NNTP server corresponding to the
1096      *    supplied pattern.   If no such newsgroups are served, a zero length
1097      *    array will be returned.  If the command fails, null will be returned.
1098      * @throws IOException on error
1099      * @see #iterateNewsgroupListing(String)
1100      * @see #iterateNewsgroups(String)
1101      */
1102     public NewsgroupInfo[] listNewsgroups(String wildmat) throws IOException
1103     {
1104         if(!NNTPReply.isPositiveCompletion(listActive(wildmat))) {
1105             return null;
1106         }
1107         return __readNewsgroupListing();
1108     }
1109 
1110 
1111     /**
1112      * List the newsgroups that match a given pattern.
1113      * Uses the "LIST ACTIVE" command.
1114      * <p>
1115      * @param wildmat a pseudo-regex pattern (cf. RFC 2980)
1116      * @return An iterable of Strings containing the raw information
1117      *    for each newsgroup served by the NNTP server corresponding to the
1118      *    supplied pattern.   If no such newsgroups are served, no entries
1119      *    will be returned.
1120      * @throws IOException on error
1121      * @since 3.0
1122      */
1123     public Iterable<String> iterateNewsgroupListing(String wildmat) throws IOException {
1124         if(NNTPReply.isPositiveCompletion(listActive(wildmat))) {
1125             return new ReplyIterator(_reader_);
1126         }
1127         throw new IOException("LIST ACTIVE "+wildmat+" command failed: "+getReplyString());
1128     }
1129 
1130     /**
1131      * List the newsgroups that match a given pattern.
1132      * Uses the "LIST ACTIVE" command.
1133      * <p>
1134      * @param wildmat a pseudo-regex pattern (cf. RFC 2980)
1135      * @return An iterable NewsgroupInfo instances containing the information
1136      *    for each newsgroup served by the NNTP server corresponding to the
1137      *    supplied pattern.   If no such newsgroups are served, no entries
1138      *    will be returned.
1139      * @throws IOException on error
1140      * @since 3.0
1141      */
1142     public Iterable<NewsgroupInfo> iterateNewsgroups(String wildmat) throws IOException {
1143         return new NewsgroupIterator(iterateNewsgroupListing(wildmat));
1144     }
1145 
1146     /***
1147      * List all new newsgroups added to the NNTP server since a particular
1148      * date subject to the conditions of the specified query.  If no new
1149      * newsgroups were added, a zero length array will be returned.  If the
1150      * command fails, null will be returned.
1151      * This uses the "NEWGROUPS" command.
1152      * <p>
1153      * @param query  The query restricting how to search for new newsgroups.
1154      * @return An array of NewsgroupInfo instances containing the information
1155      *    for each new newsgroup added to the NNTP server.   If no newsgroups
1156      *    were added, a zero length array will be returned.  If the command
1157      *    fails, null will be returned.
1158      * @exception NNTPConnectionClosedException
1159      *      If the NNTP server prematurely closes the connection as a result
1160      *      of the client being idle or some other reason causing the server
1161      *      to send NNTP reply code 400.  This exception may be caught either
1162      *      as an IOException or independently as itself.
1163      * @exception IOException  If an I/O error occurs while either sending a
1164      *      command to the server or receiving a reply from the server.
1165      * @see #iterateNewNewsgroups(NewGroupsOrNewsQuery)
1166      * @see #iterateNewNewsgroupListing(NewGroupsOrNewsQuery)
1167      ***/
1168     public NewsgroupInfo[] listNewNewsgroups(NewGroupsOrNewsQuery query)
1169     throws IOException
1170     {
1171         if (!NNTPReply.isPositiveCompletion(newgroups(
1172                                                 query.getDate(), query.getTime(),
1173                                                 query.isGMT(), query.getDistributions())))
1174         {
1175             return null;
1176         }
1177 
1178         return __readNewsgroupListing();
1179     }
1180 
1181     /**
1182      * List all new newsgroups added to the NNTP server since a particular
1183      * date subject to the conditions of the specified query.  If no new
1184      * newsgroups were added, no entries will be returned.
1185      * This uses the "NEWGROUPS" command.
1186      * <p>
1187      * @param query  The query restricting how to search for new newsgroups.
1188      * @return An iterable of Strings containing the raw information
1189      *    for each new newsgroup added to the NNTP server.   If no newsgroups
1190      *    were added, no entries will be returned.
1191      * @exception NNTPConnectionClosedException
1192      *      If the NNTP server prematurely closes the connection as a result
1193      *      of the client being idle or some other reason causing the server
1194      *      to send NNTP reply code 400.  This exception may be caught either
1195      *      as an IOException or independently as itself.
1196      * @exception IOException  If an I/O error occurs while either sending a
1197      *      command to the server or receiving a reply from the server.
1198      * @since 3.0
1199      */
1200     public Iterable<String> iterateNewNewsgroupListing(NewGroupsOrNewsQuery query) throws IOException {
1201         if (NNTPReply.isPositiveCompletion(newgroups(
1202                 query.getDate(), query.getTime(),
1203                 query.isGMT(), query.getDistributions()))) {
1204             return new ReplyIterator(_reader_);
1205         }
1206         throw new IOException("NEWGROUPS command failed: "+getReplyString());
1207     }
1208 
1209     /**
1210      * List all new newsgroups added to the NNTP server since a particular
1211      * date subject to the conditions of the specified query.  If no new
1212      * newsgroups were added, no entries will be returned.
1213      * This uses the "NEWGROUPS" command.
1214      * <p>
1215      * @param query  The query restricting how to search for new newsgroups.
1216      * @return An iterable of NewsgroupInfo instances containing the information
1217      *    for each new newsgroup added to the NNTP server.   If no newsgroups
1218      *    were added, no entries will be returned.
1219      * @exception NNTPConnectionClosedException
1220      *      If the NNTP server prematurely closes the connection as a result
1221      *      of the client being idle or some other reason causing the server
1222      *      to send NNTP reply code 400.  This exception may be caught either
1223      *      as an IOException or independently as itself.
1224      * @exception IOException  If an I/O error occurs while either sending a
1225      *      command to the server or receiving a reply from the server.
1226      * @since 3.0
1227      */
1228     public Iterable<NewsgroupInfo> iterateNewNewsgroups(NewGroupsOrNewsQuery query) throws IOException {
1229         return new NewsgroupIterator(iterateNewNewsgroupListing(query));
1230     }
1231 
1232     /***
1233      * List all new articles added to the NNTP server since a particular
1234      * date subject to the conditions of the specified query.  If no new
1235      * new news is found, a zero length array will be returned.  If the
1236      * command fails, null will be returned.  You must add at least one
1237      * newsgroup to the query, else the command will fail.  Each String
1238      * in the returned array is a unique message identifier including the
1239      * enclosing &lt; and &gt;.
1240      * This uses the "NEWNEWS" command.
1241      * <p>
1242      * @param query  The query restricting how to search for new news.  You
1243      *    must add at least one newsgroup to the query.
1244      * @return An array of String instances containing the unique message
1245      *    identifiers for each new article added to the NNTP server.  If no
1246      *    new news is found, a zero length array will be returned.  If the
1247      *    command fails, null will be returned.
1248      * @exception NNTPConnectionClosedException
1249      *      If the NNTP server prematurely closes the connection as a result
1250      *      of the client being idle or some other reason causing the server
1251      *      to send NNTP reply code 400.  This exception may be caught either
1252      *      as an IOException or independently as itself.
1253      * @exception IOException  If an I/O error occurs while either sending a
1254      *      command to the server or receiving a reply from the server.
1255      *
1256      * @see #iterateNewNews(NewGroupsOrNewsQuery)
1257      ***/
1258     public String[] listNewNews(NewGroupsOrNewsQuery query)
1259     throws IOException
1260     {
1261         if (!NNTPReply.isPositiveCompletion(
1262                 newnews(query.getNewsgroups(), query.getDate(), query.getTime(),
1263                         query.isGMT(), query.getDistributions()))) {
1264             return null;
1265         }
1266 
1267         Vector<String> list = new Vector<String>();
1268         BufferedReader reader = new DotTerminatedMessageReader(_reader_);
1269 
1270         String line;
1271         try {
1272             while ((line = reader.readLine()) != null) {
1273                 list.addElement(line);
1274             }
1275         } finally {
1276             reader.close();
1277         }
1278 
1279         int size = list.size();
1280         if (size < 1) {
1281             return new String[0];
1282         }
1283 
1284         String[] result = new String[size];
1285         list.copyInto(result);
1286 
1287         return result;
1288     }
1289 
1290     /**
1291      * List all new articles added to the NNTP server since a particular
1292      * date subject to the conditions of the specified query.  If no new
1293      * new news is found, no entries will be returned.
1294      * This uses the "NEWNEWS" command.
1295      * You must add at least one newsgroup to the query, else the command will fail.
1296      * Each String which is returned is a unique message identifier including the
1297      * enclosing &lt; and &gt;.
1298      * <p>
1299      * @param query  The query restricting how to search for new news.  You
1300      *    must add at least one newsgroup to the query.
1301      * @return An iterator of String instances containing the unique message
1302      *    identifiers for each new article added to the NNTP server.  If no
1303      *    new news is found, no strings will be returned.
1304      * @exception NNTPConnectionClosedException
1305      *      If the NNTP server prematurely closes the connection as a result
1306      *      of the client being idle or some other reason causing the server
1307      *      to send NNTP reply code 400.  This exception may be caught either
1308      *      as an IOException or independently as itself.
1309      * @exception IOException  If an I/O error occurs while either sending a
1310      *      command to the server or receiving a reply from the server.
1311      * @since 3.0
1312      */
1313     public Iterable<String> iterateNewNews(NewGroupsOrNewsQuery query) throws IOException {
1314         if (NNTPReply.isPositiveCompletion(newnews(
1315                 query.getNewsgroups(), query.getDate(), query.getTime(),
1316                 query.isGMT(), query.getDistributions()))) {
1317             return new ReplyIterator(_reader_);
1318         }
1319         throw new IOException("NEWNEWS command failed: "+getReplyString());
1320     }
1321 
1322     /***
1323      * There are a few NNTPClient methods that do not complete the
1324      * entire sequence of NNTP commands to complete a transaction.  These
1325      * commands require some action by the programmer after the reception
1326      * of a positive preliminary command.  After the programmer's code
1327      * completes its actions, it must call this method to receive
1328      * the completion reply from the server and verify the success of the
1329      * entire transaction.
1330      * <p>
1331      * For example
1332      * <pre>
1333      * writer = client.postArticle();
1334      * if(writer == null) // failure
1335      *   return false;
1336      * header = new SimpleNNTPHeader("foobar@foo.com", "Just testing");
1337      * header.addNewsgroup("alt.test");
1338      * writer.write(header.toString());
1339      * writer.write("This is just a test");
1340      * writer.close();
1341      * if(!client.completePendingCommand()) // failure
1342      *   return false;
1343      * </pre>
1344      * <p>
1345      * @return True if successfully completed, false if not.
1346      * @exception NNTPConnectionClosedException
1347      *      If the NNTP server prematurely closes the connection as a result
1348      *      of the client being idle or some other reason causing the server
1349      *      to send NNTP reply code 400.  This exception may be caught either
1350      *      as an IOException or independently as itself.
1351      * @exception IOException  If an I/O error occurs while either sending a
1352      *      command to the server or receiving a reply from the server.
1353      ***/
1354     public boolean completePendingCommand() throws IOException
1355     {
1356         return NNTPReply.isPositiveCompletion(getReply());
1357     }
1358 
1359     /***
1360      * Post an article to the NNTP server.  This method returns a
1361      * DotTerminatedMessageWriter instance to which the article can be
1362      * written.  Null is returned if the posting attempt fails.  You
1363      * should check {@link NNTP#isAllowedToPost isAllowedToPost() }
1364      *  before trying to post.  However, a posting
1365      * attempt can fail due to malformed headers.
1366      * <p>
1367      * You must not issue any commands to the NNTP server (i.e., call any
1368      * (other methods) until you finish writing to the returned Writer
1369      * instance and close it.  The NNTP protocol uses the same stream for
1370      * issuing commands as it does for returning results.  Therefore the
1371      * returned Writer actually writes directly to the NNTP connection.
1372      * After you close the writer, you can execute new commands.  If you
1373      * do not follow these requirements your program will not work properly.
1374      * <p>
1375      * Different NNTP servers will require different header formats, but
1376      * you can use the provided
1377      * {@link org.apache.commons.net.nntp.SimpleNNTPHeader}
1378      * class to construct the bare minimum acceptable header for most
1379      * news readers.  To construct more complicated headers you should
1380      * refer to RFC 822.  When the Java Mail API is finalized, you will be
1381      * able to use it to compose fully compliant Internet text messages.
1382      * The DotTerminatedMessageWriter takes care of doubling line-leading
1383      * dots and ending the message with a single dot upon closing, so all
1384      * you have to worry about is writing the header and the message.
1385      * <p>
1386      * Upon closing the returned Writer, you need to call
1387      * {@link #completePendingCommand  completePendingCommand() }
1388      * to finalize the posting and verify its success or failure from
1389      * the server reply.
1390      * <p>
1391      * @return A DotTerminatedMessageWriter to which the article (including
1392      *      header) can be written.  Returns null if the command fails.
1393      * @exception IOException  If an I/O error occurs while either sending a
1394      *      command to the server or receiving a reply from the server.
1395      ***/
1396 
1397     public Writer postArticle() throws IOException
1398     {
1399         if (!NNTPReply.isPositiveIntermediate(post())) {
1400             return null;
1401         }
1402 
1403         return new DotTerminatedMessageWriter(_writer_);
1404     }
1405 
1406 
1407     public Writer forwardArticle(String articleId) throws IOException
1408     {
1409         if (!NNTPReply.isPositiveIntermediate(ihave(articleId))) {
1410             return null;
1411         }
1412 
1413         return new DotTerminatedMessageWriter(_writer_);
1414     }
1415 
1416 
1417     /***
1418      * Logs out of the news server gracefully by sending the QUIT command.
1419      * However, you must still disconnect from the server before you can open
1420      * a new connection.
1421      * <p>
1422      * @return True if successfully completed, false if not.
1423      * @exception IOException  If an I/O error occurs while either sending a
1424      *      command to the server or receiving a reply from the server.
1425      ***/
1426     public boolean logout() throws IOException
1427     {
1428         return NNTPReply.isPositiveCompletion(quit());
1429     }
1430 
1431 
1432     /**
1433      * Log into a news server by sending the AUTHINFO USER/AUTHINFO
1434      * PASS command sequence. This is usually sent in response to a
1435      * 480 reply code from the NNTP server.
1436      * <p>
1437      * @param username a valid username
1438      * @param password the corresponding password
1439      * @return True for successful login, false for a failure
1440      * @throws IOException on error
1441      */
1442     public boolean authenticate(String username, String password)
1443         throws IOException
1444     {
1445         int replyCode = authinfoUser(username);
1446 
1447         if (replyCode == NNTPReply.MORE_AUTH_INFO_REQUIRED)
1448             {
1449                 replyCode = authinfoPass(password);
1450 
1451                 if (replyCode == NNTPReply.AUTHENTICATION_ACCEPTED)
1452                     {
1453                         _isAllowedToPost = true;
1454                         return true;
1455                     }
1456             }
1457         return false;
1458     }
1459 
1460     /***
1461      * Private implementation of XOVER functionality.
1462      *
1463      * See {@link NNTP#xover}
1464      * for legal agument formats. Alternatively, read RFC 2980 :-)
1465      * <p>
1466      * @param articleRange
1467      * @return Returns a DotTerminatedMessageReader if successful, null
1468      *         otherwise
1469      * @exception IOException
1470      */
1471     private BufferedReader __retrieveArticleInfo(String articleRange)
1472         throws IOException
1473     {
1474         if (!NNTPReply.isPositiveCompletion(xover(articleRange))) {
1475             return null;
1476         }
1477 
1478         return new DotTerminatedMessageReader(_reader_);
1479     }
1480 
1481     /**
1482      * Return article headers for a specified post.
1483      * <p>
1484      * @param articleNumber the article to retrieve headers for
1485      * @return a DotTerminatedReader if successful, null otherwise
1486      * @throws IOException on error
1487      */
1488     public BufferedReader retrieveArticleInfo(long articleNumber) throws IOException
1489     {
1490         return __retrieveArticleInfo(Long.toString(articleNumber));
1491     }
1492 
1493     /**
1494      * Return article headers for all articles between lowArticleNumber
1495      * and highArticleNumber, inclusively. Uses the XOVER command.
1496      * <p>
1497      * @param lowArticleNumber low number
1498      * @param highArticleNumber high number
1499      * @return a DotTerminatedReader if successful, null otherwise
1500      * @throws IOException on error
1501      */
1502     public BufferedReader retrieveArticleInfo(long lowArticleNumber,
1503             long highArticleNumber)
1504         throws IOException
1505     {
1506         return
1507             __retrieveArticleInfo(lowArticleNumber + "-" +
1508                                              highArticleNumber);
1509     }
1510 
1511     /**
1512      * Return article headers for all articles between lowArticleNumber
1513      * and highArticleNumber, inclusively, using the XOVER command.
1514      * <p>
1515      * @param lowArticleNumber low
1516      * @param highArticleNumber high
1517      * @return an Iterable of Articles
1518      * @throws IOException if the command failed
1519      * @since 3.0
1520      */
1521     public Iterable<Article> iterateArticleInfo(long lowArticleNumber, long highArticleNumber)
1522         throws IOException
1523     {
1524         BufferedReader info = retrieveArticleInfo(lowArticleNumber,highArticleNumber);
1525         if (info == null) {
1526             throw new IOException("XOVER command failed: "+getReplyString());
1527         }
1528         // N.B. info is already DotTerminated, so don't rewrap
1529         return new ArticleIterator(new ReplyIterator(info, false));
1530     }
1531 
1532     /***
1533      * Private implementation of XHDR functionality.
1534      *
1535      * See {@link NNTP#xhdr}
1536      * for legal agument formats. Alternatively, read RFC 1036.
1537      * <p>
1538      * @param header
1539      * @param articleRange
1540      * @return Returns a DotTerminatedMessageReader if successful, null
1541      *         otherwise
1542      * @exception IOException
1543      */
1544     private BufferedReader __retrieveHeader(String header, String articleRange)
1545         throws IOException
1546     {
1547         if (!NNTPReply.isPositiveCompletion(xhdr(header, articleRange))) {
1548             return null;
1549         }
1550 
1551         return new DotTerminatedMessageReader(_reader_);
1552     }
1553 
1554     /**
1555      * Return an article header for a specified post.
1556      * <p>
1557      * @param header the header to retrieve
1558      * @param articleNumber the article to retrieve the header for
1559      * @return a DotTerminatedReader if successful, null otherwise
1560      * @throws IOException on error
1561      */
1562     public BufferedReader retrieveHeader(String header, long articleNumber)
1563         throws IOException
1564     {
1565         return __retrieveHeader(header, Long.toString(articleNumber));
1566     }
1567 
1568     /**
1569      * Return an article header for all articles between lowArticleNumber
1570      * and highArticleNumber, inclusively.
1571      * <p>
1572      * @param header the header
1573      * @param lowArticleNumber to fetch
1574      * @param highArticleNumber to fetch
1575      * @return a DotTerminatedReader if successful, null otherwise
1576      * @throws IOException on error
1577      */
1578     public BufferedReader retrieveHeader(String header, long lowArticleNumber,
1579                                  long highArticleNumber)
1580         throws IOException
1581     {
1582         return
1583             __retrieveHeader(header,lowArticleNumber + "-" + highArticleNumber);
1584     }
1585 
1586 
1587 
1588 
1589 
1590     // DEPRECATED METHODS - for API compatibility only - DO NOT USE
1591     // ============================================================
1592 
1593 
1594 
1595     /**
1596      * @param header the header
1597      * @param lowArticleNumber to fetch
1598      * @param highArticleNumber to fetch
1599      * @return a DotTerminatedReader if successful, null otherwise
1600      * @throws IOException on error
1601      * @deprecated 3.0 use {@link #retrieveHeader(String, long, long)} instead
1602      */
1603     @Deprecated
1604     public Reader retrieveHeader(String header, int lowArticleNumber, int highArticleNumber)
1605         throws IOException
1606     {
1607         return retrieveHeader(header, (long) lowArticleNumber, (long) highArticleNumber);
1608     }
1609 
1610     /**
1611      * @param lowArticleNumber to fetch
1612      * @param highArticleNumber to fetch
1613      * @return a DotTerminatedReader if successful, null otherwise
1614      * @throws IOException on error
1615      * @deprecated 3.0 use {@link #retrieveArticleInfo(long, long)} instead
1616      */
1617     @Deprecated
1618     public Reader retrieveArticleInfo(int lowArticleNumber, int highArticleNumber) throws IOException {
1619         return retrieveArticleInfo((long) lowArticleNumber, (long) highArticleNumber);
1620     }
1621 
1622     /**
1623      * @param a tba
1624      * @param b  tba
1625      * @return  tba
1626      * @throws IOException tba
1627      * @deprecated 3.0 use {@link #retrieveHeader(String, long)} instead
1628      */
1629     @Deprecated
1630     public Reader retrieveHeader(String a, int b) throws IOException {
1631         return retrieveHeader(a, (long) b);
1632     }
1633 
1634     /**
1635      * @param a  tba
1636      * @param ap  tba
1637      * @return  tba
1638      * @throws IOException tba
1639      * @deprecated 3.0 use {@link #selectArticle(long, ArticleInfo)} instead
1640      */
1641     @Deprecated
1642     public boolean selectArticle(int a, ArticlePointer ap) throws IOException {
1643         ArticleInfo ai =  __ap2ai(ap);
1644         boolean b = selectArticle(a, ai);
1645         __ai2ap(ai, ap);
1646         return b;
1647     }
1648 
1649     /**
1650      * @param lowArticleNumber to fetch
1651      * @return a DotTerminatedReader if successful, null otherwise
1652      * @throws IOException  tba
1653      * @deprecated 3.0 use {@link #retrieveArticleInfo(long)} instead
1654      */
1655     @Deprecated
1656     public Reader retrieveArticleInfo(int lowArticleNumber) throws IOException {
1657         return retrieveArticleInfo((long) lowArticleNumber);
1658     }
1659 
1660     /**
1661      * @param a  tba
1662      * @return  tba
1663      * @throws IOException  tba
1664      * @deprecated 3.0 use {@link #selectArticle(long)} instead
1665      */
1666     @Deprecated
1667     public boolean selectArticle(int a) throws IOException {
1668         return selectArticle((long) a);
1669     }
1670 
1671     /**
1672      * @param a  tba
1673      * @return  tba
1674      * @throws IOException  tba
1675      * @deprecated 3.0 use {@link #retrieveArticleHeader(long)} instead
1676      */
1677     @Deprecated
1678     public Reader retrieveArticleHeader(int a) throws IOException {
1679         return retrieveArticleHeader((long) a);
1680     }
1681 
1682     /**
1683      * @param a  tba
1684      * @param ap  tba
1685      * @return  tba
1686      * @throws IOException  tba
1687      * @deprecated 3.0 use {@link #retrieveArticleHeader(long, ArticleInfo)} instead
1688      */
1689     @Deprecated
1690     public Reader retrieveArticleHeader(int a, ArticlePointer ap) throws IOException {
1691         ArticleInfo ai =  __ap2ai(ap);
1692         Reader rdr = retrieveArticleHeader(a, ai);
1693         __ai2ap(ai, ap);
1694         return rdr;
1695     }
1696 
1697     /**
1698      * @param a  tba
1699      * @return  tba
1700      * @throws IOException  tba
1701      * @deprecated 3.0 use {@link #retrieveArticleBody(long)} instead
1702      */
1703     @Deprecated
1704     public Reader retrieveArticleBody(int a) throws IOException {
1705         return retrieveArticleBody((long) a);
1706     }
1707 
1708     /**
1709      * @param articleNumber  The number of the the article to retrieve.
1710      * @param pointer A parameter through which to return the article's number and unique id
1711      * @return A DotTerminatedMessageReader instance from which the article
1712      *         can be read.  null if the article does not exist.
1713      * @throws IOException on error
1714      * @deprecated 3.0 use {@link #retrieveArticle(long, ArticleInfo)} instead
1715      */
1716     @Deprecated
1717     public Reader retrieveArticle(int articleNumber, ArticlePointer pointer) throws IOException {
1718         ArticleInfo ai =  __ap2ai(pointer);
1719         Reader rdr = retrieveArticle(articleNumber, ai);
1720         __ai2ap(ai, pointer);
1721         return rdr;
1722     }
1723 
1724     /**
1725      * @param articleNumber The number of the the article to retrieve
1726      * @return A DotTerminatedMessageReader instance from which the article
1727      *         can be read.  null if the article does not exist.
1728      * @throws IOException on error
1729      * @deprecated 3.0 use {@link #retrieveArticle(long)} instead
1730      */
1731     @Deprecated
1732     public Reader retrieveArticle(int articleNumber) throws IOException {
1733         return retrieveArticle((long) articleNumber);
1734     }
1735 
1736     /**
1737      * @param a  tba
1738      * @param ap  tba
1739      * @return  tba
1740      * @throws IOException  tba
1741      * @deprecated 3.0 use {@link #retrieveArticleBody(long, ArticleInfo)} instead
1742      */
1743     @Deprecated
1744     public Reader retrieveArticleBody(int a, ArticlePointer ap) throws IOException {
1745         ArticleInfo ai =  __ap2ai(ap);
1746         Reader rdr = retrieveArticleBody(a, ai);
1747         __ai2ap(ai, ap);
1748         return rdr;
1749     }
1750 
1751     /**
1752      * @param articleId The unique article identifier of the article to retrieve
1753      * @param pointer A parameter through which to return the article's number and unique id
1754      * @deprecated 3.0 use {@link #retrieveArticle(String, ArticleInfo)} instead
1755      * @return A DotTerminatedMessageReader instance from which the article can be read.
1756      * null if the article does not exist.
1757      * @throws IOException on error
1758      */
1759     @Deprecated
1760     public Reader retrieveArticle(String articleId, ArticlePointer pointer) throws IOException {
1761         ArticleInfo ai =  __ap2ai(pointer);
1762         Reader rdr = retrieveArticle(articleId, ai);
1763         __ai2ap(ai, pointer);
1764         return rdr;
1765     }
1766 
1767     /**
1768      * @param articleId The unique article identifier of the article to retrieve
1769      * @param pointer A parameter through which to return the article's number and unique id
1770      * @return A DotTerminatedMessageReader instance from which the article
1771      *         body can be read.  null if the article does not exist.
1772      * @throws IOException on error
1773      * @deprecated 3.0 use {@link #retrieveArticleBody(String, ArticleInfo)} instead
1774      */
1775     @Deprecated
1776     public Reader retrieveArticleBody(String articleId, ArticlePointer pointer) throws IOException {
1777         ArticleInfo ai =  __ap2ai(pointer);
1778         Reader rdr = retrieveArticleBody(articleId, ai);
1779         __ai2ap(ai, pointer);
1780         return rdr;
1781     }
1782 
1783     /**
1784      * @param articleId The unique article identifier of the article to retrieve
1785      * @param pointer A parameter through which to return the article's number and unique id
1786      * @return A DotTerminatedMessageReader instance from which the article
1787      *         body can be read.  null if the article does not exist.
1788      * @throws IOException on error
1789      * @deprecated 3.0 use {@link #retrieveArticleHeader(String, ArticleInfo)} instead
1790      */
1791     @Deprecated
1792     public Reader retrieveArticleHeader(String articleId, ArticlePointer pointer) throws IOException {
1793         ArticleInfo ai =  __ap2ai(pointer);
1794         Reader rdr = retrieveArticleHeader(articleId, ai);
1795         __ai2ap(ai, pointer);
1796         return rdr;
1797     }
1798 
1799     /**
1800      * @param articleId The unique article identifier of the article to retrieve
1801      * @param pointer A parameter through which to return the article's number and unique id
1802      * @return A DotTerminatedMessageReader instance from which the article
1803      *         body can be read.  null if the article does not exist.
1804      * @throws IOException on error
1805      * @deprecated 3.0 use {@link #selectArticle(String, ArticleInfo)} instead
1806      */
1807     @Deprecated
1808     public boolean selectArticle(String articleId, ArticlePointer pointer) throws IOException {
1809         ArticleInfo ai =  __ap2ai(pointer);
1810         boolean b = selectArticle(articleId, ai);
1811         __ai2ap(ai, pointer);
1812         return b;
1813 
1814     }
1815 
1816     /**
1817      * @param pointer A parameter through which to return the article's number and unique id
1818      * @return True if successful, false if not.
1819      * @throws IOException on error
1820      * @deprecated 3.0 use {@link #selectArticle(ArticleInfo)} instead
1821      */
1822     @Deprecated
1823     public boolean selectArticle(ArticlePointer pointer) throws IOException {
1824         ArticleInfo ai =  __ap2ai(pointer);
1825         boolean b = selectArticle(ai);
1826         __ai2ap(ai, pointer);
1827         return b;
1828 
1829     }
1830 
1831     /**
1832      * @param pointer A parameter through which to return the article's number and unique id
1833      * @return True if successful, false if not.
1834      * @throws IOException on error
1835      * @deprecated 3.0 use {@link #selectNextArticle(ArticleInfo)} instead
1836      */
1837     @Deprecated
1838     public boolean selectNextArticle(ArticlePointer pointer) throws IOException {
1839         ArticleInfo ai =  __ap2ai(pointer);
1840         boolean b = selectNextArticle(ai);
1841         __ai2ap(ai, pointer);
1842         return b;
1843 
1844     }
1845 
1846     /**
1847      * @param pointer A parameter through which to return the article's number and unique id
1848      * @return True if successful, false if not.
1849      * @throws IOException on error
1850      * @deprecated 3.0 use {@link #selectPreviousArticle(ArticleInfo)} instead
1851      */
1852     @Deprecated
1853     public boolean selectPreviousArticle(ArticlePointer pointer) throws IOException {
1854         ArticleInfo ai =  __ap2ai(pointer);
1855         boolean b = selectPreviousArticle(ai);
1856         __ai2ap(ai, pointer);
1857         return b;
1858     }
1859 
1860    // Helper methods
1861 
1862     private ArticleInfo __ap2ai(@SuppressWarnings("deprecation") ArticlePointer ap) {
1863         if (ap == null) {
1864             return null;
1865         }
1866         ArticleInfo ai = new ArticleInfo();
1867         return ai;
1868     }
1869 
1870     @SuppressWarnings("deprecation")
1871     private void __ai2ap(ArticleInfo ai, ArticlePointer ap){
1872         if (ap != null) { // ai cannot be null
1873             ap.articleId = ai.articleId;
1874             ap.articleNumber = (int) ai.articleNumber;
1875         }
1876     }
1877 }
1878 
1879 
1880 /* Emacs configuration
1881  * Local variables:        **
1882  * mode:             java  **
1883  * c-basic-offset:   4     **
1884  * indent-tabs-mode: nil   **
1885  * End:                    **
1886  */