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