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