View Javadoc

1   /*
2    * Copyright 2001-2005 The Apache Software Foundation
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.apache.commons.net.nntp;
17  
18  import java.io.BufferedReader;
19  import java.io.IOException;
20  import java.io.Reader;
21  import java.io.StringWriter;
22  import java.io.Writer;
23  import java.util.StringTokenizer;
24  import java.util.Vector;
25  import org.apache.commons.net.io.DotTerminatedMessageReader;
26  import org.apache.commons.net.io.DotTerminatedMessageWriter;
27  import org.apache.commons.net.io.Util;
28  import org.apache.commons.net.MalformedServerReplyException;
29  
30  /***
31   * NNTPClient encapsulates all the functionality necessary to post and
32   * retrieve articles from an NNTP server.  As with all classes derived
33   * from {@link org.apache.commons.net.SocketClient},
34   * you must first connect to the server with
35   * {@link org.apache.commons.net.SocketClient#connect  connect }
36   * before doing anything, and finally
37   * {@link org.apache.commons.net.nntp.NNTP#disconnect  disconnect() }
38   * after you're completely finished interacting with the server.
39   * Remember that the
40   * {@link org.apache.commons.net.nntp.NNTP#isAllowedToPost isAllowedToPost()}
41   *  method is defined in
42   * {@link org.apache.commons.net.nntp.NNTP}.
43   * <p>
44   * You should keep in mind that the NNTP server may choose to prematurely
45   * close a connection if the client has been idle for longer than a
46   * given time period or if the server is being shutdown by the operator or
47   * some other reason.  The NNTP class will detect a
48   * premature NNTP server connection closing when it receives a
49   * {@link org.apache.commons.net.nntp.NNTPReply#SERVICE_DISCONTINUED NNTPReply.SERVICE_DISCONTINUED }
50   *  response to a command.
51   * When that occurs, the NNTP class method encountering that reply will throw
52   * an {@link org.apache.commons.net.nntp.NNTPConnectionClosedException}
53   * .
54   * <code>NNTPConectionClosedException</code>
55   * is a subclass of <code> IOException </code> and therefore need not be
56   * caught separately, but if you are going to catch it separately, its
57   * catch block must appear before the more general <code> IOException </code>
58   * catch block.  When you encounter an
59   * {@link org.apache.commons.net.nntp.NNTPConnectionClosedException}
60   * , you must disconnect the connection with
61   * {@link org.apache.commons.net.nntp.NNTP#disconnect  disconnect() }
62   *  to properly clean up the
63   * system resources used by NNTP.  Before disconnecting, you may check the
64   * last reply code and text with
65   * {@link org.apache.commons.net.nntp.NNTP#getReplyCode  getReplyCode } and
66   * {@link org.apache.commons.net.nntp.NNTP#getReplyString  getReplyString }.
67   * <p>
68   * Rather than list it separately for each method, we mention here that
69   * every method communicating with the server and throwing an IOException
70   * can also throw a
71   * {@link org.apache.commons.net.MalformedServerReplyException}
72   * , which is a subclass
73   * of IOException.  A MalformedServerReplyException will be thrown when
74   * the reply received from the server deviates enough from the protocol
75   * specification that it cannot be interpreted in a useful manner despite
76   * attempts to be as lenient as possible.
77   * <p>
78   * <p>
79   * @author Daniel F. Savarese
80   * @author Rory Winston
81   * @author Ted Wise
82   * @see NNTP
83   * @see NNTPConnectionClosedException
84   * @see org.apache.commons.net.MalformedServerReplyException
85   ***/
86  
87  public class NNTPClient extends NNTP
88  {
89  
90      private void __parseArticlePointer(String reply, ArticlePointer pointer)
91      throws MalformedServerReplyException
92      {
93          StringTokenizer tokenizer;
94  
95          // Do loop is a kluge to simulate goto
96          do
97          {
98              tokenizer = new StringTokenizer(reply);
99  
100             if (tokenizer.countTokens() < 3)
101                 break;
102 
103             // Skip numeric response value
104             tokenizer.nextToken();
105             // Get article number
106             try
107             {
108                 pointer.articleNumber = Integer.parseInt(tokenizer.nextToken());
109             }
110             catch (NumberFormatException e)
111             {
112                 break;
113             }
114 
115             // Get article id
116             pointer.articleId = tokenizer.nextToken();
117             return ;
118         }
119         while (false);
120 
121         throw new MalformedServerReplyException(
122             "Could not parse article pointer.\nServer reply: " + reply);
123     }
124 
125 
126     private void __parseGroupReply(String reply, NewsgroupInfo info)
127     throws MalformedServerReplyException
128     {
129         String count, first, last;
130         StringTokenizer tokenizer;
131 
132         // Do loop is a kluge to simulate goto
133         do
134         {
135             tokenizer = new StringTokenizer(reply);
136 
137             if (tokenizer.countTokens() < 5)
138                 break;
139 
140             // Skip numeric response value
141             tokenizer.nextToken();
142             // Get estimated article count
143             count = tokenizer.nextToken();
144             // Get first article number
145             first = tokenizer.nextToken();
146             // Get last article number
147             last = tokenizer.nextToken();
148             // Get newsgroup name
149             info._setNewsgroup(tokenizer.nextToken());
150 
151             try
152             {
153                 info._setArticleCount(Integer.parseInt(count));
154                 info._setFirstArticle(Integer.parseInt(first));
155                 info._setLastArticle(Integer.parseInt(last));
156             }
157             catch (NumberFormatException e)
158             {
159                 break;
160             }
161 
162             info._setPostingPermission(NewsgroupInfo.UNKNOWN_POSTING_PERMISSION);
163             return ;
164         }
165         while (false);
166 
167         throw new MalformedServerReplyException(
168             "Could not parse newsgroup info.\nServer reply: " + reply);
169     }
170 
171 
172     private NewsgroupInfo __parseNewsgroupListEntry(String entry)
173     {
174         NewsgroupInfo result;
175         StringTokenizer tokenizer;
176         int lastNum, firstNum;
177         String last, first, permission;
178 
179         result = new NewsgroupInfo();
180         tokenizer = new StringTokenizer(entry);
181 
182         if (tokenizer.countTokens() < 4)
183             return null;
184 
185         result._setNewsgroup(tokenizer.nextToken());
186         last = tokenizer.nextToken();
187         first = tokenizer.nextToken();
188         permission = tokenizer.nextToken();
189 
190         try
191         {
192             lastNum = Integer.parseInt(last);
193             firstNum = Integer.parseInt(first);
194             result._setFirstArticle(firstNum);
195             result._setLastArticle(lastNum);
196 
197 	    if((firstNum == 0) && (lastNum == 0))
198 		    result._setArticleCount(0);
199 	    else
200 		    result._setArticleCount(lastNum - firstNum + 1);
201         }
202         catch (NumberFormatException e)
203         {
204             return null;
205         }
206 
207         switch (permission.charAt(0))
208         {
209         case 'y':
210         case 'Y':
211             result._setPostingPermission(
212                 NewsgroupInfo.PERMITTED_POSTING_PERMISSION);
213             break;
214         case 'n':
215         case 'N':
216             result._setPostingPermission(
217                 NewsgroupInfo.PROHIBITED_POSTING_PERMISSION);
218             break;
219         case 'm':
220         case 'M':
221             result._setPostingPermission(
222                 NewsgroupInfo.MODERATED_POSTING_PERMISSION);
223             break;
224         default:
225             result._setPostingPermission(
226                 NewsgroupInfo.UNKNOWN_POSTING_PERMISSION);
227             break;
228         }
229 
230         return result;
231     }
232 
233     private NewsgroupInfo[] __readNewsgroupListing() throws IOException
234     {
235         int size;
236         String line;
237         Vector list;
238         BufferedReader reader;
239         NewsgroupInfo tmp, info[];
240 
241         reader = new BufferedReader(new DotTerminatedMessageReader(_reader_));
242         // Start of with a big vector because we may be reading a very large
243         // amount of groups.
244         list = new Vector(2048);
245 
246         while ((line = reader.readLine()) != null)
247         {
248             tmp = __parseNewsgroupListEntry(line);
249             if (tmp != null)
250                 list.addElement(tmp);
251             else
252                 throw new MalformedServerReplyException(line);
253         }
254 
255         if ((size = list.size()) < 1)
256             return new NewsgroupInfo[0];
257 
258         info = new NewsgroupInfo[size];
259         list.copyInto(info);
260 
261         return info;
262     }
263 
264 
265     private Reader __retrieve(int command,
266                               String articleId, ArticlePointer pointer)
267     throws IOException
268     {
269         Reader reader;
270 
271         if (articleId != null)
272         {
273             if (!NNTPReply.isPositiveCompletion(sendCommand(command, articleId)))
274                 return null;
275         }
276         else
277         {
278             if (!NNTPReply.isPositiveCompletion(sendCommand(command)))
279                 return null;
280         }
281 
282 
283         if (pointer != null)
284             __parseArticlePointer(getReplyString(), pointer);
285 
286         reader = new DotTerminatedMessageReader(_reader_);
287         return reader;
288     }
289 
290 
291     private Reader __retrieve(int command,
292                               int articleNumber, ArticlePointer pointer)
293     throws IOException
294     {
295         Reader reader;
296 
297         if (!NNTPReply.isPositiveCompletion(sendCommand(command,
298                                             Integer.toString(articleNumber))))
299             return null;
300 
301         if (pointer != null)
302             __parseArticlePointer(getReplyString(), pointer);
303 
304         reader = new DotTerminatedMessageReader(_reader_);
305         return reader;
306     }
307 
308 
309 
310     /***
311      * Retrieves an article from the NNTP server.  The article is referenced
312      * by its unique article identifier (including the enclosing &lt and &gt).
313      * The article number and identifier contained in the server reply
314      * are returned through an ArticlePointer.  The <code> articleId </code>
315      * field of the ArticlePointer cannot always be trusted because some
316      * NNTP servers do not correctly follow the RFC 977 reply format.
317      * <p>
318      * A DotTerminatedMessageReader is returned from which the article can
319      * be read.  If the article does not exist, null is returned.
320      * <p>
321      * You must not issue any commands to the NNTP server (i.e., call any
322      * other methods) until you finish reading the message from the returned
323      * Reader instance.
324      * The NNTP protocol uses the same stream for issuing commands as it does
325      * for returning results.  Therefore the returned Reader actually reads
326      * directly from the NNTP connection.  After the end of message has been
327      * reached, new commands can be executed and their replies read.  If
328      * you do not follow these requirements, your program will not work
329      * properly.
330      * <p>
331      * @param articleId  The unique article identifier of the article to
332      *     retrieve.  If this parameter is null, the currently selected
333      *     article is retrieved.
334      * @param pointer    A parameter through which to return the article's
335      *   number and unique id.  The articleId field cannot always be trusted
336      *   because of server deviations from RFC 977 reply formats.  You may
337      *   set this parameter to null if you do not desire to retrieve the
338      *   returned article information.
339      * @return A DotTerminatedMessageReader instance from which the article
340      *         be read.  null if the article does not exist.
341      * @exception NNTPConnectionClosedException
342      *      If the NNTP server prematurely closes the connection as a result
343      *      of the client being idle or some other reason causing the server
344      *      to send NNTP reply code 400.  This exception may be caught either
345      *      as an IOException or independently as itself.
346      * @exception IOException  If an I/O error occurs while either sending a
347      *      command to the server or receiving a reply from the server.
348      ***/
349     public Reader retrieveArticle(String articleId, ArticlePointer pointer)
350     throws IOException
351     {
352         return __retrieve(NNTPCommand.ARTICLE, articleId, pointer);
353 
354     }
355 
356     /*** Same as <code> retrieveArticle(articleId, null) </code> ***/
357     public Reader retrieveArticle(String articleId) throws IOException
358     {
359         return retrieveArticle(articleId, null);
360     }
361 
362     /*** Same as <code> retrieveArticle(null) </code> ***/
363     public Reader retrieveArticle() throws IOException
364     {
365         return retrieveArticle(null);
366     }
367 
368 
369     /***
370      * Retrieves an article from the currently selected newsgroup.  The
371      * article is referenced by its article number.
372      * The article number and identifier contained in the server reply
373      * are returned through an ArticlePointer.  The <code> articleId </code>
374      * field of the ArticlePointer cannot always be trusted because some
375      * NNTP servers do not correctly follow the RFC 977 reply format.
376      * <p>
377      * A DotTerminatedMessageReader is returned from which the article can
378      * be read.  If the article does not exist, null is returned.
379      * <p>
380      * You must not issue any commands to the NNTP server (i.e., call any
381      * other methods) until you finish reading the message from the returned
382      * Reader instance.
383      * The NNTP protocol uses the same stream for issuing commands as it does
384      * for returning results.  Therefore the returned Reader actually reads
385      * directly from the NNTP connection.  After the end of message has been
386      * reached, new commands can be executed and their replies read.  If
387      * you do not follow these requirements, your program will not work
388      * properly.
389      * <p>
390      * @param articleNumber  The number of the the article to
391      *     retrieve.
392      * @param pointer    A parameter through which to return the article's
393      *   number and unique id.  The articleId field cannot always be trusted
394      *   because of server deviations from RFC 977 reply formats.  You may
395      *   set this parameter to null if you do not desire to retrieve the
396      *   returned article information.
397      * @return A DotTerminatedMessageReader instance from which the article
398      *         be read.  null if the article does not exist.
399      * @exception NNTPConnectionClosedException
400      *      If the NNTP server prematurely closes the connection as a result
401      *      of the client being idle or some other reason causing the server
402      *      to send NNTP reply code 400.  This exception may be caught either
403      *      as an IOException or independently as itself.
404      * @exception IOException  If an I/O error occurs while either sending a
405      *      command to the server or receiving a reply from the server.
406      ***/
407     public Reader retrieveArticle(int articleNumber, ArticlePointer pointer)
408     throws IOException
409     {
410         return __retrieve(NNTPCommand.ARTICLE, articleNumber, pointer);
411     }
412 
413     /*** Same as <code> retrieveArticle(articleNumber, null) </code> ***/
414     public Reader retrieveArticle(int articleNumber) throws IOException
415     {
416         return retrieveArticle(articleNumber, null);
417     }
418 
419 
420 
421     /***
422      * Retrieves an article header from the NNTP server.  The article is
423      * referenced
424      * by its unique article identifier (including the enclosing &lt and &gt).
425      * The article number and identifier contained in the server reply
426      * are returned through an ArticlePointer.  The <code> articleId </code>
427      * field of the ArticlePointer cannot always be trusted because some
428      * NNTP servers do not correctly follow the RFC 977 reply format.
429      * <p>
430      * A DotTerminatedMessageReader is returned from which the article can
431      * be read.  If the article does not exist, null is returned.
432      * <p>
433      * You must not issue any commands to the NNTP server (i.e., call any
434      * other methods) until you finish reading the message from the returned
435      * Reader instance.
436      * The NNTP protocol uses the same stream for issuing commands as it does
437      * for returning results.  Therefore the returned Reader actually reads
438      * directly from the NNTP connection.  After the end of message has been
439      * reached, new commands can be executed and their replies read.  If
440      * you do not follow these requirements, your program will not work
441      * properly.
442      * <p>
443      * @param articleId  The unique article identifier of the article whose
444      *    header is being retrieved.  If this parameter is null, the
445      *    header of the currently selected article is retrieved.
446      * @param pointer    A parameter through which to return the article's
447      *   number and unique id.  The articleId field cannot always be trusted
448      *   because of server deviations from RFC 977 reply formats.  You may
449      *   set this parameter to null if you do not desire to retrieve the
450      *   returned article information.
451      * @return A DotTerminatedMessageReader instance from which the article
452      *         header can be read.  null if the article does not exist.
453      * @exception NNTPConnectionClosedException
454      *      If the NNTP server prematurely closes the connection as a result
455      *      of the client being idle or some other reason causing the server
456      *      to send NNTP reply code 400.  This exception may be caught either
457      *      as an IOException or independently as itself.
458      * @exception IOException  If an I/O error occurs while either sending a
459      *      command to the server or receiving a reply from the server.
460      ***/
461     public Reader retrieveArticleHeader(String articleId, ArticlePointer pointer)
462     throws IOException
463     {
464         return __retrieve(NNTPCommand.HEAD, articleId, pointer);
465 
466     }
467 
468     /*** Same as <code> retrieveArticleHeader(articleId, null) </code> ***/
469     public Reader retrieveArticleHeader(String articleId) throws IOException
470     {
471         return retrieveArticleHeader(articleId, null);
472     }
473 
474     /*** Same as <code> retrieveArticleHeader(null) </code> ***/
475     public Reader retrieveArticleHeader() throws IOException
476     {
477         return retrieveArticleHeader(null);
478     }
479 
480 
481     /***
482      * Retrieves an article header from the currently selected newsgroup.  The
483      * article is referenced by its article number.
484      * The article number and identifier contained in the server reply
485      * are returned through an ArticlePointer.  The <code> articleId </code>
486      * field of the ArticlePointer cannot always be trusted because some
487      * NNTP servers do not correctly follow the RFC 977 reply format.
488      * <p>
489      * A DotTerminatedMessageReader is returned from which the article can
490      * be read.  If the article does not exist, null is returned.
491      * <p>
492      * You must not issue any commands to the NNTP server (i.e., call any
493      * other methods) until you finish reading the message from the returned
494      * Reader instance.
495      * The NNTP protocol uses the same stream for issuing commands as it does
496      * for returning results.  Therefore the returned Reader actually reads
497      * directly from the NNTP connection.  After the end of message has been
498      * reached, new commands can be executed and their replies read.  If
499      * you do not follow these requirements, your program will not work
500      * properly.
501      * <p>
502      * @param articleNumber  The number of the the article whose header is
503      *     being retrieved.
504      * @param pointer    A parameter through which to return the article's
505      *   number and unique id.  The articleId field cannot always be trusted
506      *   because of server deviations from RFC 977 reply formats.  You may
507      *   set this parameter to null if you do not desire to retrieve the
508      *   returned article information.
509      * @return A DotTerminatedMessageReader instance from which the article
510      *         header can be read.  null if the article does not exist.
511      * @exception NNTPConnectionClosedException
512      *      If the NNTP server prematurely closes the connection as a result
513      *      of the client being idle or some other reason causing the server
514      *      to send NNTP reply code 400.  This exception may be caught either
515      *      as an IOException or independently as itself.
516      * @exception IOException  If an I/O error occurs while either sending a
517      *      command to the server or receiving a reply from the server.
518      ***/
519     public Reader retrieveArticleHeader(int articleNumber,
520                                         ArticlePointer pointer)
521     throws IOException
522     {
523         return __retrieve(NNTPCommand.HEAD, articleNumber, pointer);
524     }
525 
526 
527     /*** Same as <code> retrieveArticleHeader(articleNumber, null) </code> ***/
528     public Reader retrieveArticleHeader(int articleNumber) throws IOException
529     {
530         return retrieveArticleHeader(articleNumber, null);
531     }
532 
533 
534 
535     /***
536      * Retrieves an article body from the NNTP server.  The article is
537      * referenced
538      * by its unique article identifier (including the enclosing &lt and &gt).
539      * The article number and identifier contained in the server reply
540      * are returned through an ArticlePointer.  The <code> articleId </code>
541      * field of the ArticlePointer cannot always be trusted because some
542      * NNTP servers do not correctly follow the RFC 977 reply format.
543      * <p>
544      * A DotTerminatedMessageReader is returned from which the article can
545      * be read.  If the article does not exist, null is returned.
546      * <p>
547      * You must not issue any commands to the NNTP server (i.e., call any
548      * other methods) until you finish reading the message from the returned
549      * Reader instance.
550      * The NNTP protocol uses the same stream for issuing commands as it does
551      * for returning results.  Therefore the returned Reader actually reads
552      * directly from the NNTP connection.  After the end of message has been
553      * reached, new commands can be executed and their replies read.  If
554      * you do not follow these requirements, your program will not work
555      * properly.
556      * <p>
557      * @param articleId  The unique article identifier of the article whose
558      *    body is being retrieved.  If this parameter is null, the
559      *    body of the currently selected article is retrieved.
560      * @param pointer    A parameter through which to return the article's
561      *   number and unique id.  The articleId field cannot always be trusted
562      *   because of server deviations from RFC 977 reply formats.  You may
563      *   set this parameter to null if you do not desire to retrieve the
564      *   returned article information.
565      * @return A DotTerminatedMessageReader instance from which the article
566      *         body can be read.  null if the article does not exist.
567      * @exception NNTPConnectionClosedException
568      *      If the NNTP server prematurely closes the connection as a result
569      *      of the client being idle or some other reason causing the server
570      *      to send NNTP reply code 400.  This exception may be caught either
571      *      as an IOException or independently as itself.
572      * @exception IOException  If an I/O error occurs while either sending a
573      *      command to the server or receiving a reply from the server.
574      ***/
575     public Reader retrieveArticleBody(String articleId, ArticlePointer pointer)
576     throws IOException
577     {
578         return __retrieve(NNTPCommand.BODY, articleId, pointer);
579 
580     }
581 
582     /*** Same as <code> retrieveArticleBody(articleId, null) </code> ***/
583     public Reader retrieveArticleBody(String articleId) throws IOException
584     {
585         return retrieveArticleBody(articleId, null);
586     }
587 
588     /*** Same as <code> retrieveArticleBody(null) </code> ***/
589     public Reader retrieveArticleBody() throws IOException
590     {
591         return retrieveArticleBody(null);
592     }
593 
594 
595     /***
596      * Retrieves an article body from the currently selected newsgroup.  The
597      * article is referenced by its article number.
598      * The article number and identifier contained in the server reply
599      * are returned through an ArticlePointer.  The <code> articleId </code>
600      * field of the ArticlePointer cannot always be trusted because some
601      * NNTP servers do not correctly follow the RFC 977 reply format.
602      * <p>
603      * A DotTerminatedMessageReader is returned from which the article can
604      * be read.  If the article does not exist, null is returned.
605      * <p>
606      * You must not issue any commands to the NNTP server (i.e., call any
607      * other methods) until you finish reading the message from the returned
608      * Reader instance.
609      * The NNTP protocol uses the same stream for issuing commands as it does
610      * for returning results.  Therefore the returned Reader actually reads
611      * directly from the NNTP connection.  After the end of message has been
612      * reached, new commands can be executed and their replies read.  If
613      * you do not follow these requirements, your program will not work
614      * properly.
615      * <p>
616      * @param articleNumber  The number of the the article whose body is
617      *     being retrieved.
618      * @param pointer    A parameter through which to return the article's
619      *   number and unique id.  The articleId field cannot always be trusted
620      *   because of server deviations from RFC 977 reply formats.  You may
621      *   set this parameter to null if you do not desire to retrieve the
622      *   returned article information.
623      * @return A DotTerminatedMessageReader instance from which the article
624      *         body can be read.  null if the article does not exist.
625      * @exception NNTPConnectionClosedException
626      *      If the NNTP server prematurely closes the connection as a result
627      *      of the client being idle or some other reason causing the server
628      *      to send NNTP reply code 400.  This exception may be caught either
629      *      as an IOException or independently as itself.
630      * @exception IOException  If an I/O error occurs while either sending a
631      *      command to the server or receiving a reply from the server.
632      ***/
633     public Reader retrieveArticleBody(int articleNumber,
634                                       ArticlePointer pointer)
635     throws IOException
636     {
637         return __retrieve(NNTPCommand.BODY, articleNumber, pointer);
638     }
639 
640 
641     /*** Same as <code> retrieveArticleBody(articleNumber, null) </code> ***/
642     public Reader retrieveArticleBody(int articleNumber) throws IOException
643     {
644         return retrieveArticleBody(articleNumber, null);
645     }
646 
647 
648     /***
649      * Select the specified newsgroup to be the target of for future article
650      * retrieval and posting operations.  Also return the newsgroup
651      * information contained in the server reply through the info parameter.
652      * <p>
653      * @param newsgroup  The newsgroup to select.
654      * @param info  A parameter through which the newsgroup information of
655      *      the selected newsgroup contained in the server reply is returned.
656      *      Set this to null if you do not desire this information.
657      * @return True if the newsgroup exists and was selected, false otherwise.
658      * @exception NNTPConnectionClosedException
659      *      If the NNTP server prematurely closes the connection as a result
660      *      of the client being idle or some other reason causing the server
661      *      to send NNTP reply code 400.  This exception may be caught either
662      *      as an IOException or independently as itself.
663      * @exception IOException  If an I/O error occurs while either sending a
664      *      command to the server or receiving a reply from the server.
665      ***/
666     public boolean selectNewsgroup(String newsgroup, NewsgroupInfo info)
667     throws IOException
668     {
669         if (!NNTPReply.isPositiveCompletion(group(newsgroup)))
670             return false;
671 
672         if (info != null)
673             __parseGroupReply(getReplyString(), info);
674 
675         return true;
676     }
677 
678     /*** Same as <code> selectNewsgroup(newsgroup, null) </code> ***/
679     public boolean selectNewsgroup(String newsgroup) throws IOException
680     {
681         return selectNewsgroup(newsgroup, null);
682     }
683 
684     /***
685      * List the command help from the server.
686      * <p>
687      * @return The sever help information.
688      * @exception NNTPConnectionClosedException
689      *      If the NNTP server prematurely closes the connection as a result
690      *      of the client being idle or some other reason causing the server
691      *      to send NNTP reply code 400.  This exception may be caught either
692      *      as an IOException or independently as itself.
693      * @exception IOException  If an I/O error occurs while either sending a
694      *      command to the server or receiving a reply from the server.
695      ***/
696     public String listHelp() throws IOException
697     {
698         StringWriter help;
699         Reader reader;
700 
701         if (!NNTPReply.isInformational(help()))
702             return null;
703 
704         help = new StringWriter();
705         reader = new DotTerminatedMessageReader(_reader_);
706         Util.copyReader(reader, help);
707         reader.close();
708         help.close();
709         return help.toString();
710     }
711 
712 
713     /***
714      * Select an article by its unique identifier (including enclosing
715      * &lt and &gt) and return its article number and id through the
716      * pointer parameter.  This is achieved through the STAT command.
717      * According to RFC 977, this will NOT set the current article pointer
718      * on the server.  To do that, you must reference the article by its
719      * number.
720      * <p>
721      * @param articleId  The unique article identifier of the article that
722      *    is being selectedd.  If this parameter is null, the
723      *    body of the current article is selected
724      * @param pointer    A parameter through which to return the article's
725      *   number and unique id.  The articleId field cannot always be trusted
726      *   because of server deviations from RFC 977 reply formats.  You may
727      *   set this parameter to null if you do not desire to retrieve the
728      *   returned article information.
729      * @return True if successful, false if not.
730      * @exception NNTPConnectionClosedException
731      *      If the NNTP server prematurely closes the connection as a result
732      *      of the client being idle or some other reason causing the server
733      *      to send NNTP reply code 400.  This exception may be caught either
734      *      as an IOException or independently as itself.
735      * @exception IOException  If an I/O error occurs while either sending a
736      *      command to the server or receiving a reply from the server.
737      ***/
738     public boolean selectArticle(String articleId, ArticlePointer pointer)
739     throws IOException
740     {
741         if (articleId != null)
742         {
743             if (!NNTPReply.isPositiveCompletion(stat(articleId)))
744                 return false;
745         }
746         else
747         {
748             if (!NNTPReply.isPositiveCompletion(stat()))
749                 return false;
750         }
751 
752         if (pointer != null)
753             __parseArticlePointer(getReplyString(), pointer);
754 
755         return true;
756     }
757 
758     /**** Same as <code> selectArticle(articleId, null) </code> ***/
759     public boolean selectArticle(String articleId) throws IOException
760     {
761         return selectArticle(articleId, null);
762     }
763 
764     /****
765      * Same as <code> selectArticle(null, articleId) </code>.  Useful
766      * for retrieving the current article number.
767      ***/
768     public boolean selectArticle(ArticlePointer pointer) throws IOException
769     {
770         return selectArticle(null, pointer);
771     }
772 
773 
774     /***
775      * Select an article in the currently selected newsgroup by its number.
776      * and return its article number and id through the
777      * pointer parameter.  This is achieved through the STAT command.
778      * According to RFC 977, this WILL set the current article pointer
779      * on the server.  Use this command to select an article before retrieving
780      * it, or to obtain an article's unique identifier given its number.
781      * <p>
782      * @param articleNumber The number of the article to select from the
783      *       currently selected newsgroup.
784      * @param pointer    A parameter through which to return the article's
785      *   number and unique id.  Although the articleId field cannot always
786      *   be trusted because of server deviations from RFC 977 reply formats,
787      *   we haven't found a server that misformats this information in response
788      *   to this particular command.  You may set this parameter to null if
789      *   you do not desire to retrieve the returned article information.
790      * @return True if successful, false if not.
791      * @exception NNTPConnectionClosedException
792      *      If the NNTP server prematurely closes the connection as a result
793      *      of the client being idle or some other reason causing the server
794      *      to send NNTP reply code 400.  This exception may be caught either
795      *      as an IOException or independently as itself.
796      * @exception IOException  If an I/O error occurs while either sending a
797      *      command to the server or receiving a reply from the server.
798      ***/
799     public boolean selectArticle(int articleNumber, ArticlePointer pointer)
800     throws IOException
801     {
802         if (!NNTPReply.isPositiveCompletion(stat(articleNumber)))
803             return false;
804 
805         if (pointer != null)
806             __parseArticlePointer(getReplyString(), pointer);
807 
808         return true;
809     }
810 
811 
812     /*** Same as <code> selectArticle(articleNumber, null) </code> ***/
813     public boolean selectArticle(int articleNumber) throws IOException
814     {
815         return selectArticle(articleNumber, null);
816     }
817 
818 
819     /***
820      * Select the article preceeding the currently selected article in the
821      * currently selected newsgroup and return its number and unique id
822      * through the pointer parameter.  Because of deviating server
823      * implementations, the articleId information cannot be trusted.  To
824      * obtain the article identifier, issue a
825      * <code> selectArticle(pointer.articleNumber, pointer) </code> immediately
826      * afterward.
827      * <p>
828      * @param pointer    A parameter through which to return the article's
829      *   number and unique id.  The articleId field cannot always be trusted
830      *   because of server deviations from RFC 977 reply formats.  You may
831      *   set this parameter to null if you do not desire to retrieve the
832      *   returned article information.
833      * @return True if successful, false if not (e.g., there is no previous
834      *     article).
835      * @exception NNTPConnectionClosedException
836      *      If the NNTP server prematurely closes the connection as a result
837      *      of the client being idle or some other reason causing the server
838      *      to send NNTP reply code 400.  This exception may be caught either
839      *      as an IOException or independently as itself.
840      * @exception IOException  If an I/O error occurs while either sending a
841      *      command to the server or receiving a reply from the server.
842      ***/
843     public boolean selectPreviousArticle(ArticlePointer pointer)
844     throws IOException
845     {
846         if (!NNTPReply.isPositiveCompletion(last()))
847             return false;
848 
849         if (pointer != null)
850             __parseArticlePointer(getReplyString(), pointer);
851 
852         return true;
853     }
854 
855     /*** Same as <code> selectPreviousArticle(null) </code> ***/
856     public boolean selectPreviousArticle() throws IOException
857     {
858         return selectPreviousArticle(null);
859     }
860 
861 
862     /***
863      * Select the article following the currently selected article in the
864      * currently selected newsgroup and return its number and unique id
865      * through the pointer parameter.  Because of deviating server
866      * implementations, the articleId information cannot be trusted.  To
867      * obtain the article identifier, issue a
868      * <code> selectArticle(pointer.articleNumber, pointer) </code> immediately
869      * afterward.
870      * <p>
871      * @param pointer    A parameter through which to return the article's
872      *   number and unique id.  The articleId field cannot always be trusted
873      *   because of server deviations from RFC 977 reply formats.  You may
874      *   set this parameter to null if you do not desire to retrieve the
875      *   returned article information.
876      * @return True if successful, false if not (e.g., there is no following
877      *         article).
878      * @exception NNTPConnectionClosedException
879      *      If the NNTP server prematurely closes the connection as a result
880      *      of the client being idle or some other reason causing the server
881      *      to send NNTP reply code 400.  This exception may be caught either
882      *      as an IOException or independently as itself.
883      * @exception IOException  If an I/O error occurs while either sending a
884      *      command to the server or receiving a reply from the server.
885      ***/
886     public boolean selectNextArticle(ArticlePointer pointer) throws IOException
887     {
888         if (!NNTPReply.isPositiveCompletion(next()))
889             return false;
890 
891         if (pointer != null)
892             __parseArticlePointer(getReplyString(), pointer);
893 
894         return true;
895     }
896 
897 
898     /*** Same as <code> selectNextArticle(null) </code> ***/
899     public boolean selectNextArticle() throws IOException
900     {
901         return selectNextArticle(null);
902     }
903 
904 
905     /***
906      * List all newsgroups served by the NNTP server.  If no newsgroups
907      * are served, a zero length array will be returned.  If the command
908      * fails, null will be returned.
909      * <p>
910      * @return An array of NewsgroupInfo instances containing the information
911      *    for each newsgroup served by the NNTP server.   If no newsgroups
912      *    are served, a zero length array will be returned.  If the command
913      *    fails, null will be returned.
914      * @exception NNTPConnectionClosedException
915      *      If the NNTP server prematurely closes the connection as a result
916      *      of the client being idle or some other reason causing the server
917      *      to send NNTP reply code 400.  This exception may be caught either
918      *      as an IOException or independently as itself.
919      * @exception IOException  If an I/O error occurs while either sending a
920      *      command to the server or receiving a reply from the server.
921      ***/
922     public NewsgroupInfo[] listNewsgroups() throws IOException
923     {
924         if (!NNTPReply.isPositiveCompletion(list()))
925             return null;
926 
927         return __readNewsgroupListing();
928     }
929 
930     /**
931      * An overloaded listNewsgroups() command that allows us to
932      * specify with a pattern what groups we want to list. Wraps the
933      * LIST ACTIVE command.
934      * <p>
935      * @param wildmat a pseudo-regex pattern (cf. RFC 2980)
936      * @return An array of NewsgroupInfo instances containing the information
937      *    for each newsgroup served by the NNTP server corresponding to the
938      *    supplied pattern.   If no such newsgroups are served, a zero length
939      *    array will be returned.  If the command fails, null will be returned.
940      * @throws IOException
941      */
942     public NewsgroupInfo[] listNewsgroups(String wildmat) throws IOException
943     {
944         if(!NNTPReply.isPositiveCompletion(listActive(wildmat)))
945             return null;
946         return __readNewsgroupListing();
947     }
948 
949 
950     /***
951      * List all new newsgroups added to the NNTP server since a particular
952      * date subject to the conditions of the specified query.  If no new
953      * newsgroups were added, a zero length array will be returned.  If the
954      * command fails, null will be returned.
955      * <p>
956      * @param query  The query restricting how to search for new newsgroups.
957      * @return An array of NewsgroupInfo instances containing the information
958      *    for each new newsgroup added to the NNTP server.   If no newsgroups
959      *    were added, a zero length array will be returned.  If the command
960      *    fails, null will be returned.
961      * @exception NNTPConnectionClosedException
962      *      If the NNTP server prematurely closes the connection as a result
963      *      of the client being idle or some other reason causing the server
964      *      to send NNTP reply code 400.  This exception may be caught either
965      *      as an IOException or independently as itself.
966      * @exception IOException  If an I/O error occurs while either sending a
967      *      command to the server or receiving a reply from the server.
968      ***/
969     public NewsgroupInfo[] listNewNewsgroups(NewGroupsOrNewsQuery query)
970     throws IOException
971     {
972         if (!NNTPReply.isPositiveCompletion(newgroups(
973                                                 query.getDate(), query.getTime(),
974                                                 query.isGMT(), query.getDistributions())))
975             return null;
976 
977         return __readNewsgroupListing();
978     }
979 
980 
981     /***
982      * List all new articles added to the NNTP server since a particular
983      * date subject to the conditions of the specified query.  If no new
984      * new news is found, a zero length array will be returned.  If the
985      * command fails, null will be returned.  You must add at least one
986      * newsgroup to the query, else the command will fail.  Each String
987      * in the returned array is a unique message identifier including the
988      * enclosing &lt and &gt.
989      * <p>
990      * @param query  The query restricting how to search for new news.  You
991      *    must add at least one newsgroup to the query.
992      * @return An array of String instances containing the unique message
993      *    identifiers for each new article added to the NNTP server.  If no
994      *    new news is found, a zero length array will be returned.  If the
995      *    command fails, null will be returned.
996      * @exception NNTPConnectionClosedException
997      *      If the NNTP server prematurely closes the connection as a result
998      *      of the client being idle or some other reason causing the server
999      *      to send NNTP reply code 400.  This exception may be caught either
1000      *      as an IOException or independently as itself.
1001      * @exception IOException  If an I/O error occurs while either sending a
1002      *      command to the server or receiving a reply from the server.
1003      ***/
1004     public String[] listNewNews(NewGroupsOrNewsQuery query)
1005     throws IOException
1006     {
1007         int size;
1008         String line;
1009         Vector list;
1010         String[] result;
1011         BufferedReader reader;
1012 
1013         if (!NNTPReply.isPositiveCompletion(newnews(
1014                                                 query.getNewsgroups(), query.getDate(), query.getTime(),
1015                                                 query.isGMT(), query.getDistributions())))
1016             return null;
1017 
1018         list = new Vector();
1019         reader = new BufferedReader(new DotTerminatedMessageReader(_reader_));
1020 
1021         while ((line = reader.readLine()) != null)
1022             list.addElement(line);
1023 
1024         size = list.size();
1025 
1026         if (size < 1)
1027             return new String[0];
1028 
1029         result = new String[size];
1030         list.copyInto(result);
1031 
1032         return result;
1033     }
1034 
1035     /***
1036      * There are a few NNTPClient methods that do not complete the
1037      * entire sequence of NNTP commands to complete a transaction.  These
1038      * commands require some action by the programmer after the reception
1039      * of a positive preliminary command.  After the programmer's code
1040      * completes its actions, it must call this method to receive
1041      * the completion reply from the server and verify the success of the
1042      * entire transaction.
1043      * <p>
1044      * For example
1045      * <pre>
1046      * writer = client.postArticle();
1047      * if(writer == null) // failure
1048      *   return false;
1049      * header = new SimpleNNTPHeader("foobar@foo.com", "Just testing");
1050      * header.addNewsgroup("alt.test");
1051      * writer.write(header.toString());
1052      * writer.write("This is just a test");
1053      * writer.close();
1054      * if(!client.completePendingCommand()) // failure
1055      *   return false;
1056      * </pre>
1057      * <p>
1058      * @return True if successfully completed, false if not.
1059      * @exception NNTPConnectionClosedException
1060      *      If the NNTP server prematurely closes the connection as a result
1061      *      of the client being idle or some other reason causing the server
1062      *      to send NNTP reply code 400.  This exception may be caught either
1063      *      as an IOException or independently as itself.
1064      * @exception IOException  If an I/O error occurs while either sending a
1065      *      command to the server or receiving a reply from the server.
1066      ***/
1067     public boolean completePendingCommand() throws IOException
1068     {
1069         return NNTPReply.isPositiveCompletion(getReply());
1070     }
1071 
1072     /***
1073      * Post an article to the NNTP server.  This method returns a
1074      * DotTerminatedMessageWriter instance to which the article can be
1075      * written.  Null is returned if the posting attempt fails.  You
1076      * should check {@link NNTP#isAllowedToPost isAllowedToPost() }
1077      *  before trying to post.  However, a posting
1078      * attempt can fail due to malformed headers.
1079      * <p>
1080      * You must not issue any commands to the NNTP server (i.e., call any
1081      * (other methods) until you finish writing to the returned Writer
1082      * instance and close it.  The NNTP protocol uses the same stream for
1083      * issuing commands as it does for returning results.  Therefore the
1084      * returned Writer actually writes directly to the NNTP connection.
1085      * After you close the writer, you can execute new commands.  If you
1086      * do not follow these requirements your program will not work properly.
1087      * <p>
1088      * Different NNTP servers will require different header formats, but
1089      * you can use the provided
1090      * {@link org.apache.commons.net.nntp.SimpleNNTPHeader}
1091      * class to construct the bare minimum acceptable header for most
1092      * news readers.  To construct more complicated headers you should
1093      * refer to RFC 822.  When the Java Mail API is finalized, you will be
1094      * able to use it to compose fully compliant Internet text messages.
1095      * The DotTerminatedMessageWriter takes care of doubling line-leading
1096      * dots and ending the message with a single dot upon closing, so all
1097      * you have to worry about is writing the header and the message.
1098      * <p>
1099      * Upon closing the returned Writer, you need to call
1100      * {@link #completePendingCommand  completePendingCommand() }
1101      * to finalize the posting and verify its success or failure from
1102      * the server reply.
1103      * <p>
1104      * @return A DotTerminatedMessageWriter to which the article (including
1105      *      header) can be written.  Returns null if the command fails.
1106      * @exception IOException  If an I/O error occurs while either sending a
1107      *      command to the server or receiving a reply from the server.
1108      ***/
1109 
1110     public Writer postArticle() throws IOException
1111     {
1112         if (!NNTPReply.isPositiveIntermediate(post()))
1113             return null;
1114 
1115         return new DotTerminatedMessageWriter(_writer_);
1116     }
1117 
1118 
1119     public Writer forwardArticle(String articleId) throws IOException
1120     {
1121         if (!NNTPReply.isPositiveIntermediate(ihave(articleId)))
1122             return null;
1123 
1124         return new DotTerminatedMessageWriter(_writer_);
1125     }
1126 
1127 
1128     /***
1129      * Logs out of the news server gracefully by sending the QUIT command.
1130      * However, you must still disconnect from the server before you can open
1131      * a new connection.
1132      * <p>
1133      * @return True if successfully completed, false if not.
1134      * @exception IOException  If an I/O error occurs while either sending a
1135      *      command to the server or receiving a reply from the server.
1136      ***/
1137     public boolean logout() throws IOException
1138     {
1139         return NNTPReply.isPositiveCompletion(quit());
1140     }
1141 
1142 
1143     /**
1144      * Log into a news server by sending the AUTHINFO USER/AUTHINFO
1145      * PASS command sequence. This is usually sent in response to a
1146      * 480 reply code from the NNTP server.
1147      * <p>
1148      * @param username a valid username
1149      * @param password the corresponding password
1150      * @return True for successful login, false for a failure
1151      * @throws IOException
1152      */
1153     public boolean authenticate(String username, String password)
1154         throws IOException
1155     {
1156         int replyCode = authinfoUser(username);
1157 
1158         if (replyCode == NNTPReply.MORE_AUTH_INFO_REQUIRED)
1159             {
1160                 replyCode = authinfoPass(password);
1161 
1162                 if (replyCode == NNTPReply.AUTHENTICATION_ACCEPTED)
1163                     {
1164                         _isAllowedToPost = true;
1165                         return true;
1166                     }
1167             }
1168         return false;
1169     }
1170 
1171     /***
1172      * Private implementation of XOVER functionality.
1173      *
1174      * See {@link NNTP#xover}
1175      * for legal agument formats. Alternatively, read RFC 2980 :-)
1176      * <p>
1177      * @param articleRange
1178      * @return Returns a DotTerminatedMessageReader if successful, null
1179      *         otherwise
1180      * @exception IOException
1181      */
1182     private Reader __retrieveArticleInfo(String articleRange)
1183         throws IOException
1184     {
1185         if (!NNTPReply.isPositiveCompletion(xover(articleRange)))
1186             return null;
1187 
1188         return new DotTerminatedMessageReader(_reader_);
1189     }
1190 
1191     /**
1192      * Return article headers for a specified post.
1193      * <p>
1194      * @param articleNumber the article to retrieve headers for
1195      * @return a DotTerminatedReader if successful, null otherwise
1196      * @throws IOException
1197      */
1198     public Reader retrieveArticleInfo(int articleNumber) throws IOException
1199     {
1200         return __retrieveArticleInfo(Integer.toString(articleNumber));
1201     }
1202 
1203     /**
1204      * Return article headers for all articles between lowArticleNumber
1205      * and highArticleNumber, inclusively.
1206      * <p>
1207      * @param lowArticleNumber
1208      * @param highArticleNumber
1209      * @return a DotTerminatedReader if successful, null otherwise
1210      * @throws IOException
1211      */
1212     public Reader retrieveArticleInfo(int lowArticleNumber,
1213                                       int highArticleNumber)
1214         throws IOException
1215     {
1216         return
1217             __retrieveArticleInfo(new String(lowArticleNumber + "-" +
1218                                              highArticleNumber));
1219     }
1220 
1221     /***
1222      * Private implementation of XHDR functionality.
1223      *
1224      * See {@link NNTP#xhdr}
1225      * for legal agument formats. Alternatively, read RFC 1036.
1226      * <p>
1227      * @param header
1228      * @param articleRange
1229      * @return Returns a DotTerminatedMessageReader if successful, null
1230      *         otherwise
1231      * @exception IOException
1232      */
1233     private Reader __retrieveHeader(String header, String articleRange)
1234         throws IOException
1235     {
1236         if (!NNTPReply.isPositiveCompletion(xhdr(header, articleRange)))
1237             return null;
1238 
1239         return new DotTerminatedMessageReader(_reader_);
1240     }
1241 
1242     /**
1243      * Return an article header for a specified post.
1244      * <p>
1245      * @param header the header to retrieve
1246      * @param articleNumber the article to retrieve the header for
1247      * @return a DotTerminatedReader if successful, null otherwise
1248      * @throws IOException
1249      */
1250     public Reader retrieveHeader(String header, int articleNumber)
1251         throws IOException
1252     {
1253         return __retrieveHeader(header, Integer.toString(articleNumber));
1254     }
1255 
1256     /**
1257      * Return an article header for all articles between lowArticleNumber
1258      * and highArticleNumber, inclusively.
1259      * <p>
1260      * @param header
1261      * @param lowArticleNumber
1262      * @param highArticleNumber
1263      * @return a DotTerminatedReader if successful, null otherwise
1264      * @throws IOException
1265      */
1266     public Reader retrieveHeader(String header, int lowArticleNumber,
1267                                  int highArticleNumber)
1268         throws IOException
1269     {
1270         return
1271             __retrieveHeader(header,
1272                              new String(lowArticleNumber + "-" +
1273                                         highArticleNumber));
1274     }
1275 }
1276 
1277 
1278 /* Emacs configuration
1279  * Local variables:        **
1280  * mode:             java  **
1281  * c-basic-offset:   4     **
1282  * indent-tabs-mode: nil   **
1283  * End:                    **
1284  */