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