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