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 */