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