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