001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.commons.net.nntp;
019
020 import java.io.BufferedReader;
021 import java.io.IOException;
022 import java.io.Reader;
023 import java.io.StringWriter;
024 import java.io.Writer;
025 import java.util.StringTokenizer;
026 import java.util.Vector;
027
028 import org.apache.commons.net.MalformedServerReplyException;
029 import org.apache.commons.net.io.DotTerminatedMessageReader;
030 import org.apache.commons.net.io.DotTerminatedMessageWriter;
031 import org.apache.commons.net.io.Util;
032
033 /***
034 * NNTPClient encapsulates all the functionality necessary to post and
035 * retrieve articles from an NNTP server. As with all classes derived
036 * from {@link org.apache.commons.net.SocketClient},
037 * you must first connect to the server with
038 * {@link org.apache.commons.net.SocketClient#connect connect }
039 * before doing anything, and finally
040 * {@link org.apache.commons.net.nntp.NNTP#disconnect disconnect() }
041 * after you're completely finished interacting with the server.
042 * Remember that the
043 * {@link org.apache.commons.net.nntp.NNTP#isAllowedToPost isAllowedToPost()}
044 * method is defined in
045 * {@link org.apache.commons.net.nntp.NNTP}.
046 * <p>
047 * You should keep in mind that the NNTP server may choose to prematurely
048 * close a connection if the client has been idle for longer than a
049 * given time period or if the server is being shutdown by the operator or
050 * some other reason. The NNTP class will detect a
051 * premature NNTP server connection closing when it receives a
052 * {@link org.apache.commons.net.nntp.NNTPReply#SERVICE_DISCONTINUED NNTPReply.SERVICE_DISCONTINUED }
053 * response to a command.
054 * When that occurs, the NNTP class method encountering that reply will throw
055 * an {@link org.apache.commons.net.nntp.NNTPConnectionClosedException}
056 * .
057 * <code>NNTPConectionClosedException</code>
058 * is a subclass of <code> IOException </code> and therefore need not be
059 * caught separately, but if you are going to catch it separately, its
060 * catch block must appear before the more general <code> IOException </code>
061 * catch block. When you encounter an
062 * {@link org.apache.commons.net.nntp.NNTPConnectionClosedException}
063 * , you must disconnect the connection with
064 * {@link org.apache.commons.net.nntp.NNTP#disconnect disconnect() }
065 * to properly clean up the
066 * system resources used by NNTP. Before disconnecting, you may check the
067 * last reply code and text with
068 * {@link org.apache.commons.net.nntp.NNTP#getReplyCode getReplyCode } and
069 * {@link org.apache.commons.net.nntp.NNTP#getReplyString getReplyString }.
070 * <p>
071 * Rather than list it separately for each method, we mention here that
072 * every method communicating with the server and throwing an IOException
073 * can also throw a
074 * {@link org.apache.commons.net.MalformedServerReplyException}
075 * , which is a subclass
076 * of IOException. A MalformedServerReplyException will be thrown when
077 * the reply received from the server deviates enough from the protocol
078 * specification that it cannot be interpreted in a useful manner despite
079 * attempts to be as lenient as possible.
080 * <p>
081 * <p>
082 * @author Daniel F. Savarese
083 * @author Rory Winston
084 * @author Ted Wise
085 * @see NNTP
086 * @see NNTPConnectionClosedException
087 * @see org.apache.commons.net.MalformedServerReplyException
088 ***/
089
090 public class NNTPClient extends NNTP
091 {
092
093 private void __parseArticlePointer(String reply, ArticlePointer pointer)
094 throws MalformedServerReplyException
095 {
096 StringTokenizer tokenizer;
097
098 // Do loop is a kluge to simulate goto
099 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 < and >).
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 < and >).
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 < and >).
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 * < and >) 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 < and >.
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 */