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