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