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