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