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; 032import org.apache.commons.net.util.NetConstants; 033 034/** 035 * NNTPClient encapsulates all the functionality necessary to post and 036 * retrieve articles from an NNTP server. As with all classes derived 037 * from {@link org.apache.commons.net.SocketClient}, 038 * you must first connect to the server with 039 * {@link org.apache.commons.net.SocketClient#connect connect } 040 * before doing anything, and finally 041 * {@link org.apache.commons.net.nntp.NNTP#disconnect disconnect() } 042 * after you're completely finished interacting with the server. 043 * Remember that the 044 * {@link org.apache.commons.net.nntp.NNTP#isAllowedToPost isAllowedToPost()} 045 * method is defined in 046 * {@link org.apache.commons.net.nntp.NNTP}. 047 * <p> 048 * You should keep in mind that the NNTP server may choose to prematurely 049 * close a connection if the client has been idle for longer than a 050 * given time period or if the server is being shutdown by the operator or 051 * some other reason. The NNTP class will detect a 052 * premature NNTP server connection closing when it receives a 053 * {@link org.apache.commons.net.nntp.NNTPReply#SERVICE_DISCONTINUED NNTPReply.SERVICE_DISCONTINUED } 054 * response to a command. 055 * When that occurs, the NNTP class method encountering that reply will throw 056 * an {@link org.apache.commons.net.nntp.NNTPConnectionClosedException} 057 * . 058 * <code>NNTPConectionClosedException</code> 059 * is a subclass of <code> IOException </code> and therefore need not be 060 * caught separately, but if you are going to catch it separately, its 061 * catch block must appear before the more general <code> IOException </code> 062 * catch block. When you encounter an 063 * {@link org.apache.commons.net.nntp.NNTPConnectionClosedException} 064 * , you must disconnect the connection with 065 * {@link org.apache.commons.net.nntp.NNTP#disconnect disconnect() } 066 * to properly clean up the 067 * system resources used by NNTP. Before disconnecting, you may check the 068 * last reply code and text with 069 * {@link org.apache.commons.net.nntp.NNTP#getReplyCode getReplyCode } and 070 * {@link org.apache.commons.net.nntp.NNTP#getReplyString getReplyString }. 071 * <p> 072 * Rather than list it separately for each method, we mention here that 073 * every method communicating with the server and throwing an IOException 074 * can also throw a 075 * {@link org.apache.commons.net.MalformedServerReplyException} 076 * , which is a subclass 077 * of IOException. A MalformedServerReplyException will be thrown when 078 * the reply received from the server deviates enough from the protocol 079 * specification that it cannot be interpreted in a useful manner despite 080 * attempts to be as lenient as possible. 081 * 082 * @see NNTP 083 * @see NNTPConnectionClosedException 084 * @see org.apache.commons.net.MalformedServerReplyException 085 */ 086 087public class NNTPClient extends NNTP 088{ 089 090 private static final NewsgroupInfo[] EMPTY_NEWSGROUP_INFO_ARRAY = new NewsgroupInfo[0]; 091 092 /** 093 * Parse the reply and store the id and number in the pointer. 094 * 095 * @param reply the reply to parse "22n nnn <aaa>" 096 * @param pointer the pointer to update 097 * 098 * @throws MalformedServerReplyException if response could not be parsed 099 */ 100 private void parseArticlePointer(final String reply, final ArticleInfo pointer) 101 throws MalformedServerReplyException 102 { 103 final String tokens[] = reply.split(" "); 104 if (tokens.length >= 3) { // OK, we can parset the line 105 int i = 1; // skip reply code 106 try 107 { 108 // Get article number 109 pointer.articleNumber = Long.parseLong(tokens[i++]); 110 // Get article id 111 pointer.articleId = tokens[i++]; 112 return; // done 113 } 114 catch (final NumberFormatException e) 115 { 116 // drop through and raise exception 117 } 118 } 119 throw new MalformedServerReplyException( 120 "Could not parse article pointer.\nServer reply: " + reply); 121 } 122 123 /* 124 * 211 n f l s group selected 125 * (n = estimated number of articles in group, 126 * f = first article number in the group, 127 * l = last article number in the group, 128 * s = name of the group.) 129 */ 130 131 private static void parseGroupReply(final String reply, final NewsgroupInfo info) 132 throws MalformedServerReplyException 133 { 134 final String tokens[] = reply.split(" "); 135 if (tokens.length >= 5) { 136 int i = 1; // Skip numeric response value 137 try 138 { 139 // Get estimated article count 140 info.setArticleCount(Long.parseLong(tokens[i++])); 141 // Get first article number 142 info.setFirstArticle(Long.parseLong(tokens[i++])); 143 // Get last article number 144 info.setLastArticle(Long.parseLong(tokens[i++])); 145 // Get newsgroup name 146 info.setNewsgroup(tokens[i++]); 147 148 info.setPostingPermission(NewsgroupInfo.UNKNOWN_POSTING_PERMISSION); 149 return ; 150 } catch (final NumberFormatException e) 151 { 152 // drop through to report error 153 } 154 } 155 156 throw new MalformedServerReplyException( 157 "Could not parse newsgroup info.\nServer reply: " + reply); 158 } 159 160 161 // Format: group last first p 162 static NewsgroupInfo parseNewsgroupListEntry(final String entry) 163 { 164 final String tokens[] = entry.split(" "); 165 if (tokens.length < 4) { 166 return null; 167 } 168 final NewsgroupInfo result = new NewsgroupInfo(); 169 170 int i = 0; 171 172 result.setNewsgroup(tokens[i++]); 173 174 try 175 { 176 final long lastNum = Long.parseLong(tokens[i++]); 177 final long firstNum = Long.parseLong(tokens[i++]); 178 result.setFirstArticle(firstNum); 179 result.setLastArticle(lastNum); 180 if ((firstNum == 0) && (lastNum == 0)) { 181 result.setArticleCount(0); 182 } else { 183 result.setArticleCount(lastNum - firstNum + 1); 184 } 185 } catch (final NumberFormatException e) { 186 return null; 187 } 188 189 switch (tokens[i++].charAt(0)) 190 { 191 case 'y': 192 case 'Y': 193 result.setPostingPermission( 194 NewsgroupInfo.PERMITTED_POSTING_PERMISSION); 195 break; 196 case 'n': 197 case 'N': 198 result.setPostingPermission( 199 NewsgroupInfo.PROHIBITED_POSTING_PERMISSION); 200 break; 201 case 'm': 202 case 'M': 203 result.setPostingPermission( 204 NewsgroupInfo.MODERATED_POSTING_PERMISSION); 205 break; 206 default: 207 result.setPostingPermission( 208 NewsgroupInfo.UNKNOWN_POSTING_PERMISSION); 209 break; 210 } 211 212 return result; 213 } 214 215 /** 216 * Parse a response line from {@link #retrieveArticleInfo(long, long)}. 217 * 218 * @param line a response line 219 * @return the parsed {@link Article}, if unparseable then isDummy() 220 * will be true, and the subject will contain the raw info. 221 * @since 3.0 222 */ 223 static Article parseArticleEntry(final String line) { 224 // Extract the article information 225 // Mandatory format (from NNTP RFC 2980) is : 226 // articleNumber\tSubject\tAuthor\tDate\tID\tReference(s)\tByte Count\tLine Count 227 228 final Article article = new Article(); 229 article.setSubject(line); // in case parsing fails 230 final String parts[] = line.split("\t"); 231 if (parts.length > 6) { 232 int i = 0; 233 try { 234 article.setArticleNumber(Long.parseLong(parts[i++])); 235 article.setSubject(parts[i++]); 236 article.setFrom(parts[i++]); 237 article.setDate(parts[i++]); 238 article.setArticleId(parts[i++]); 239 article.addReference(parts[i++]); 240 } catch (final NumberFormatException e) { 241 // ignored, already handled 242 } 243 } 244 return article; 245 } 246 247 private NewsgroupInfo[] readNewsgroupListing() throws IOException 248 { 249 250 // Start of with a big vector because we may be reading a very large 251 // amount of groups. 252 final Vector<NewsgroupInfo> list = new Vector<>(2048); 253 254 String line; 255 try (final BufferedReader reader = new DotTerminatedMessageReader(_reader_)) { 256 while ((line = reader.readLine()) != null) { 257 final NewsgroupInfo tmp = parseNewsgroupListEntry(line); 258 if (tmp != null) { 259 list.addElement(tmp); 260 } else { 261 throw new MalformedServerReplyException(line); 262 } 263 } 264 } 265 final int size; 266 if ((size = list.size()) < 1) { 267 return EMPTY_NEWSGROUP_INFO_ARRAY; 268 } 269 270 final NewsgroupInfo[] info = new NewsgroupInfo[size]; 271 list.copyInto(info); 272 273 return info; 274 } 275 276 277 private BufferedReader retrieve(final int command, final String articleId, final 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(final int command, final long articleNumber, final 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 * @throws 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 * @throws 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(final String articleId, final 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(final 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 * @throws 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 * @throws 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(final long articleNumber, final 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(final 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 * @throws 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 * @throws 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(final String articleId, final 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(final 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 * @throws 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 * @throws 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(final long articleNumber, 559 final 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(final 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 * @throws 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 * @throws 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(final String articleId, final 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(final 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 * @throws 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 * @throws 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(final long articleNumber, 692 final 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(final 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 * @throws 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 * @throws 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(final String newsgroup, final 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(final 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 * @throws 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 * @throws 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 try (final StringWriter help = new StringWriter(); 773 final BufferedReader reader = new DotTerminatedMessageReader(_reader_)) { 774 Util.copyReader(reader, help); 775 return help.toString(); 776 } 777 } 778 779 /** 780 * Send a "LIST OVERVIEW.FMT" command to the server. 781 * 782 * @return the contents of the Overview format, of {@code null} if the command failed 783 * @throws IOException on error 784 */ 785 public String[] listOverviewFmt() throws IOException 786 { 787 if (!NNTPReply.isPositiveCompletion(sendCommand("LIST", "OVERVIEW.FMT"))) { 788 return null; 789 } 790 791 try (final BufferedReader reader = new DotTerminatedMessageReader(_reader_)) { 792 String line; 793 final ArrayList<String> list = new ArrayList<>(); 794 while ((line = reader.readLine()) != null) { 795 list.add(line); 796 } 797 return list.toArray(NetConstants.EMPTY_STRING_ARRAY); 798 } 799 } 800 801 /** 802 * Select an article by its unique identifier (including enclosing 803 * < and >) and return its article number and id through the 804 * pointer parameter. This is achieved through the STAT command. 805 * According to RFC 977, this will NOT set the current article pointer 806 * on the server. To do that, you must reference the article by its 807 * number. 808 * <p> 809 * @param articleId The unique article identifier of the article that 810 * is being selectedd. If this parameter is null, the 811 * body of the current article is selected 812 * @param pointer A parameter through which to return the article's 813 * number and unique id. The articleId field cannot always be trusted 814 * because of server deviations from RFC 977 reply formats. You may 815 * set this parameter to null if you do not desire to retrieve the 816 * returned article information. 817 * @return True if successful, false if not. 818 * @throws NNTPConnectionClosedException 819 * If the NNTP server prematurely closes the connection as a result 820 * of the client being idle or some other reason causing the server 821 * to send NNTP reply code 400. This exception may be caught either 822 * as an IOException or independently as itself. 823 * @throws IOException If an I/O error occurs while either sending a 824 * command to the server or receiving a reply from the server. 825 */ 826 public boolean selectArticle(final String articleId, final ArticleInfo pointer) 827 throws IOException 828 { 829 if (articleId != null) { 830 if (!NNTPReply.isPositiveCompletion(stat(articleId))) { 831 return false; 832 } 833 } else { 834 if (!NNTPReply.isPositiveCompletion(stat())) { 835 return false; 836 } 837 } 838 839 if (pointer != null) { 840 parseArticlePointer(getReplyString(), pointer); 841 } 842 843 return true; 844 } 845 846 /** 847 * Same as <code> selectArticle(articleId, (ArticleInfo) null) </code> 848 * @param articleId the article Id 849 * @return true if successful 850 * @throws IOException on error 851 */ 852 public boolean selectArticle(final String articleId) throws IOException 853 { 854 return selectArticle(articleId, (ArticleInfo) null); 855 } 856 857 /*** 858 * Same as <code> selectArticle((String) null, articleId) </code>. Useful 859 * for retrieving the current article number. 860 * @param pointer to the article 861 * @return true if OK 862 * @throws IOException on error 863 */ 864 public boolean selectArticle(final ArticleInfo pointer) throws IOException 865 { 866 return selectArticle(null, pointer); 867 } 868 869 870 /** 871 * Select an article in the currently selected newsgroup by its number. 872 * and return its article number and id through the 873 * pointer parameter. This is achieved through the STAT command. 874 * According to RFC 977, this WILL set the current article pointer 875 * on the server. Use this command to select an article before retrieving 876 * it, or to obtain an article's unique identifier given its number. 877 * <p> 878 * @param articleNumber The number of the article to select from the 879 * currently selected newsgroup. 880 * @param pointer A parameter through which to return the article's 881 * number and unique id. Although the articleId field cannot always 882 * be trusted because of server deviations from RFC 977 reply formats, 883 * we haven't found a server that misformats this information in response 884 * to this particular command. You may set this parameter to null if 885 * you do not desire to retrieve the returned article information. 886 * @return True if successful, false if not. 887 * @throws NNTPConnectionClosedException 888 * If the NNTP server prematurely closes the connection as a result 889 * of the client being idle or some other reason causing the server 890 * to send NNTP reply code 400. This exception may be caught either 891 * as an IOException or independently as itself. 892 * @throws IOException If an I/O error occurs while either sending a 893 * command to the server or receiving a reply from the server. 894 */ 895 public boolean selectArticle(final long articleNumber, final ArticleInfo pointer) 896 throws IOException 897 { 898 if (!NNTPReply.isPositiveCompletion(stat(articleNumber))) { 899 return false; 900 } 901 902 if (pointer != null) { 903 parseArticlePointer(getReplyString(), pointer); 904 } 905 906 return true; 907 } 908 909 910 /** Same as <code> selectArticle(articleNumber, null) </code> 911 * @param articleNumber the numger 912 * @return true if successful 913 * @throws IOException on error */ 914 public boolean selectArticle(final long articleNumber) throws IOException 915 { 916 return selectArticle(articleNumber, null); 917 } 918 919 920 /** 921 * Select the article preceeding the currently selected article in the 922 * currently selected newsgroup and return its number and unique id 923 * through the pointer parameter. Because of deviating server 924 * implementations, the articleId information cannot be trusted. To 925 * obtain the article identifier, issue a 926 * <code> selectArticle(pointer.articleNumber, pointer) </code> immediately 927 * afterward. 928 * <p> 929 * @param pointer A parameter through which to return the article's 930 * number and unique id. The articleId field cannot always be trusted 931 * because of server deviations from RFC 977 reply formats. You may 932 * set this parameter to null if you do not desire to retrieve the 933 * returned article information. 934 * @return True if successful, false if not (e.g., there is no previous 935 * article). 936 * @throws NNTPConnectionClosedException 937 * If the NNTP server prematurely closes the connection as a result 938 * of the client being idle or some other reason causing the server 939 * to send NNTP reply code 400. This exception may be caught either 940 * as an IOException or independently as itself. 941 * @throws IOException If an I/O error occurs while either sending a 942 * command to the server or receiving a reply from the server. 943 */ 944 public boolean selectPreviousArticle(final ArticleInfo pointer) 945 throws IOException 946 { 947 if (!NNTPReply.isPositiveCompletion(last())) { 948 return false; 949 } 950 951 if (pointer != null) { 952 parseArticlePointer(getReplyString(), pointer); 953 } 954 955 return true; 956 } 957 958 /** Same as <code> selectPreviousArticle((ArticleInfo) null) </code> 959 * @return true if successful 960 * @throws IOException on error */ 961 public boolean selectPreviousArticle() throws IOException 962 { 963 return selectPreviousArticle((ArticleInfo) null); 964 } 965 966 967 /** 968 * Select the article following the currently selected article in the 969 * currently selected newsgroup and return its number and unique id 970 * through the pointer parameter. Because of deviating server 971 * implementations, the articleId information cannot be trusted. To 972 * obtain the article identifier, issue a 973 * <code> selectArticle(pointer.articleNumber, pointer) </code> immediately 974 * afterward. 975 * <p> 976 * @param pointer A parameter through which to return the article's 977 * number and unique id. The articleId field cannot always be trusted 978 * because of server deviations from RFC 977 reply formats. You may 979 * set this parameter to null if you do not desire to retrieve the 980 * returned article information. 981 * @return True if successful, false if not (e.g., there is no following 982 * article). 983 * @throws NNTPConnectionClosedException 984 * If the NNTP server prematurely closes the connection as a result 985 * of the client being idle or some other reason causing the server 986 * to send NNTP reply code 400. This exception may be caught either 987 * as an IOException or independently as itself. 988 * @throws IOException If an I/O error occurs while either sending a 989 * command to the server or receiving a reply from the server. 990 */ 991 public boolean selectNextArticle(final ArticleInfo pointer) throws IOException 992 { 993 if (!NNTPReply.isPositiveCompletion(next())) { 994 return false; 995 } 996 997 if (pointer != null) { 998 parseArticlePointer(getReplyString(), pointer); 999 } 1000 1001 return true; 1002 } 1003 1004 1005 /** Same as <code> selectNextArticle((ArticleInfo) null) </code> 1006 * @return true if successful 1007 * @throws IOException on error */ 1008 public boolean selectNextArticle() throws IOException 1009 { 1010 return selectNextArticle((ArticleInfo) null); 1011 } 1012 1013 1014 /** 1015 * List all newsgroups served by the NNTP server. If no newsgroups 1016 * are served, a zero length array will be returned. If the command 1017 * fails, null will be returned. 1018 * The method uses the "LIST" command. 1019 * <p> 1020 * @return An array of NewsgroupInfo instances containing the information 1021 * for each newsgroup served by the NNTP server. If no newsgroups 1022 * are served, a zero length array will be returned. If the command 1023 * fails, null will be returned. 1024 * @throws NNTPConnectionClosedException 1025 * If the NNTP server prematurely closes the connection as a result 1026 * of the client being idle or some other reason causing the server 1027 * to send NNTP reply code 400. This exception may be caught either 1028 * as an IOException or independently as itself. 1029 * @throws IOException If an I/O error occurs while either sending a 1030 * command to the server or receiving a reply from the server. 1031 * @see #iterateNewsgroupListing() 1032 * @see #iterateNewsgroups() 1033 */ 1034 public NewsgroupInfo[] listNewsgroups() throws IOException 1035 { 1036 if (!NNTPReply.isPositiveCompletion(list())) { 1037 return null; 1038 } 1039 1040 return readNewsgroupListing(); 1041 } 1042 1043 /** 1044 * List all newsgroups served by the NNTP server. If no newsgroups 1045 * are served, no entries will be returned. 1046 * The method uses the "LIST" command. 1047 * <p> 1048 * @return An iterable of NewsgroupInfo instances containing the information 1049 * for each newsgroup served by the NNTP server. If no newsgroups 1050 * are served, no entries will be returned. 1051 * @throws NNTPConnectionClosedException 1052 * If the NNTP server prematurely closes the connection as a result 1053 * of the client being idle or some other reason causing the server 1054 * to send NNTP reply code 400. This exception may be caught either 1055 * as an IOException or independently as itself. 1056 * @throws IOException If an I/O error occurs while either sending a 1057 * command to the server or receiving a reply from the server. 1058 * @since 3.0 1059 */ 1060 public Iterable<String> iterateNewsgroupListing() throws IOException { 1061 if (NNTPReply.isPositiveCompletion(list())) { 1062 return new ReplyIterator(_reader_); 1063 } 1064 throw new IOException("LIST command failed: "+getReplyString()); 1065 } 1066 1067 /** 1068 * List all newsgroups served by the NNTP server. If no newsgroups 1069 * are served, no entries will be returned. 1070 * The method uses the "LIST" command. 1071 * <p> 1072 * @return An iterable of Strings containing the raw information 1073 * for each newsgroup served by the NNTP server. If no newsgroups 1074 * are served, no entries will be returned. 1075 * @throws NNTPConnectionClosedException 1076 * If the NNTP server prematurely closes the connection as a result 1077 * of the client being idle or some other reason causing the server 1078 * to send NNTP reply code 400. This exception may be caught either 1079 * as an IOException or independently as itself. 1080 * @throws IOException If an I/O error occurs while either sending a 1081 * command to the server or receiving a reply from the server. 1082 * @since 3.0 1083 */ 1084 public Iterable<NewsgroupInfo> iterateNewsgroups() throws IOException { 1085 return new NewsgroupIterator(iterateNewsgroupListing()); 1086 } 1087 1088 /** 1089 * List the newsgroups that match a given pattern. 1090 * Uses the "LIST ACTIVE" command. 1091 * <p> 1092 * @param wildmat a pseudo-regex pattern (cf. RFC 2980) 1093 * @return An array of NewsgroupInfo instances containing the information 1094 * for each newsgroup served by the NNTP server corresponding to the 1095 * supplied pattern. If no such newsgroups are served, a zero length 1096 * array will be returned. If the command fails, null will be returned. 1097 * @throws IOException on error 1098 * @see #iterateNewsgroupListing(String) 1099 * @see #iterateNewsgroups(String) 1100 */ 1101 public NewsgroupInfo[] listNewsgroups(final String wildmat) throws IOException 1102 { 1103 if(!NNTPReply.isPositiveCompletion(listActive(wildmat))) { 1104 return null; 1105 } 1106 return readNewsgroupListing(); 1107 } 1108 1109 1110 /** 1111 * List the newsgroups that match a given pattern. 1112 * Uses the "LIST ACTIVE" command. 1113 * <p> 1114 * @param wildmat a pseudo-regex pattern (cf. RFC 2980) 1115 * @return An iterable of Strings containing the raw information 1116 * for each newsgroup served by the NNTP server corresponding to the 1117 * supplied pattern. If no such newsgroups are served, no entries 1118 * will be returned. 1119 * @throws IOException on error 1120 * @since 3.0 1121 */ 1122 public Iterable<String> iterateNewsgroupListing(final String wildmat) throws IOException { 1123 if(NNTPReply.isPositiveCompletion(listActive(wildmat))) { 1124 return new ReplyIterator(_reader_); 1125 } 1126 throw new IOException("LIST ACTIVE "+wildmat+" command failed: "+getReplyString()); 1127 } 1128 1129 /** 1130 * List the newsgroups that match a given pattern. 1131 * Uses the "LIST ACTIVE" command. 1132 * <p> 1133 * @param wildmat a pseudo-regex pattern (cf. RFC 2980) 1134 * @return An iterable NewsgroupInfo instances containing the information 1135 * for each newsgroup served by the NNTP server corresponding to the 1136 * supplied pattern. If no such newsgroups are served, no entries 1137 * will be returned. 1138 * @throws IOException on error 1139 * @since 3.0 1140 */ 1141 public Iterable<NewsgroupInfo> iterateNewsgroups(final String wildmat) throws IOException { 1142 return new NewsgroupIterator(iterateNewsgroupListing(wildmat)); 1143 } 1144 1145 /** 1146 * List all new newsgroups added to the NNTP server since a particular 1147 * date subject to the conditions of the specified query. If no new 1148 * newsgroups were added, a zero length array will be returned. If the 1149 * command fails, null will be returned. 1150 * This uses the "NEWGROUPS" command. 1151 * <p> 1152 * @param query The query restricting how to search for new newsgroups. 1153 * @return An array of NewsgroupInfo instances containing the information 1154 * for each new newsgroup added to the NNTP server. If no newsgroups 1155 * were added, a zero length array will be returned. If the command 1156 * fails, null will be returned. 1157 * @throws NNTPConnectionClosedException 1158 * If the NNTP server prematurely closes the connection as a result 1159 * of the client being idle or some other reason causing the server 1160 * to send NNTP reply code 400. This exception may be caught either 1161 * as an IOException or independently as itself. 1162 * @throws IOException If an I/O error occurs while either sending a 1163 * command to the server or receiving a reply from the server. 1164 * @see #iterateNewNewsgroups(NewGroupsOrNewsQuery) 1165 * @see #iterateNewNewsgroupListing(NewGroupsOrNewsQuery) 1166 */ 1167 public NewsgroupInfo[] listNewNewsgroups(final NewGroupsOrNewsQuery query) 1168 throws IOException 1169 { 1170 if (!NNTPReply.isPositiveCompletion(newgroups( 1171 query.getDate(), query.getTime(), 1172 query.isGMT(), query.getDistributions()))) 1173 { 1174 return null; 1175 } 1176 1177 return readNewsgroupListing(); 1178 } 1179 1180 /** 1181 * List all new newsgroups added to the NNTP server since a particular 1182 * date subject to the conditions of the specified query. If no new 1183 * newsgroups were added, no entries will be returned. 1184 * This uses the "NEWGROUPS" command. 1185 * <p> 1186 * @param query The query restricting how to search for new newsgroups. 1187 * @return An iterable of Strings containing the raw information 1188 * for each new newsgroup added to the NNTP server. If no newsgroups 1189 * were added, no entries will be returned. 1190 * @throws NNTPConnectionClosedException 1191 * If the NNTP server prematurely closes the connection as a result 1192 * of the client being idle or some other reason causing the server 1193 * to send NNTP reply code 400. This exception may be caught either 1194 * as an IOException or independently as itself. 1195 * @throws IOException If an I/O error occurs while either sending a 1196 * command to the server or receiving a reply from the server. 1197 * @since 3.0 1198 */ 1199 public Iterable<String> iterateNewNewsgroupListing(final NewGroupsOrNewsQuery query) throws IOException { 1200 if (NNTPReply.isPositiveCompletion(newgroups( 1201 query.getDate(), query.getTime(), 1202 query.isGMT(), query.getDistributions()))) { 1203 return new ReplyIterator(_reader_); 1204 } 1205 throw new IOException("NEWGROUPS command failed: "+getReplyString()); 1206 } 1207 1208 /** 1209 * List all new newsgroups added to the NNTP server since a particular 1210 * date subject to the conditions of the specified query. If no new 1211 * newsgroups were added, no entries will be returned. 1212 * This uses the "NEWGROUPS" command. 1213 * <p> 1214 * @param query The query restricting how to search for new newsgroups. 1215 * @return An iterable of NewsgroupInfo instances containing the information 1216 * for each new newsgroup added to the NNTP server. If no newsgroups 1217 * were added, no entries will be returned. 1218 * @throws NNTPConnectionClosedException 1219 * If the NNTP server prematurely closes the connection as a result 1220 * of the client being idle or some other reason causing the server 1221 * to send NNTP reply code 400. This exception may be caught either 1222 * as an IOException or independently as itself. 1223 * @throws IOException If an I/O error occurs while either sending a 1224 * command to the server or receiving a reply from the server. 1225 * @since 3.0 1226 */ 1227 public Iterable<NewsgroupInfo> iterateNewNewsgroups(final NewGroupsOrNewsQuery query) throws IOException { 1228 return new NewsgroupIterator(iterateNewNewsgroupListing(query)); 1229 } 1230 1231 /** 1232 * List all new articles added to the NNTP server since a particular 1233 * date subject to the conditions of the specified query. If no new 1234 * new news is found, a zero length array will be returned. If the 1235 * command fails, null will be returned. You must add at least one 1236 * newsgroup to the query, else the command will fail. Each String 1237 * in the returned array is a unique message identifier including the 1238 * enclosing < and >. 1239 * This uses the "NEWNEWS" command. 1240 * <p> 1241 * @param query The query restricting how to search for new news. You 1242 * must add at least one newsgroup to the query. 1243 * @return An array of String instances containing the unique message 1244 * identifiers for each new article added to the NNTP server. If no 1245 * new news is found, a zero length array will be returned. If the 1246 * command fails, null will be returned. 1247 * @throws NNTPConnectionClosedException 1248 * If the NNTP server prematurely closes the connection as a result 1249 * of the client being idle or some other reason causing the server 1250 * to send NNTP reply code 400. This exception may be caught either 1251 * as an IOException or independently as itself. 1252 * @throws IOException If an I/O error occurs while either sending a 1253 * command to the server or receiving a reply from the server. 1254 * 1255 * @see #iterateNewNews(NewGroupsOrNewsQuery) 1256 */ 1257 public String[] listNewNews(final NewGroupsOrNewsQuery query) 1258 throws IOException 1259 { 1260 if (!NNTPReply.isPositiveCompletion(newnews(query.getNewsgroups(), query.getDate(), query.getTime(), 1261 query.isGMT(), query.getDistributions()))) { 1262 return null; 1263 } 1264 1265 final Vector<String> list = new Vector<>(); 1266 try (final BufferedReader reader = new DotTerminatedMessageReader(_reader_)) { 1267 1268 String line; 1269 while ((line = reader.readLine()) != null) { 1270 list.addElement(line); 1271 } 1272 } 1273 1274 final int size = list.size(); 1275 if (size < 1) { 1276 return NetConstants.EMPTY_STRING_ARRAY; 1277 } 1278 1279 final String[] result = new String[size]; 1280 list.copyInto(result); 1281 1282 return result; 1283 } 1284 1285 /** 1286 * List all new articles added to the NNTP server since a particular 1287 * date subject to the conditions of the specified query. If no new 1288 * new news is found, no entries will be returned. 1289 * This uses the "NEWNEWS" command. 1290 * You must add at least one newsgroup to the query, else the command will fail. 1291 * Each String which is returned is a unique message identifier including the 1292 * enclosing < and >. 1293 * <p> 1294 * @param query The query restricting how to search for new news. You 1295 * must add at least one newsgroup to the query. 1296 * @return An iterator of String instances containing the unique message 1297 * identifiers for each new article added to the NNTP server. If no 1298 * new news is found, no strings will be returned. 1299 * @throws NNTPConnectionClosedException 1300 * If the NNTP server prematurely closes the connection as a result 1301 * of the client being idle or some other reason causing the server 1302 * to send NNTP reply code 400. This exception may be caught either 1303 * as an IOException or independently as itself. 1304 * @throws IOException If an I/O error occurs while either sending a 1305 * command to the server or receiving a reply from the server. 1306 * @since 3.0 1307 */ 1308 public Iterable<String> iterateNewNews(final NewGroupsOrNewsQuery query) throws IOException { 1309 if (NNTPReply.isPositiveCompletion(newnews( 1310 query.getNewsgroups(), query.getDate(), query.getTime(), 1311 query.isGMT(), query.getDistributions()))) { 1312 return new ReplyIterator(_reader_); 1313 } 1314 throw new IOException("NEWNEWS command failed: "+getReplyString()); 1315 } 1316 1317 /** 1318 * There are a few NNTPClient methods that do not complete the 1319 * entire sequence of NNTP commands to complete a transaction. These 1320 * commands require some action by the programmer after the reception 1321 * of a positive preliminary command. After the programmer's code 1322 * completes its actions, it must call this method to receive 1323 * the completion reply from the server and verify the success of the 1324 * entire transaction. 1325 * <p> 1326 * For example 1327 * <pre> 1328 * writer = client.postArticle(); 1329 * if(writer == null) // failure 1330 * return false; 1331 * header = new SimpleNNTPHeader("foobar@foo.com", "Just testing"); 1332 * header.addNewsgroup("alt.test"); 1333 * writer.write(header.toString()); 1334 * writer.write("This is just a test"); 1335 * writer.close(); 1336 * if(!client.completePendingCommand()) // failure 1337 * return false; 1338 * </pre> 1339 * <p> 1340 * @return True if successfully completed, false if not. 1341 * @throws NNTPConnectionClosedException 1342 * If the NNTP server prematurely closes the connection as a result 1343 * of the client being idle or some other reason causing the server 1344 * to send NNTP reply code 400. This exception may be caught either 1345 * as an IOException or independently as itself. 1346 * @throws IOException If an I/O error occurs while either sending a 1347 * command to the server or receiving a reply from the server. 1348 */ 1349 public boolean completePendingCommand() throws IOException 1350 { 1351 return NNTPReply.isPositiveCompletion(getReply()); 1352 } 1353 1354 /** 1355 * Post an article to the NNTP server. This method returns a 1356 * DotTerminatedMessageWriter instance to which the article can be 1357 * written. Null is returned if the posting attempt fails. You 1358 * should check {@link NNTP#isAllowedToPost isAllowedToPost() } 1359 * before trying to post. However, a posting 1360 * attempt can fail due to malformed headers. 1361 * <p> 1362 * You must not issue any commands to the NNTP server (i.e., call any 1363 * (other methods) until you finish writing to the returned Writer 1364 * instance and close it. The NNTP protocol uses the same stream for 1365 * issuing commands as it does for returning results. Therefore the 1366 * returned Writer actually writes directly to the NNTP connection. 1367 * After you close the writer, you can execute new commands. If you 1368 * do not follow these requirements your program will not work properly. 1369 * <p> 1370 * Different NNTP servers will require different header formats, but 1371 * you can use the provided 1372 * {@link org.apache.commons.net.nntp.SimpleNNTPHeader} 1373 * class to construct the bare minimum acceptable header for most 1374 * news readers. To construct more complicated headers you should 1375 * refer to RFC 822. When the Java Mail API is finalized, you will be 1376 * able to use it to compose fully compliant Internet text messages. 1377 * The DotTerminatedMessageWriter takes care of doubling line-leading 1378 * dots and ending the message with a single dot upon closing, so all 1379 * you have to worry about is writing the header and the message. 1380 * <p> 1381 * Upon closing the returned Writer, you need to call 1382 * {@link #completePendingCommand completePendingCommand() } 1383 * to finalize the posting and verify its success or failure from 1384 * the server reply. 1385 * <p> 1386 * @return A DotTerminatedMessageWriter to which the article (including 1387 * header) can be written. Returns null if the command fails. 1388 * @throws IOException If an I/O error occurs while either sending a 1389 * command to the server or receiving a reply from the server. 1390 */ 1391 1392 public Writer postArticle() throws IOException 1393 { 1394 if (!NNTPReply.isPositiveIntermediate(post())) { 1395 return null; 1396 } 1397 1398 return new DotTerminatedMessageWriter(_writer_); 1399 } 1400 1401 1402 public Writer forwardArticle(final String articleId) throws IOException 1403 { 1404 if (!NNTPReply.isPositiveIntermediate(ihave(articleId))) { 1405 return null; 1406 } 1407 1408 return new DotTerminatedMessageWriter(_writer_); 1409 } 1410 1411 1412 /** 1413 * Logs out of the news server gracefully by sending the QUIT command. 1414 * However, you must still disconnect from the server before you can open 1415 * a new connection. 1416 * <p> 1417 * @return True if successfully completed, false if not. 1418 * @throws IOException If an I/O error occurs while either sending a 1419 * command to the server or receiving a reply from the server. 1420 */ 1421 public boolean logout() throws IOException 1422 { 1423 return NNTPReply.isPositiveCompletion(quit()); 1424 } 1425 1426 1427 /** 1428 * Log into a news server by sending the AUTHINFO USER/AUTHINFO 1429 * PASS command sequence. This is usually sent in response to a 1430 * 480 reply code from the NNTP server. 1431 * <p> 1432 * @param username a valid username 1433 * @param password the corresponding password 1434 * @return True for successful login, false for a failure 1435 * @throws IOException on error 1436 */ 1437 public boolean authenticate(final String username, final String password) 1438 throws IOException 1439 { 1440 int replyCode = authinfoUser(username); 1441 1442 if (replyCode == NNTPReply.MORE_AUTH_INFO_REQUIRED) 1443 { 1444 replyCode = authinfoPass(password); 1445 1446 if (replyCode == NNTPReply.AUTHENTICATION_ACCEPTED) 1447 { 1448 this._isAllowedToPost = true; 1449 return true; 1450 } 1451 } 1452 return false; 1453 } 1454 1455 /** 1456 * Private implementation of XOVER functionality. 1457 * 1458 * See {@link NNTP#xover} 1459 * for legal agument formats. Alternatively, read RFC 2980 :-) 1460 * <p> 1461 * @param articleRange 1462 * @return Returns a DotTerminatedMessageReader if successful, null 1463 * otherwise 1464 * @throws IOException 1465 */ 1466 private BufferedReader retrieveArticleInfo(final String articleRange) 1467 throws IOException 1468 { 1469 if (!NNTPReply.isPositiveCompletion(xover(articleRange))) { 1470 return null; 1471 } 1472 1473 return new DotTerminatedMessageReader(_reader_); 1474 } 1475 1476 /** 1477 * Return article headers for a specified post. 1478 * <p> 1479 * @param articleNumber the article to retrieve headers for 1480 * @return a DotTerminatedReader if successful, null otherwise 1481 * @throws IOException on error 1482 */ 1483 public BufferedReader retrieveArticleInfo(final long articleNumber) throws IOException 1484 { 1485 return retrieveArticleInfo(Long.toString(articleNumber)); 1486 } 1487 1488 /** 1489 * Return article headers for all articles between lowArticleNumber 1490 * and highArticleNumber, inclusively. Uses the XOVER command. 1491 * <p> 1492 * @param lowArticleNumber low number 1493 * @param highArticleNumber high number 1494 * @return a DotTerminatedReader if successful, null otherwise 1495 * @throws IOException on error 1496 */ 1497 public BufferedReader retrieveArticleInfo(final long lowArticleNumber, 1498 final long highArticleNumber) 1499 throws IOException 1500 { 1501 return 1502 retrieveArticleInfo(lowArticleNumber + "-" + 1503 highArticleNumber); 1504 } 1505 1506 /** 1507 * Return article headers for all articles between lowArticleNumber 1508 * and highArticleNumber, inclusively, using the XOVER command. 1509 * <p> 1510 * @param lowArticleNumber low 1511 * @param highArticleNumber high 1512 * @return an Iterable of Articles 1513 * @throws IOException if the command failed 1514 * @since 3.0 1515 */ 1516 public Iterable<Article> iterateArticleInfo(final long lowArticleNumber, final long highArticleNumber) 1517 throws IOException 1518 { 1519 final BufferedReader info = retrieveArticleInfo(lowArticleNumber,highArticleNumber); 1520 if (info == null) { 1521 throw new IOException("XOVER command failed: "+getReplyString()); 1522 } 1523 // N.B. info is already DotTerminated, so don't rewrap 1524 return new ArticleIterator(new ReplyIterator(info, false)); 1525 } 1526 1527 /** 1528 * Private implementation of XHDR functionality. 1529 * 1530 * See {@link NNTP#xhdr} 1531 * for legal agument formats. Alternatively, read RFC 1036. 1532 * <p> 1533 * @param header 1534 * @param articleRange 1535 * @return Returns a DotTerminatedMessageReader if successful, null 1536 * otherwise 1537 * @throws IOException 1538 */ 1539 private BufferedReader retrieveHeader(final String header, final String articleRange) 1540 throws IOException 1541 { 1542 if (!NNTPReply.isPositiveCompletion(xhdr(header, articleRange))) { 1543 return null; 1544 } 1545 1546 return new DotTerminatedMessageReader(_reader_); 1547 } 1548 1549 /** 1550 * Return an article header for a specified post. 1551 * <p> 1552 * @param header the header to retrieve 1553 * @param articleNumber the article to retrieve the header for 1554 * @return a DotTerminatedReader if successful, null otherwise 1555 * @throws IOException on error 1556 */ 1557 public BufferedReader retrieveHeader(final String header, final long articleNumber) 1558 throws IOException 1559 { 1560 return retrieveHeader(header, Long.toString(articleNumber)); 1561 } 1562 1563 /** 1564 * Return an article header for all articles between lowArticleNumber 1565 * and highArticleNumber, inclusively. 1566 * <p> 1567 * @param header the header 1568 * @param lowArticleNumber to fetch 1569 * @param highArticleNumber to fetch 1570 * @return a DotTerminatedReader if successful, null otherwise 1571 * @throws IOException on error 1572 */ 1573 public BufferedReader retrieveHeader(final String header, final long lowArticleNumber, 1574 final long highArticleNumber) 1575 throws IOException 1576 { 1577 return 1578 retrieveHeader(header,lowArticleNumber + "-" + highArticleNumber); 1579 } 1580 1581 1582 1583 1584 1585 // DEPRECATED METHODS - for API compatibility only - DO NOT USE 1586 // ============================================================ 1587 1588 1589 1590 /** 1591 * @param header the header 1592 * @param lowArticleNumber to fetch 1593 * @param highArticleNumber to fetch 1594 * @return a DotTerminatedReader if successful, null otherwise 1595 * @throws IOException on error 1596 * @deprecated 3.0 use {@link #retrieveHeader(String, long, long)} instead 1597 */ 1598 @Deprecated 1599 public Reader retrieveHeader(final String header, final int lowArticleNumber, final int highArticleNumber) 1600 throws IOException 1601 { 1602 return retrieveHeader(header, (long) lowArticleNumber, (long) highArticleNumber); 1603 } 1604 1605 /** 1606 * @param lowArticleNumber to fetch 1607 * @param highArticleNumber to fetch 1608 * @return a DotTerminatedReader if successful, null otherwise 1609 * @throws IOException on error 1610 * @deprecated 3.0 use {@link #retrieveArticleInfo(long, long)} instead 1611 */ 1612 @Deprecated 1613 public Reader retrieveArticleInfo(final int lowArticleNumber, final int highArticleNumber) throws IOException { 1614 return retrieveArticleInfo((long) lowArticleNumber, (long) highArticleNumber); 1615 } 1616 1617 /** 1618 * @param a tba 1619 * @param b tba 1620 * @return tba 1621 * @throws IOException tba 1622 * @deprecated 3.0 use {@link #retrieveHeader(String, long)} instead 1623 */ 1624 @Deprecated 1625 public Reader retrieveHeader(final String a, final int b) throws IOException { 1626 return retrieveHeader(a, (long) b); 1627 } 1628 1629 /** 1630 * @param a tba 1631 * @param ap tba 1632 * @return tba 1633 * @throws IOException tba 1634 * @deprecated 3.0 use {@link #selectArticle(long, ArticleInfo)} instead 1635 */ 1636 @Deprecated 1637 public boolean selectArticle(final int a, final ArticlePointer ap) throws IOException { 1638 final ArticleInfo ai = ap2ai(ap); 1639 final boolean b = selectArticle(a, ai); 1640 ai2ap(ai, ap); 1641 return b; 1642 } 1643 1644 /** 1645 * @param lowArticleNumber to fetch 1646 * @return a DotTerminatedReader if successful, null otherwise 1647 * @throws IOException tba 1648 * @deprecated 3.0 use {@link #retrieveArticleInfo(long)} instead 1649 */ 1650 @Deprecated 1651 public Reader retrieveArticleInfo(final int lowArticleNumber) throws IOException { 1652 return retrieveArticleInfo((long) lowArticleNumber); 1653 } 1654 1655 /** 1656 * @param a tba 1657 * @return tba 1658 * @throws IOException tba 1659 * @deprecated 3.0 use {@link #selectArticle(long)} instead 1660 */ 1661 @Deprecated 1662 public boolean selectArticle(final int a) throws IOException { 1663 return selectArticle((long) a); 1664 } 1665 1666 /** 1667 * @param a tba 1668 * @return tba 1669 * @throws IOException tba 1670 * @deprecated 3.0 use {@link #retrieveArticleHeader(long)} instead 1671 */ 1672 @Deprecated 1673 public Reader retrieveArticleHeader(final int a) throws IOException { 1674 return retrieveArticleHeader((long) a); 1675 } 1676 1677 /** 1678 * @param a tba 1679 * @param ap tba 1680 * @return tba 1681 * @throws IOException tba 1682 * @deprecated 3.0 use {@link #retrieveArticleHeader(long, ArticleInfo)} instead 1683 */ 1684 @Deprecated 1685 public Reader retrieveArticleHeader(final int a, final ArticlePointer ap) throws IOException { 1686 final ArticleInfo ai = ap2ai(ap); 1687 final Reader rdr = retrieveArticleHeader(a, ai); 1688 ai2ap(ai, ap); 1689 return rdr; 1690 } 1691 1692 /** 1693 * @param a tba 1694 * @return tba 1695 * @throws IOException tba 1696 * @deprecated 3.0 use {@link #retrieveArticleBody(long)} instead 1697 */ 1698 @Deprecated 1699 public Reader retrieveArticleBody(final int a) throws IOException { 1700 return retrieveArticleBody((long) a); 1701 } 1702 1703 /** 1704 * @param articleNumber The number of the the article to retrieve. 1705 * @param pointer A parameter through which to return the article's number and unique id 1706 * @return A DotTerminatedMessageReader instance from which the article 1707 * can be read. null if the article does not exist. 1708 * @throws IOException on error 1709 * @deprecated 3.0 use {@link #retrieveArticle(long, ArticleInfo)} instead 1710 */ 1711 @Deprecated 1712 public Reader retrieveArticle(final int articleNumber, final ArticlePointer pointer) throws IOException { 1713 final ArticleInfo ai = ap2ai(pointer); 1714 final Reader rdr = retrieveArticle(articleNumber, ai); 1715 ai2ap(ai, pointer); 1716 return rdr; 1717 } 1718 1719 /** 1720 * @param articleNumber The number of the the article to retrieve 1721 * @return A DotTerminatedMessageReader instance from which the article 1722 * can be read. null if the article does not exist. 1723 * @throws IOException on error 1724 * @deprecated 3.0 use {@link #retrieveArticle(long)} instead 1725 */ 1726 @Deprecated 1727 public Reader retrieveArticle(final int articleNumber) throws IOException { 1728 return retrieveArticle((long) articleNumber); 1729 } 1730 1731 /** 1732 * @param a tba 1733 * @param ap tba 1734 * @return tba 1735 * @throws IOException tba 1736 * @deprecated 3.0 use {@link #retrieveArticleBody(long, ArticleInfo)} instead 1737 */ 1738 @Deprecated 1739 public Reader retrieveArticleBody(final int a, final ArticlePointer ap) throws IOException { 1740 final ArticleInfo ai = ap2ai(ap); 1741 final Reader rdr = retrieveArticleBody(a, ai); 1742 ai2ap(ai, ap); 1743 return rdr; 1744 } 1745 1746 /** 1747 * @param articleId The unique article identifier of the article to retrieve 1748 * @param pointer A parameter through which to return the article's number and unique id 1749 * @deprecated 3.0 use {@link #retrieveArticle(String, ArticleInfo)} instead 1750 * @return A DotTerminatedMessageReader instance from which the article can be read. 1751 * null if the article does not exist. 1752 * @throws IOException on error 1753 */ 1754 @Deprecated 1755 public Reader retrieveArticle(final String articleId, final ArticlePointer pointer) throws IOException { 1756 final ArticleInfo ai = ap2ai(pointer); 1757 final Reader rdr = retrieveArticle(articleId, ai); 1758 ai2ap(ai, pointer); 1759 return rdr; 1760 } 1761 1762 /** 1763 * @param articleId The unique article identifier of the article to retrieve 1764 * @param pointer A parameter through which to return the article's number and unique id 1765 * @return A DotTerminatedMessageReader instance from which the article 1766 * body can be read. null if the article does not exist. 1767 * @throws IOException on error 1768 * @deprecated 3.0 use {@link #retrieveArticleBody(String, ArticleInfo)} instead 1769 */ 1770 @Deprecated 1771 public Reader retrieveArticleBody(final String articleId, final ArticlePointer pointer) throws IOException { 1772 final ArticleInfo ai = ap2ai(pointer); 1773 final Reader rdr = retrieveArticleBody(articleId, ai); 1774 ai2ap(ai, pointer); 1775 return rdr; 1776 } 1777 1778 /** 1779 * @param articleId The unique article identifier of the article to retrieve 1780 * @param pointer A parameter through which to return the article's number and unique id 1781 * @return A DotTerminatedMessageReader instance from which the article 1782 * body can be read. null if the article does not exist. 1783 * @throws IOException on error 1784 * @deprecated 3.0 use {@link #retrieveArticleHeader(String, ArticleInfo)} instead 1785 */ 1786 @Deprecated 1787 public Reader retrieveArticleHeader(final String articleId, final ArticlePointer pointer) throws IOException { 1788 final ArticleInfo ai = ap2ai(pointer); 1789 final Reader rdr = retrieveArticleHeader(articleId, ai); 1790 ai2ap(ai, pointer); 1791 return rdr; 1792 } 1793 1794 /** 1795 * @param articleId The unique article identifier of the article to retrieve 1796 * @param pointer A parameter through which to return the article's number and unique id 1797 * @return A DotTerminatedMessageReader instance from which the article 1798 * body can be read. null if the article does not exist. 1799 * @throws IOException on error 1800 * @deprecated 3.0 use {@link #selectArticle(String, ArticleInfo)} instead 1801 */ 1802 @Deprecated 1803 public boolean selectArticle(final String articleId, final ArticlePointer pointer) throws IOException { 1804 final ArticleInfo ai = ap2ai(pointer); 1805 final boolean b = selectArticle(articleId, ai); 1806 ai2ap(ai, pointer); 1807 return b; 1808 1809 } 1810 1811 /** 1812 * @param pointer A parameter through which to return the article's number and unique id 1813 * @return True if successful, false if not. 1814 * @throws IOException on error 1815 * @deprecated 3.0 use {@link #selectArticle(ArticleInfo)} instead 1816 */ 1817 @Deprecated 1818 public boolean selectArticle(final ArticlePointer pointer) throws IOException { 1819 final ArticleInfo ai = ap2ai(pointer); 1820 final boolean b = selectArticle(ai); 1821 ai2ap(ai, pointer); 1822 return b; 1823 1824 } 1825 1826 /** 1827 * @param pointer A parameter through which to return the article's number and unique id 1828 * @return True if successful, false if not. 1829 * @throws IOException on error 1830 * @deprecated 3.0 use {@link #selectNextArticle(ArticleInfo)} instead 1831 */ 1832 @Deprecated 1833 public boolean selectNextArticle(final ArticlePointer pointer) throws IOException { 1834 final ArticleInfo ai = ap2ai(pointer); 1835 final boolean b = selectNextArticle(ai); 1836 ai2ap(ai, pointer); 1837 return b; 1838 1839 } 1840 1841 /** 1842 * @param pointer A parameter through which to return the article's number and unique id 1843 * @return True if successful, false if not. 1844 * @throws IOException on error 1845 * @deprecated 3.0 use {@link #selectPreviousArticle(ArticleInfo)} instead 1846 */ 1847 @Deprecated 1848 public boolean selectPreviousArticle(final ArticlePointer pointer) throws IOException { 1849 final ArticleInfo ai = ap2ai(pointer); 1850 final boolean b = selectPreviousArticle(ai); 1851 ai2ap(ai, pointer); 1852 return b; 1853 } 1854 1855 // Helper methods 1856 1857 private ArticleInfo ap2ai(@SuppressWarnings("deprecation") final ArticlePointer ap) { 1858 if (ap == null) { 1859 return null; 1860 } 1861 final ArticleInfo ai = new ArticleInfo(); 1862 return ai; 1863 } 1864 1865 @SuppressWarnings("deprecation") 1866 private void ai2ap(final ArticleInfo ai, final ArticlePointer ap){ 1867 if (ap != null) { // ai cannot be null 1868 ap.articleId = ai.articleId; 1869 ap.articleNumber = (int) ai.articleNumber; 1870 } 1871 } 1872}