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