1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.commons.net.nntp; 19 20 import java.io.BufferedReader; 21 import java.io.IOException; 22 import java.io.Reader; 23 import java.io.StringWriter; 24 import java.io.Writer; 25 import java.util.ArrayList; 26 import java.util.Vector; 27 28 import org.apache.commons.net.MalformedServerReplyException; 29 import org.apache.commons.net.io.DotTerminatedMessageReader; 30 import org.apache.commons.net.io.DotTerminatedMessageWriter; 31 import org.apache.commons.net.io.Util; 32 import org.apache.commons.net.util.NetConstants; 33 34 /** 35 * NNTPClient encapsulates all the functionality necessary to post and retrieve articles from an NNTP server. As with all classes derived from 36 * {@link org.apache.commons.net.SocketClient}, you must first connect to the server with {@link org.apache.commons.net.SocketClient#connect connect } before 37 * doing anything, and finally {@link org.apache.commons.net.nntp.NNTP#disconnect disconnect() } after you're completely finished interacting with the server. 38 * Remember that the {@link org.apache.commons.net.nntp.NNTP#isAllowedToPost isAllowedToPost()} method is defined in {@link org.apache.commons.net.nntp.NNTP}. 39 * <p> 40 * You should keep in mind that the NNTP server may choose to prematurely close a connection if the client has been idle for longer than a given time period or 41 * if the server is being shutdown by the operator or some other reason. The NNTP class will detect a premature NNTP server connection closing when it receives 42 * a {@link org.apache.commons.net.nntp.NNTPReply#SERVICE_DISCONTINUED NNTPReply.SERVICE_DISCONTINUED } response to a command. When that occurs, the NNTP class 43 * method encountering that reply will throw an {@link org.apache.commons.net.nntp.NNTPConnectionClosedException} . <code>NNTPConectionClosedException</code> is 44 * a subclass of <code> IOException </code> and therefore need not be caught separately, but if you are going to catch it separately, its catch block must 45 * appear before the more general <code> IOException </code> catch block. When you encounter an 46 * {@link org.apache.commons.net.nntp.NNTPConnectionClosedException} , you must disconnect the connection with 47 * {@link org.apache.commons.net.nntp.NNTP#disconnect disconnect() } to properly clean up the system resources used by NNTP. Before disconnecting, you may check 48 * the last reply code and text with {@link org.apache.commons.net.nntp.NNTP#getReplyCode getReplyCode } and 49 * {@link org.apache.commons.net.nntp.NNTP#getReplyString getReplyString }. 50 * </p> 51 * <p> 52 * Rather than list it separately for each method, we mention here that every method communicating with the server and throwing an IOException can also throw a 53 * {@link org.apache.commons.net.MalformedServerReplyException} , which is a subclass of IOException. A MalformedServerReplyException will be thrown when the 54 * reply received from the server deviates enough from the protocol specification that it cannot be interpreted in a useful manner despite attempts to be as 55 * lenient as possible. 56 * </p> 57 * 58 * @see NNTP 59 * @see NNTPConnectionClosedException 60 * @see org.apache.commons.net.MalformedServerReplyException 61 */ 62 63 public class NNTPClient extends NNTP { 64 65 private static final NewsgroupInfo[] EMPTY_NEWSGROUP_INFO_ARRAY = {}; 66 67 /** 68 * Parse a response line from {@link #retrieveArticleInfo(long, long)}. 69 * 70 * @param line a response line 71 * @return the parsed {@link Article}, if unparseable then isDummy() will be true, and the subject will contain the raw info. 72 * @since 3.0 73 */ 74 static Article parseArticleEntry(final String line) { 75 // Extract the article information 76 // Mandatory format (from NNTP RFC 2980) is : 77 // articleNumber\tSubject\tAuthor\tDate\tID\tReference(s)\tByte Count\tLine Count 78 79 final Article article = new Article(); 80 article.setSubject(line); // in case parsing fails 81 final String[] parts = line.split("\t"); 82 if (parts.length > 6) { 83 int i = 0; 84 try { 85 article.setArticleNumber(Long.parseLong(parts[i++])); 86 article.setSubject(parts[i++]); 87 article.setFrom(parts[i++]); 88 article.setDate(parts[i++]); 89 article.setArticleId(parts[i++]); 90 article.addReference(parts[i++]); 91 } catch (final NumberFormatException e) { 92 // ignored, already handled 93 } 94 } 95 return article; 96 } 97 98 /* 99 * 211 n f l s group selected (n = estimated number of articles in group, f = first article number in the group, l = last article number in the group, s = 100 * name of the group.) 101 */ 102 103 private static void parseGroupReply(final String reply, final NewsgroupInfo info) throws MalformedServerReplyException { 104 final String[] tokens = reply.split(" "); 105 if (tokens.length >= 5) { 106 int i = 1; // Skip numeric response value 107 try { 108 // Get estimated article count 109 info.setArticleCount(Long.parseLong(tokens[i++])); 110 // Get first article number 111 info.setFirstArticle(Long.parseLong(tokens[i++])); 112 // Get last article number 113 info.setLastArticle(Long.parseLong(tokens[i++])); 114 // Get newsgroup name 115 info.setNewsgroup(tokens[i++]); 116 117 info.setPostingPermission(NewsgroupInfo.UNKNOWN_POSTING_PERMISSION); 118 return; 119 } catch (final NumberFormatException e) { 120 // drop through to report error 121 } 122 } 123 124 throw new MalformedServerReplyException("Could not parse newsgroup info.\nServer reply: " + reply); 125 } 126 127 // Format: group last first p 128 static NewsgroupInfo parseNewsgroupListEntry(final String entry) { 129 final String[] tokens = entry.split(" "); 130 if (tokens.length < 4) { 131 return null; 132 } 133 final NewsgroupInfo result = new NewsgroupInfo(); 134 135 int i = 0; 136 137 result.setNewsgroup(tokens[i++]); 138 139 try { 140 final long lastNum = Long.parseLong(tokens[i++]); 141 final long firstNum = Long.parseLong(tokens[i++]); 142 result.setFirstArticle(firstNum); 143 result.setLastArticle(lastNum); 144 if (firstNum == 0 && lastNum == 0) { 145 result.setArticleCount(0); 146 } else { 147 result.setArticleCount(lastNum - firstNum + 1); 148 } 149 } catch (final NumberFormatException e) { 150 return null; 151 } 152 153 switch (tokens[i++].charAt(0)) { 154 case 'y': 155 case 'Y': 156 result.setPostingPermission(NewsgroupInfo.PERMITTED_POSTING_PERMISSION); 157 break; 158 case 'n': 159 case 'N': 160 result.setPostingPermission(NewsgroupInfo.PROHIBITED_POSTING_PERMISSION); 161 break; 162 case 'm': 163 case 'M': 164 result.setPostingPermission(NewsgroupInfo.MODERATED_POSTING_PERMISSION); 165 break; 166 default: 167 result.setPostingPermission(NewsgroupInfo.UNKNOWN_POSTING_PERMISSION); 168 break; 169 } 170 171 return result; 172 } 173 174 @SuppressWarnings("deprecation") 175 private void ai2ap(final ArticleInfo ai, final ArticlePointer ap) { 176 if (ap != null) { // ai cannot be null 177 ap.articleId = ai.articleId; 178 ap.articleNumber = (int) ai.articleNumber; 179 } 180 } 181 182 private ArticleInfo ap2ai(@SuppressWarnings("deprecation") final ArticlePointer ap) { 183 if (ap == null) { 184 return null; 185 } 186 final ArticleInfo ai = new ArticleInfo(); 187 return ai; 188 } 189 190 /** 191 * Log into a news server by sending the AUTHINFO USER/AUTHINFO PASS command sequence. This is usually sent in response to a 480 reply code from the NNTP 192 * server. 193 * 194 * @param user a valid user name 195 * @param password the corresponding password 196 * @return True for successful login, false for a failure 197 * @throws IOException on error 198 */ 199 public boolean authenticate(final String user, final String password) throws IOException { 200 int replyCode = authinfoUser(user); 201 202 if (replyCode == NNTPReply.MORE_AUTH_INFO_REQUIRED) { 203 replyCode = authinfoPass(password); 204 205 if (replyCode == NNTPReply.AUTHENTICATION_ACCEPTED) { 206 this._isAllowedToPost = true; 207 return true; 208 } 209 } 210 return false; 211 } 212 213 /** 214 * There are a few NNTPClient methods that do not complete the entire sequence of NNTP commands to complete a transaction. These commands require some 215 * action by the programmer after the reception of a positive preliminary command. After the programmer's code completes its actions, it must call this 216 * method to receive the completion reply from the server and verify the success of the entire transaction. 217 * <p> 218 * For example 219 * </p> 220 * <pre> 221 * writer = client.postArticle(); 222 * if (writer == null) // failure 223 * return false; 224 * header = new SimpleNNTPHeader("foobar@foo.com", "Just testing"); 225 * header.addNewsgroup("alt.test"); 226 * writer.write(header.toString()); 227 * writer.write("This is just a test"); 228 * writer.close(); 229 * if (!client.completePendingCommand()) // failure 230 * return false; 231 * </pre> 232 * 233 * @return True if successfully completed, false if not. 234 * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason 235 * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or 236 * independently as itself. 237 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 238 */ 239 public boolean completePendingCommand() throws IOException { 240 return NNTPReply.isPositiveCompletion(getReply()); 241 } 242 243 public Writer forwardArticle(final String articleId) throws IOException { 244 if (!NNTPReply.isPositiveIntermediate(ihave(articleId))) { 245 return null; 246 } 247 248 return new DotTerminatedMessageWriter(_writer_); 249 } 250 251 /** 252 * Return article headers for all articles between lowArticleNumber and highArticleNumber, inclusively, using the XOVER command. 253 * 254 * @param lowArticleNumber low 255 * @param highArticleNumber high 256 * @return an Iterable of Articles 257 * @throws IOException if the command failed 258 * @since 3.0 259 */ 260 public Iterable<Article> iterateArticleInfo(final long lowArticleNumber, final long highArticleNumber) throws IOException { 261 final BufferedReader info = retrieveArticleInfo(lowArticleNumber, highArticleNumber); 262 if (info == null) { 263 throw new IOException("XOVER command failed: " + getReplyString()); 264 } 265 // N.B. info is already DotTerminated, so don't rewrap 266 return new ArticleIterator(new ReplyIterator(info, false)); 267 } 268 269 /** 270 * List all new articles added to the NNTP server since a particular date subject to the conditions of the specified query. If no new news is found, 271 * no entries will be returned. This uses the "NEWNEWS" command. You must add at least one newsgroup to the query, else the command will fail. 272 * Each String which is returned is a unique message identifier including the enclosing < and >. 273 * 274 * @param query The query restricting how to search for new news. You must add at least one newsgroup to the query. 275 * @return An iterator of String instances containing the unique message identifiers for each new article added to the NNTP server. If no new news is found, 276 * no strings will be returned. 277 * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason 278 * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or 279 * independently as itself. 280 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 281 * @since 3.0 282 */ 283 public Iterable<String> iterateNewNews(final NewGroupsOrNewsQuery query) throws IOException { 284 if (NNTPReply.isPositiveCompletion(newnews(query.getNewsgroups(), query.getDate(), query.getTime(), query.isGMT(), query.getDistributions()))) { 285 return new ReplyIterator(_reader_); 286 } 287 throw new IOException("NEWNEWS command failed: " + getReplyString()); 288 } 289 290 /** 291 * List all new newsgroups added to the NNTP server since a particular date subject to the conditions of the specified query. If no new newsgroups were 292 * added, no entries will be returned. This uses the "NEWGROUPS" command. 293 * 294 * @param query The query restricting how to search for new newsgroups. 295 * @return An iterable of Strings containing the raw information for each new newsgroup added to the NNTP server. If no newsgroups were added, no entries 296 * will be returned. 297 * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason 298 * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or 299 * independently as itself. 300 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 301 * @since 3.0 302 */ 303 public Iterable<String> iterateNewNewsgroupListing(final NewGroupsOrNewsQuery query) throws IOException { 304 if (NNTPReply.isPositiveCompletion(newgroups(query.getDate(), query.getTime(), query.isGMT(), query.getDistributions()))) { 305 return new ReplyIterator(_reader_); 306 } 307 throw new IOException("NEWGROUPS command failed: " + getReplyString()); 308 } 309 310 /** 311 * List all new newsgroups added to the NNTP server since a particular date subject to the conditions of the specified query. If no new newsgroups were 312 * added, no entries will be returned. This uses the "NEWGROUPS" command. 313 * 314 * @param query The query restricting how to search for new newsgroups. 315 * @return An iterable of NewsgroupInfo instances containing the information for each new newsgroup added to the NNTP server. If no newsgroups were added, 316 * no entries will be returned. 317 * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason 318 * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or 319 * independently as itself. 320 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 321 * @since 3.0 322 */ 323 public Iterable<NewsgroupInfo> iterateNewNewsgroups(final NewGroupsOrNewsQuery query) throws IOException { 324 return new NewsgroupIterator(iterateNewNewsgroupListing(query)); 325 } 326 327 /** 328 * List all newsgroups served by the NNTP server. If no newsgroups are served, no entries will be returned. The method uses the "LIST" command. 329 * 330 * @return An iterable of NewsgroupInfo instances containing the information for each newsgroup served by the NNTP server. If no newsgroups are served, no 331 * entries will be returned. 332 * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason 333 * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or 334 * independently as itself. 335 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 336 * @since 3.0 337 */ 338 public Iterable<String> iterateNewsgroupListing() throws IOException { 339 if (NNTPReply.isPositiveCompletion(list())) { 340 return new ReplyIterator(_reader_); 341 } 342 throw new IOException("LIST command failed: " + getReplyString()); 343 } 344 345 /** 346 * List the newsgroups that match a given pattern. Uses the "LIST ACTIVE" command. 347 * 348 * @param wildmat a pseudo-regex pattern (cf. RFC 2980) 349 * @return An iterable of Strings containing the raw information for each newsgroup served by the NNTP server corresponding to the supplied pattern. If no 350 * such newsgroups are served, no entries will be returned. 351 * @throws IOException on error 352 * @since 3.0 353 */ 354 public Iterable<String> iterateNewsgroupListing(final String wildmat) throws IOException { 355 if (NNTPReply.isPositiveCompletion(listActive(wildmat))) { 356 return new ReplyIterator(_reader_); 357 } 358 throw new IOException("LIST ACTIVE " + wildmat + " command failed: " + getReplyString()); 359 } 360 361 /** 362 * List all newsgroups served by the NNTP server. If no newsgroups are served, no entries will be returned. The method uses the "LIST" command. 363 * 364 * @return An iterable of Strings containing the raw information for each newsgroup served by the NNTP server. If no newsgroups are served, no entries will 365 * be returned. 366 * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason 367 * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or 368 * independently as itself. 369 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 370 * @since 3.0 371 */ 372 public Iterable<NewsgroupInfo> iterateNewsgroups() throws IOException { 373 return new NewsgroupIterator(iterateNewsgroupListing()); 374 } 375 376 /** 377 * List the newsgroups that match a given pattern. Uses the "LIST ACTIVE" command. 378 * 379 * @param wildmat a pseudo-regex pattern (cf. RFC 2980) 380 * @return An iterable NewsgroupInfo instances containing the information for each newsgroup served by the NNTP server corresponding to the supplied 381 * pattern. If no such newsgroups are served, no entries will be returned. 382 * @throws IOException on error 383 * @since 3.0 384 */ 385 public Iterable<NewsgroupInfo> iterateNewsgroups(final String wildmat) throws IOException { 386 return new NewsgroupIterator(iterateNewsgroupListing(wildmat)); 387 } 388 389 /** 390 * List the command help from the server. 391 * 392 * @return The sever help information. 393 * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason 394 * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or 395 * independently as itself. 396 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 397 */ 398 public String listHelp() throws IOException { 399 if (!NNTPReply.isInformational(help())) { 400 return null; 401 } 402 403 try (final StringWriter help = new StringWriter(); final BufferedReader reader = new DotTerminatedMessageReader(_reader_)) { 404 Util.copyReader(reader, help); 405 return help.toString(); 406 } 407 } 408 409 /** 410 * List all new articles added to the NNTP server since a particular date subject to the conditions of the specified query. If no new news is found, a 411 * zero length array will be returned. If the command fails, null will be returned. You must add at least one newsgroup to the query, else the command will 412 * fail. Each String in the returned array is a unique message identifier including the enclosing < and >. This uses the "NEWNEWS" command. 413 * 414 * @param query The query restricting how to search for new news. You must add at least one newsgroup to the query. 415 * @return An array of String instances containing the unique message identifiers for each new article added to the NNTP server. If no new news is found, a 416 * zero length array will be returned. If the command fails, null will be returned. 417 * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason 418 * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or 419 * independently as itself. 420 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 421 * 422 * @see #iterateNewNews(NewGroupsOrNewsQuery) 423 */ 424 public String[] listNewNews(final NewGroupsOrNewsQuery query) throws IOException { 425 if (!NNTPReply.isPositiveCompletion(newnews(query.getNewsgroups(), query.getDate(), query.getTime(), query.isGMT(), query.getDistributions()))) { 426 return null; 427 } 428 429 final Vector<String> list = new Vector<>(); 430 try (final BufferedReader reader = new DotTerminatedMessageReader(_reader_)) { 431 432 String line; 433 while ((line = reader.readLine()) != null) { 434 list.addElement(line); 435 } 436 } 437 438 final int size = list.size(); 439 if (size < 1) { 440 return NetConstants.EMPTY_STRING_ARRAY; 441 } 442 443 final String[] result = new String[size]; 444 list.copyInto(result); 445 446 return result; 447 } 448 449 /** 450 * List all new newsgroups added to the NNTP server since a particular date subject to the conditions of the specified query. If no new newsgroups were 451 * added, a zero length array will be returned. If the command fails, null will be returned. This uses the "NEWGROUPS" command. 452 * 453 * @param query The query restricting how to search for new newsgroups. 454 * @return An array of NewsgroupInfo instances containing the information for each new newsgroup added to the NNTP server. If no newsgroups were added, a 455 * zero length array will be returned. If the command fails, null will be returned. 456 * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason 457 * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or 458 * independently as itself. 459 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 460 * @see #iterateNewNewsgroups(NewGroupsOrNewsQuery) 461 * @see #iterateNewNewsgroupListing(NewGroupsOrNewsQuery) 462 */ 463 public NewsgroupInfo[] listNewNewsgroups(final NewGroupsOrNewsQuery query) throws IOException { 464 if (!NNTPReply.isPositiveCompletion(newgroups(query.getDate(), query.getTime(), query.isGMT(), query.getDistributions()))) { 465 return null; 466 } 467 468 return readNewsgroupListing(); 469 } 470 471 /** 472 * List all newsgroups served by the NNTP server. If no newsgroups are served, a zero length array will be returned. If the command fails, null will be 473 * returned. The method uses the "LIST" command. 474 * 475 * @return An array of NewsgroupInfo instances containing the information for each newsgroup served by the NNTP server. If no newsgroups are served, a zero 476 * length array will be returned. If the command fails, null will be returned. 477 * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason 478 * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or 479 * independently as itself. 480 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 481 * @see #iterateNewsgroupListing() 482 * @see #iterateNewsgroups() 483 */ 484 public NewsgroupInfo[] listNewsgroups() throws IOException { 485 if (!NNTPReply.isPositiveCompletion(list())) { 486 return null; 487 } 488 489 return readNewsgroupListing(); 490 } 491 492 /** 493 * List the newsgroups that match a given pattern. Uses the "LIST ACTIVE" command. 494 * 495 * @param wildmat a pseudo-regex pattern (cf. RFC 2980) 496 * @return An array of NewsgroupInfo instances containing the information for each newsgroup served by the NNTP server corresponding to the supplied 497 * pattern. If no such newsgroups are served, a zero length array will be returned. If the command fails, null will be returned. 498 * @throws IOException on error 499 * @see #iterateNewsgroupListing(String) 500 * @see #iterateNewsgroups(String) 501 */ 502 public NewsgroupInfo[] listNewsgroups(final String wildmat) throws IOException { 503 if (!NNTPReply.isPositiveCompletion(listActive(wildmat))) { 504 return null; 505 } 506 return readNewsgroupListing(); 507 } 508 509 /** 510 * Send a "LIST OVERVIEW.FMT" command to the server. 511 * 512 * @return the contents of the Overview format, of {@code null} if the command failed 513 * @throws IOException on error 514 */ 515 public String[] listOverviewFmt() throws IOException { 516 if (!NNTPReply.isPositiveCompletion(sendCommand("LIST", "OVERVIEW.FMT"))) { 517 return null; 518 } 519 520 try (final BufferedReader reader = new DotTerminatedMessageReader(_reader_)) { 521 String line; 522 final ArrayList<String> list = new ArrayList<>(); 523 while ((line = reader.readLine()) != null) { 524 list.add(line); 525 } 526 return list.toArray(NetConstants.EMPTY_STRING_ARRAY); 527 } 528 } 529 530 /** 531 * Logs out of the news server gracefully by sending the QUIT command. However, you must still disconnect from the server before you can open a new 532 * connection. 533 * 534 * @return True if successfully completed, false if not. 535 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 536 */ 537 public boolean logout() throws IOException { 538 return NNTPReply.isPositiveCompletion(quit()); 539 } 540 541 /** 542 * Parse the reply and store the id and number in the pointer. 543 * 544 * @param reply the reply to parse "22n nnn <aaa>" 545 * @param pointer the pointer to update 546 * 547 * @throws MalformedServerReplyException if response could not be parsed 548 */ 549 private void parseArticlePointer(final String reply, final ArticleInfo pointer) throws MalformedServerReplyException { 550 final String[] tokens = reply.split(" "); 551 if (tokens.length >= 3) { // OK, we can parset the line 552 int i = 1; // skip reply code 553 try { 554 // Get article number 555 pointer.articleNumber = Long.parseLong(tokens[i++]); 556 // Get article id 557 pointer.articleId = tokens[i++]; 558 return; // done 559 } catch (final NumberFormatException e) { 560 // drop through and raise exception 561 } 562 } 563 throw new MalformedServerReplyException("Could not parse article pointer.\nServer reply: " + reply); 564 } 565 566 /** 567 * Post an article to the NNTP server. This method returns a DotTerminatedMessageWriter instance to which the article can be written. Null is returned if 568 * the posting attempt fails. You should check {@link NNTP#isAllowedToPost isAllowedToPost() } before trying to post. However, a posting attempt can fail 569 * due to malformed headers. 570 * <p> 571 * You must not issue any commands to the NNTP server (i.e., call any (other methods) until you finish writing to the returned Writer instance and close it. 572 * The NNTP protocol uses the same stream for issuing commands as it does for returning results. Therefore, the returned Writer actually writes directly to 573 * the NNTP connection. After you close the writer, you can execute new commands. If you do not follow these requirements your program will not work 574 * properly. 575 * </p> 576 * <p> 577 * Different NNTP servers will require different header formats, but you can use the provided {@link org.apache.commons.net.nntp.SimpleNNTPHeader} class to 578 * construct the bare minimum acceptable header for most newsreaders. To construct more complicated headers you should refer to RFC 822. When the Java Mail 579 * API is finalized, you will be able to use it to compose fully compliant Internet text messages. The DotTerminatedMessageWriter takes care of doubling 580 * line-leading dots and ending the message with a single dot upon closing, so all you have to worry about is writing the header and the message. 581 * </p> 582 * <p> 583 * Upon closing the returned Writer, you need to call {@link #completePendingCommand completePendingCommand() } to finalize the posting and verify its 584 * success or failure from the server reply. 585 * </p> 586 * 587 * @return A DotTerminatedMessageWriter to which the article (including header) can be written. Returns null if the command fails. 588 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 589 */ 590 591 public Writer postArticle() throws IOException { 592 if (!NNTPReply.isPositiveIntermediate(post())) { 593 return null; 594 } 595 596 return new DotTerminatedMessageWriter(_writer_); 597 } 598 599 private NewsgroupInfo[] readNewsgroupListing() throws IOException { 600 601 // Start of with a big vector because we may be reading a very large 602 // amount of groups. 603 final Vector<NewsgroupInfo> list = new Vector<>(2048); 604 605 String line; 606 try (final BufferedReader reader = new DotTerminatedMessageReader(_reader_)) { 607 while ((line = reader.readLine()) != null) { 608 final NewsgroupInfo tmp = parseNewsgroupListEntry(line); 609 if (tmp == null) { 610 throw new MalformedServerReplyException(line); 611 } 612 list.addElement(tmp); 613 } 614 } 615 final int size; 616 if ((size = list.size()) < 1) { 617 return EMPTY_NEWSGROUP_INFO_ARRAY; 618 } 619 620 final NewsgroupInfo[] info = new NewsgroupInfo[size]; 621 list.copyInto(info); 622 623 return info; 624 } 625 626 private BufferedReader retrieve(final int command, final long articleNumber, final ArticleInfo pointer) throws IOException { 627 if (!NNTPReply.isPositiveCompletion(sendCommand(command, Long.toString(articleNumber)))) { 628 return null; 629 } 630 631 if (pointer != null) { 632 parseArticlePointer(getReplyString(), pointer); 633 } 634 635 return new DotTerminatedMessageReader(_reader_); 636 } 637 638 private BufferedReader retrieve(final int command, final String articleId, final ArticleInfo pointer) throws IOException { 639 if (articleId != null) { 640 if (!NNTPReply.isPositiveCompletion(sendCommand(command, articleId))) { 641 return null; 642 } 643 } else if (!NNTPReply.isPositiveCompletion(sendCommand(command))) { 644 return null; 645 } 646 647 if (pointer != null) { 648 parseArticlePointer(getReplyString(), pointer); 649 } 650 651 return new DotTerminatedMessageReader(_reader_); 652 } 653 654 /** 655 * Same as <code> retrieveArticle((String) null) </code> Note: the return can be cast to a {@link BufferedReader} 656 * 657 * @return A DotTerminatedMessageReader instance from which the article can be read. null if the article does not exist. 658 * @throws IOException if an IO error occurs 659 */ 660 public Reader retrieveArticle() throws IOException { 661 return retrieveArticle((String) null); 662 } 663 664 /** 665 * @param articleNumber The number of the article to retrieve 666 * @return A DotTerminatedMessageReader instance from which the article can be read. null if the article does not exist. 667 * @throws IOException on error 668 * @deprecated 3.0 use {@link #retrieveArticle(long)} instead 669 */ 670 @Deprecated 671 public Reader retrieveArticle(final int articleNumber) throws IOException { 672 return retrieveArticle((long) articleNumber); 673 } 674 675 /** 676 * @param articleNumber The number of the article to retrieve. 677 * @param pointer A parameter through which to return the article's number and unique id 678 * @return A DotTerminatedMessageReader instance from which the article can be read. null if the article does not exist. 679 * @throws IOException on error 680 * @deprecated 3.0 use {@link #retrieveArticle(long, ArticleInfo)} instead 681 */ 682 @Deprecated 683 public Reader retrieveArticle(final int articleNumber, final ArticlePointer pointer) throws IOException { 684 final ArticleInfo ai = ap2ai(pointer); 685 final Reader rdr = retrieveArticle(articleNumber, ai); 686 ai2ap(ai, pointer); 687 return rdr; 688 } 689 690 /** 691 * Same as <code> retrieveArticle(articleNumber, null) </code> 692 * 693 * @param articleNumber the article number to fetch 694 * @return A DotTerminatedMessageReader instance from which the article can be read. null if the article does not exist. 695 * @throws IOException if an IO error occurs 696 */ 697 public BufferedReader retrieveArticle(final long articleNumber) throws IOException { 698 return retrieveArticle(articleNumber, null); 699 } 700 701 /** 702 * Retrieves an article from the currently selected newsgroup. The article is referenced by its article number. The article number and identifier contained 703 * in the server reply are returned through an ArticleInfo. The <code> articleId </code> field of the ArticleInfo cannot always be trusted because some NNTP 704 * servers do not correctly follow the RFC 977 reply format. 705 * <p> 706 * A DotTerminatedMessageReader is returned from which the article can be read. If the article does not exist, null is returned. 707 * </p> 708 * <p> 709 * You must not issue any commands to the NNTP server (i.e., call any other methods) until you finish reading the message from the returned BufferedReader 710 * instance. The NNTP protocol uses the same stream for issuing commands as it does for returning results. Therefore, the returned BufferedReader actually 711 * reads directly from the NNTP connection. After the end of message has been reached, new commands can be executed and their replies read. If you do not 712 * follow these requirements, your program will not work properly. 713 * </p> 714 * 715 * @param articleNumber The number of the article to retrieve. 716 * @param pointer A parameter through which to return the article's number and unique id. The articleId field cannot always be trusted because of 717 * server deviations from RFC 977 reply formats. You may set this parameter to null if you do not desire to retrieve the returned 718 * article information. 719 * @return A DotTerminatedMessageReader instance from which the article can be read. null if the article does not exist. 720 * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason 721 * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or 722 * independently as itself. 723 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 724 */ 725 public BufferedReader retrieveArticle(final long articleNumber, final ArticleInfo pointer) throws IOException { 726 return retrieve(NNTPCommand.ARTICLE, articleNumber, pointer); 727 } 728 729 /** 730 * Same as <code> retrieveArticle(articleId, (ArticleInfo) null) </code> Note: the return can be cast to a {@link BufferedReader} 731 * 732 * @param articleId the article id to retrieve 733 * @return A DotTerminatedMessageReader instance from which the article can be read. null if the article does not exist. 734 * @throws IOException if an IO error occurs 735 */ 736 public Reader retrieveArticle(final String articleId) throws IOException { 737 return retrieveArticle(articleId, (ArticleInfo) null); 738 } 739 740 /** 741 * Retrieves an article from the NNTP server. The article is referenced by its unique article identifier (including the enclosing < and >). The 742 * article number and identifier contained in the server reply are returned through an ArticleInfo. The <code> articleId </code> field of the ArticleInfo 743 * cannot always be trusted because some NNTP servers do not correctly follow the RFC 977 reply format. 744 * <p> 745 * A DotTerminatedMessageReader is returned from which the article can be read. If the article does not exist, null is returned. 746 * </p> 747 * <p> 748 * You must not issue any commands to the NNTP server (i.e., call any other methods) until you finish reading the message from the returned BufferedReader 749 * instance. The NNTP protocol uses the same stream for issuing commands as it does for returning results. Therefore, the returned BufferedReader actually 750 * reads directly from the NNTP connection. After the end of message has been reached, new commands can be executed and their replies read. If you do not 751 * follow these requirements, your program will not work properly. 752 * </p> 753 * 754 * @param articleId The unique article identifier of the article to retrieve. If this parameter is null, the currently selected article is retrieved. 755 * @param pointer A parameter through which to return the article's number and unique id. The articleId field cannot always be trusted because of server 756 * deviations from RFC 977 reply formats. You may set this parameter to null if you do not desire to retrieve the returned article 757 * information. 758 * @return A DotTerminatedMessageReader instance from which the article can be read. null if the article does not exist. 759 * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason 760 * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or 761 * independently as itself. 762 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 763 */ 764 public BufferedReader retrieveArticle(final String articleId, final ArticleInfo pointer) throws IOException { 765 return retrieve(NNTPCommand.ARTICLE, articleId, pointer); 766 767 } 768 769 /** 770 * @param articleId The unique article identifier of the article to retrieve 771 * @param pointer A parameter through which to return the article's number and unique id 772 * @deprecated 3.0 use {@link #retrieveArticle(String, ArticleInfo)} instead 773 * @return A DotTerminatedMessageReader instance from which the article can be read. null if the article does not exist. 774 * @throws IOException on error 775 */ 776 @Deprecated 777 public Reader retrieveArticle(final String articleId, final ArticlePointer pointer) throws IOException { 778 final ArticleInfo ai = ap2ai(pointer); 779 final Reader rdr = retrieveArticle(articleId, ai); 780 ai2ap(ai, pointer); 781 return rdr; 782 } 783 784 /** 785 * Same as <code> retrieveArticleBody(null) </code> Note: the return can be cast to a {@link BufferedReader} 786 * 787 * @return A DotTerminatedMessageReader instance from which the article body can be read. null if the article does not exist. 788 * @throws IOException if an error occurs 789 */ 790 public Reader retrieveArticleBody() throws IOException { 791 return retrieveArticleBody(null); 792 } 793 794 /** 795 * @param a tba 796 * @return tba 797 * @throws IOException tba 798 * @deprecated 3.0 use {@link #retrieveArticleBody(long)} instead 799 */ 800 @Deprecated 801 public Reader retrieveArticleBody(final int a) throws IOException { 802 return retrieveArticleBody((long) a); 803 } 804 805 /** 806 * @param a tba 807 * @param ap tba 808 * @return tba 809 * @throws IOException tba 810 * @deprecated 3.0 use {@link #retrieveArticleBody(long, ArticleInfo)} instead 811 */ 812 @Deprecated 813 public Reader retrieveArticleBody(final int a, final ArticlePointer ap) throws IOException { 814 final ArticleInfo ai = ap2ai(ap); 815 final Reader rdr = retrieveArticleBody(a, ai); 816 ai2ap(ai, ap); 817 return rdr; 818 } 819 820 /** 821 * Same as <code> retrieveArticleBody(articleNumber, null) </code> 822 * 823 * @param articleNumber the article number 824 * @return the reader 825 * @throws IOException if an error occurs 826 */ 827 public BufferedReader retrieveArticleBody(final long articleNumber) throws IOException { 828 return retrieveArticleBody(articleNumber, null); 829 } 830 831 /** 832 * Retrieves an article body from the currently selected newsgroup. The article is referenced by its article number. The article number and identifier 833 * contained in the server reply are returned through an ArticleInfo. The <code> articleId </code> field of the ArticleInfo cannot always be trusted because 834 * some NNTP servers do not correctly follow the RFC 977 reply format. 835 * <p> 836 * A DotTerminatedMessageReader is returned from which the article can be read. If the article does not exist, null is returned. 837 * </p> 838 * <p> 839 * You must not issue any commands to the NNTP server (i.e., call any other methods) until you finish reading the message from the returned BufferedReader 840 * instance. The NNTP protocol uses the same stream for issuing commands as it does for returning results. Therefore, the returned BufferedReader actually 841 * reads directly from the NNTP connection. After the end of message has been reached, new commands can be executed and their replies read. If you do not 842 * follow these requirements, your program will not work properly. 843 * </p> 844 * 845 * @param articleNumber The number of the article whose body is being retrieved. 846 * @param pointer A parameter through which to return the article's number and unique id. The articleId field cannot always be trusted because of 847 * server deviations from RFC 977 reply formats. You may set this parameter to null if you do not desire to retrieve the returned 848 * article information. 849 * @return A DotTerminatedMessageReader instance from which the article body can be read. null if the article does not exist. 850 * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason 851 * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or 852 * independently as itself. 853 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 854 */ 855 public BufferedReader retrieveArticleBody(final long articleNumber, final ArticleInfo pointer) throws IOException { 856 return retrieve(NNTPCommand.BODY, articleNumber, pointer); 857 } 858 859 /** 860 * Same as <code> retrieveArticleBody(articleId, (ArticleInfo) null) </code> Note: the return can be cast to a {@link BufferedReader} 861 * 862 * @param articleId the article id 863 * @return A DotTerminatedMessageReader instance from which the article body can be read. null if the article does not exist. 864 * @throws IOException if an error occurs 865 */ 866 public Reader retrieveArticleBody(final String articleId) throws IOException { 867 return retrieveArticleBody(articleId, (ArticleInfo) null); 868 } 869 870 /** 871 * Retrieves an article body from the NNTP server. The article is referenced by its unique article identifier (including the enclosing < and >). The 872 * article number and identifier contained in the server reply are returned through an ArticleInfo. The <code> articleId </code> field of the ArticleInfo 873 * cannot always be trusted because some NNTP servers do not correctly follow the RFC 977 reply format. 874 * <p> 875 * A DotTerminatedMessageReader is returned from which the article can be read. If the article does not exist, null is returned. 876 * </p> 877 * <p> 878 * You must not issue any commands to the NNTP server (i.e., call any other methods) until you finish reading the message from the returned BufferedReader 879 * instance. The NNTP protocol uses the same stream for issuing commands as it does for returning results. Therefore, the returned BufferedReader actually 880 * reads directly from the NNTP connection. After the end of message has been reached, new commands can be executed and their replies read. If you do not 881 * follow these requirements, your program will not work properly. 882 * </p> 883 * 884 * @param articleId The unique article identifier of the article whose body is being retrieved. If this parameter is null, the body of the currently 885 * selected article is retrieved. 886 * @param pointer A parameter through which to return the article's number and unique id. The articleId field cannot always be trusted because of server 887 * deviations from RFC 977 reply formats. You may set this parameter to null if you do not desire to retrieve the returned article 888 * information. 889 * @return A DotTerminatedMessageReader instance from which the article body can be read. null if the article does not exist. 890 * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason 891 * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or 892 * independently as itself. 893 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 894 */ 895 public BufferedReader retrieveArticleBody(final String articleId, final ArticleInfo pointer) throws IOException { 896 return retrieve(NNTPCommand.BODY, articleId, pointer); 897 898 } 899 900 /** 901 * @param articleId The unique article identifier of the article to retrieve 902 * @param pointer A parameter through which to return the article's number and unique id 903 * @return A DotTerminatedMessageReader instance from which the article body can be read. null if the article does not exist. 904 * @throws IOException on error 905 * @deprecated 3.0 use {@link #retrieveArticleBody(String, ArticleInfo)} instead 906 */ 907 @Deprecated 908 public Reader retrieveArticleBody(final String articleId, final ArticlePointer pointer) throws IOException { 909 final ArticleInfo ai = ap2ai(pointer); 910 final Reader rdr = retrieveArticleBody(articleId, ai); 911 ai2ap(ai, pointer); 912 return rdr; 913 } 914 915 /** 916 * Same as <code> retrieveArticleHeader((String) null) </code> Note: the return can be cast to a {@link BufferedReader} 917 * 918 * @return the reader 919 * @throws IOException if an error occurs 920 */ 921 public Reader retrieveArticleHeader() throws IOException { 922 return retrieveArticleHeader((String) null); 923 } 924 925 /** 926 * @param a tba 927 * @return tba 928 * @throws IOException tba 929 * @deprecated 3.0 use {@link #retrieveArticleHeader(long)} instead 930 */ 931 @Deprecated 932 public Reader retrieveArticleHeader(final int a) throws IOException { 933 return retrieveArticleHeader((long) a); 934 } 935 936 /** 937 * @param a tba 938 * @param ap tba 939 * @return tba 940 * @throws IOException tba 941 * @deprecated 3.0 use {@link #retrieveArticleHeader(long, ArticleInfo)} instead 942 */ 943 @Deprecated 944 public Reader retrieveArticleHeader(final int a, final ArticlePointer ap) throws IOException { 945 final ArticleInfo ai = ap2ai(ap); 946 final Reader rdr = retrieveArticleHeader(a, ai); 947 ai2ap(ai, ap); 948 return rdr; 949 } 950 951 /** 952 * Same as <code> retrieveArticleHeader(articleNumber, null) </code> 953 * 954 * @param articleNumber the article number 955 * @return the reader 956 * @throws IOException if an error occurs 957 */ 958 public BufferedReader retrieveArticleHeader(final long articleNumber) throws IOException { 959 return retrieveArticleHeader(articleNumber, null); 960 } 961 962 /** 963 * Retrieves an article header from the currently selected newsgroup. The article is referenced by its article number. The article number and identifier 964 * contained in the server reply are returned through an ArticleInfo. The <code> articleId </code> field of the ArticleInfo cannot always be trusted because 965 * some NNTP servers do not correctly follow the RFC 977 reply format. 966 * <p> 967 * A DotTerminatedMessageReader is returned from which the article can be read. If the article does not exist, null is returned. 968 * </p> 969 * <p> 970 * You must not issue any commands to the NNTP server (i.e., call any other methods) until you finish reading the message from the returned BufferedReader 971 * instance. The NNTP protocol uses the same stream for issuing commands as it does for returning results. Therefore, the returned BufferedReader actually 972 * reads directly from the NNTP connection. After the end of message has been reached, new commands can be executed and their replies read. If you do not 973 * follow these requirements, your program will not work properly. 974 * </p> 975 * 976 * @param articleNumber The number of the article whose header is being retrieved. 977 * @param pointer A parameter through which to return the article's number and unique id. The articleId field cannot always be trusted because of 978 * server deviations from RFC 977 reply formats. You may set this parameter to null if you do not desire to retrieve the returned 979 * article information. 980 * @return A DotTerminatedMessageReader instance from which the article header can be read. null if the article does not exist. 981 * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason 982 * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or 983 * independently as itself. 984 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 985 */ 986 public BufferedReader retrieveArticleHeader(final long articleNumber, final ArticleInfo pointer) throws IOException { 987 return retrieve(NNTPCommand.HEAD, articleNumber, pointer); 988 } 989 990 /** 991 * Same as <code> retrieveArticleHeader(articleId, (ArticleInfo) null) </code> Note: the return can be cast to a {@link BufferedReader} 992 * 993 * @param articleId the article id to fetch 994 * @return the reader 995 * @throws IOException if an error occurs 996 */ 997 public Reader retrieveArticleHeader(final String articleId) throws IOException { 998 return retrieveArticleHeader(articleId, (ArticleInfo) null); 999 } 1000 1001 /** 1002 * Retrieves an article header from the NNTP server. The article is referenced by its unique article identifier (including the enclosing < and >). The 1003 * article number and identifier contained in the server reply are returned through an ArticleInfo. The <code> articleId </code> field of the ArticleInfo 1004 * cannot always be trusted because some NNTP servers do not correctly follow the RFC 977 reply format. 1005 * <p> 1006 * A DotTerminatedMessageReader is returned from which the article can be read. If the article does not exist, null is returned. 1007 * </p> 1008 * <p> 1009 * You must not issue any commands to the NNTP server (i.e., call any other methods) until you finish reading the message from the returned BufferedReader 1010 * instance. The NNTP protocol uses the same stream for issuing commands as it does for returning results. Therefore, the returned BufferedReader actually 1011 * reads directly from the NNTP connection. After the end of message has been reached, new commands can be executed and their replies read. If you do not 1012 * follow these requirements, your program will not work properly. 1013 * </p> 1014 * 1015 * @param articleId The unique article identifier of the article whose header is being retrieved. If this parameter is null, the header of the currently 1016 * selected article is retrieved. 1017 * @param pointer A parameter through which to return the article's number and unique id. The articleId field cannot always be trusted because of server 1018 * deviations from RFC 977 reply formats. You may set this parameter to null if you do not desire to retrieve the returned article 1019 * information. 1020 * @return A DotTerminatedMessageReader instance from which the article header can be read. null if the article does not exist. 1021 * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason 1022 * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or 1023 * independently as itself. 1024 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 1025 */ 1026 public BufferedReader retrieveArticleHeader(final String articleId, final ArticleInfo pointer) throws IOException { 1027 return retrieve(NNTPCommand.HEAD, articleId, pointer); 1028 1029 } 1030 1031 /** 1032 * @param articleId The unique article identifier of the article to retrieve 1033 * @param pointer A parameter through which to return the article's number and unique id 1034 * @return A DotTerminatedMessageReader instance from which the article body can be read. null if the article does not exist. 1035 * @throws IOException on error 1036 * @deprecated 3.0 use {@link #retrieveArticleHeader(String, ArticleInfo)} instead 1037 */ 1038 @Deprecated 1039 public Reader retrieveArticleHeader(final String articleId, final ArticlePointer pointer) throws IOException { 1040 final ArticleInfo ai = ap2ai(pointer); 1041 final Reader rdr = retrieveArticleHeader(articleId, ai); 1042 ai2ap(ai, pointer); 1043 return rdr; 1044 } 1045 1046 /** 1047 * @param lowArticleNumber to fetch 1048 * @return a DotTerminatedReader if successful, null otherwise 1049 * @throws IOException tba 1050 * @deprecated 3.0 use {@link #retrieveArticleInfo(long)} instead 1051 */ 1052 @Deprecated 1053 public Reader retrieveArticleInfo(final int lowArticleNumber) throws IOException { 1054 return retrieveArticleInfo((long) lowArticleNumber); 1055 } 1056 1057 /** 1058 * @param lowArticleNumber to fetch 1059 * @param highArticleNumber to fetch 1060 * @return a DotTerminatedReader if successful, null otherwise 1061 * @throws IOException on error 1062 * @deprecated 3.0 use {@link #retrieveArticleInfo(long, long)} instead 1063 */ 1064 @Deprecated 1065 public Reader retrieveArticleInfo(final int lowArticleNumber, final int highArticleNumber) throws IOException { 1066 return retrieveArticleInfo((long) lowArticleNumber, (long) highArticleNumber); 1067 } 1068 1069 /** 1070 * Return article headers for a specified post. 1071 * 1072 * @param articleNumber the article to retrieve headers for 1073 * @return a DotTerminatedReader if successful, null otherwise 1074 * @throws IOException on error 1075 */ 1076 public BufferedReader retrieveArticleInfo(final long articleNumber) throws IOException { 1077 return retrieveArticleInfo(Long.toString(articleNumber)); 1078 } 1079 1080 /** 1081 * Return article headers for all articles between lowArticleNumber and highArticleNumber, inclusively. Uses the XOVER command. 1082 * 1083 * @param lowArticleNumber low number 1084 * @param highArticleNumber high number 1085 * @return a DotTerminatedReader if successful, null otherwise 1086 * @throws IOException on error 1087 */ 1088 public BufferedReader retrieveArticleInfo(final long lowArticleNumber, final long highArticleNumber) throws IOException { 1089 return retrieveArticleInfo(lowArticleNumber + "-" + highArticleNumber); 1090 } 1091 1092 /** 1093 * Private implementation of XOVER functionality. 1094 * 1095 * See {@link NNTP#xover} for legal agument formats. Alternatively, read RFC 2980 :-) 1096 * 1097 * @param articleRange 1098 * @return Returns a DotTerminatedMessageReader if successful, null otherwise 1099 * @throws IOException 1100 */ 1101 private BufferedReader retrieveArticleInfo(final String articleRange) throws IOException { 1102 if (!NNTPReply.isPositiveCompletion(xover(articleRange))) { 1103 return null; 1104 } 1105 1106 return new DotTerminatedMessageReader(_reader_); 1107 } 1108 1109 /** 1110 * @param a tba 1111 * @param b tba 1112 * @return tba 1113 * @throws IOException tba 1114 * @deprecated 3.0 use {@link #retrieveHeader(String, long)} instead 1115 */ 1116 @Deprecated 1117 public Reader retrieveHeader(final String a, final int b) throws IOException { 1118 return retrieveHeader(a, (long) b); 1119 } 1120 1121 /** 1122 * @param header the header 1123 * @param lowArticleNumber to fetch 1124 * @param highArticleNumber to fetch 1125 * @return a DotTerminatedReader if successful, null otherwise 1126 * @throws IOException on error 1127 * @deprecated 3.0 use {@link #retrieveHeader(String, long, long)} instead 1128 */ 1129 @Deprecated 1130 public Reader retrieveHeader(final String header, final int lowArticleNumber, final int highArticleNumber) throws IOException { 1131 return retrieveHeader(header, (long) lowArticleNumber, (long) highArticleNumber); 1132 } 1133 1134 /** 1135 * Return an article header for a specified post. 1136 * 1137 * @param header the header to retrieve 1138 * @param articleNumber the article to retrieve the header for 1139 * @return a DotTerminatedReader if successful, null otherwise 1140 * @throws IOException on error 1141 */ 1142 public BufferedReader retrieveHeader(final String header, final long articleNumber) throws IOException { 1143 return retrieveHeader(header, Long.toString(articleNumber)); 1144 } 1145 1146 /** 1147 * Return an article header for all articles between lowArticleNumber and highArticleNumber, inclusively. 1148 * 1149 * @param header the header 1150 * @param lowArticleNumber to fetch 1151 * @param highArticleNumber to fetch 1152 * @return a DotTerminatedReader if successful, null otherwise 1153 * @throws IOException on error 1154 */ 1155 public BufferedReader retrieveHeader(final String header, final long lowArticleNumber, final long highArticleNumber) throws IOException { 1156 return retrieveHeader(header, lowArticleNumber + "-" + highArticleNumber); 1157 } 1158 1159 /** 1160 * Private implementation of XHDR functionality. 1161 * <p> 1162 * See {@link NNTP#xhdr} for legal argument formats. Alternatively, read RFC 1036. 1163 * </p> 1164 * 1165 * @param header 1166 * @param articleRange 1167 * @return Returns a {@link DotTerminatedMessageReader} if successful, {@code null} otherwise 1168 * @throws IOException 1169 */ 1170 private BufferedReader retrieveHeader(final String header, final String articleRange) throws IOException { 1171 if (!NNTPReply.isPositiveCompletion(xhdr(header, articleRange))) { 1172 return null; 1173 } 1174 1175 return new DotTerminatedMessageReader(_reader_); 1176 } 1177 1178 /*** 1179 * Same as <code> selectArticle((String) null, articleId) </code>. Useful for retrieving the current article number. 1180 * 1181 * @param pointer to the article 1182 * @return true if OK 1183 * @throws IOException on error 1184 */ 1185 public boolean selectArticle(final ArticleInfo pointer) throws IOException { 1186 return selectArticle(null, pointer); 1187 } 1188 1189 /** 1190 * @param pointer A parameter through which to return the article's number and unique id 1191 * @return True if successful, false if not. 1192 * @throws IOException on error 1193 * @deprecated 3.0 use {@link #selectArticle(ArticleInfo)} instead 1194 */ 1195 @Deprecated 1196 public boolean selectArticle(final ArticlePointer pointer) throws IOException { 1197 final ArticleInfo ai = ap2ai(pointer); 1198 final boolean b = selectArticle(ai); 1199 ai2ap(ai, pointer); 1200 return b; 1201 1202 } 1203 1204 /** 1205 * @param a tba 1206 * @return tba 1207 * @throws IOException tba 1208 * @deprecated 3.0 use {@link #selectArticle(long)} instead 1209 */ 1210 @Deprecated 1211 public boolean selectArticle(final int a) throws IOException { 1212 return selectArticle((long) a); 1213 } 1214 1215 /** 1216 * @param a tba 1217 * @param ap tba 1218 * @return tba 1219 * @throws IOException tba 1220 * @deprecated 3.0 use {@link #selectArticle(long, ArticleInfo)} instead 1221 */ 1222 @Deprecated 1223 public boolean selectArticle(final int a, final ArticlePointer ap) throws IOException { 1224 final ArticleInfo ai = ap2ai(ap); 1225 final boolean b = selectArticle(a, ai); 1226 ai2ap(ai, ap); 1227 return b; 1228 } 1229 1230 /** 1231 * Same as <code> selectArticle(articleNumber, null) </code> 1232 * 1233 * @param articleNumber the numger 1234 * @return true if successful 1235 * @throws IOException on error 1236 */ 1237 public boolean selectArticle(final long articleNumber) throws IOException { 1238 return selectArticle(articleNumber, null); 1239 } 1240 1241 /** 1242 * Select an article in the currently selected newsgroup by its number. and return its article number and id through the pointer parameter. This is achieved 1243 * through the STAT command. According to RFC 977, this WILL set the current article pointer on the server. Use this command to select an article before 1244 * retrieving it, or to obtain an article's unique identifier given its number. 1245 * 1246 * @param articleNumber The number of the article to select from the currently selected newsgroup. 1247 * @param pointer A parameter through which to return the article's number and unique id. Although the articleId field cannot always be trusted 1248 * because of server deviations from RFC 977 reply formats, we haven't found a server that misformats this information in response to 1249 * this particular command. You may set this parameter to null if you do not desire to retrieve the returned article information. 1250 * @return True if successful, false if not. 1251 * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason 1252 * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or 1253 * independently as itself. 1254 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 1255 */ 1256 public boolean selectArticle(final long articleNumber, final ArticleInfo pointer) throws IOException { 1257 if (!NNTPReply.isPositiveCompletion(stat(articleNumber))) { 1258 return false; 1259 } 1260 1261 if (pointer != null) { 1262 parseArticlePointer(getReplyString(), pointer); 1263 } 1264 1265 return true; 1266 } 1267 1268 /** 1269 * Same as <code> selectArticle(articleId, (ArticleInfo) null) </code> 1270 * 1271 * @param articleId the article's Id 1272 * @return true if successful 1273 * @throws IOException on error 1274 */ 1275 public boolean selectArticle(final String articleId) throws IOException { 1276 return selectArticle(articleId, (ArticleInfo) null); 1277 } 1278 1279 /** 1280 * Select an article by its unique identifier (including enclosing < and >) and return its article number and id through the pointer parameter. This 1281 * is achieved through the STAT command. According to RFC 977, this will NOT set the current article pointer on the server. To do that, you must reference 1282 * the article by its number. 1283 * 1284 * @param articleId The unique article identifier of the article that is being selectedd. If this parameter is null, the body of the current article is 1285 * selected 1286 * @param pointer A parameter through which to return the article's number and unique id. The articleId field cannot always be trusted because of server 1287 * deviations from RFC 977 reply formats. You may set this parameter to null if you do not desire to retrieve the returned article 1288 * information. 1289 * @return True if successful, false if not. 1290 * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason 1291 * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or 1292 * independently as itself. 1293 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 1294 */ 1295 public boolean selectArticle(final String articleId, final ArticleInfo pointer) throws IOException { 1296 if (articleId != null) { 1297 if (!NNTPReply.isPositiveCompletion(stat(articleId))) { 1298 return false; 1299 } 1300 } else if (!NNTPReply.isPositiveCompletion(stat())) { 1301 return false; 1302 } 1303 1304 if (pointer != null) { 1305 parseArticlePointer(getReplyString(), pointer); 1306 } 1307 1308 return true; 1309 } 1310 1311 /** 1312 * @param articleId The unique article identifier of the article to retrieve 1313 * @param pointer A parameter through which to return the article's number and unique id 1314 * @return A DotTerminatedMessageReader instance from which the article body can be read. null if the article does not exist. 1315 * @throws IOException on error 1316 * @deprecated 3.0 use {@link #selectArticle(String, ArticleInfo)} instead 1317 */ 1318 @Deprecated 1319 public boolean selectArticle(final String articleId, final ArticlePointer pointer) throws IOException { 1320 final ArticleInfo ai = ap2ai(pointer); 1321 final boolean b = selectArticle(articleId, ai); 1322 ai2ap(ai, pointer); 1323 return b; 1324 1325 } 1326 1327 /** 1328 * Same as <code> selectNewsgroup(newsgroup, null) </code> 1329 * 1330 * @param newsgroup the newsgroup name 1331 * @return true if newsgroup exist and was selected 1332 * @throws IOException if an error occurs 1333 */ 1334 public boolean selectNewsgroup(final String newsgroup) throws IOException { 1335 return selectNewsgroup(newsgroup, null); 1336 } 1337 1338 /** 1339 * Select the specified newsgroup to be the target of for future article retrieval and posting operations. Also return the newsgroup information contained 1340 * in the server reply through the info parameter. 1341 * 1342 * @param newsgroup The newsgroup to select. 1343 * @param info A parameter through which the newsgroup information of the selected newsgroup contained in the server reply is returned. Set this to 1344 * null if you do not desire this information. 1345 * @return True if the newsgroup exists and was selected, false otherwise. 1346 * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason 1347 * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or 1348 * independently as itself. 1349 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 1350 */ 1351 public boolean selectNewsgroup(final String newsgroup, final NewsgroupInfo info) throws IOException { 1352 if (!NNTPReply.isPositiveCompletion(group(newsgroup))) { 1353 return false; 1354 } 1355 1356 if (info != null) { 1357 parseGroupReply(getReplyString(), info); 1358 } 1359 1360 return true; 1361 } 1362 1363 /** 1364 * Same as <code> selectNextArticle((ArticleInfo) null) </code> 1365 * 1366 * @return true if successful 1367 * @throws IOException on error 1368 */ 1369 public boolean selectNextArticle() throws IOException { 1370 return selectNextArticle((ArticleInfo) null); 1371 } 1372 1373 /** 1374 * Select the article following the currently selected article in the currently selected newsgroup and return its number and unique id through the pointer 1375 * parameter. Because of deviating server implementations, the articleId information cannot be trusted. To obtain the article identifier, issue a 1376 * <code> selectArticle(pointer.articleNumber, pointer) </code> immediately afterward. 1377 * 1378 * @param pointer A parameter through which to return the article's number and unique id. The articleId field cannot always be trusted because of server 1379 * deviations from RFC 977 reply formats. You may set this parameter to null if you do not desire to retrieve the returned article 1380 * information. 1381 * @return True if successful, false if not (e.g., there is no following article). 1382 * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason 1383 * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or 1384 * independently as itself. 1385 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 1386 */ 1387 public boolean selectNextArticle(final ArticleInfo pointer) throws IOException { 1388 if (!NNTPReply.isPositiveCompletion(next())) { 1389 return false; 1390 } 1391 1392 if (pointer != null) { 1393 parseArticlePointer(getReplyString(), pointer); 1394 } 1395 1396 return true; 1397 } 1398 1399 /** 1400 * @param pointer A parameter through which to return the article's number and unique id 1401 * @return True if successful, false if not. 1402 * @throws IOException on error 1403 * @deprecated 3.0 use {@link #selectNextArticle(ArticleInfo)} instead 1404 */ 1405 @Deprecated 1406 public boolean selectNextArticle(final ArticlePointer pointer) throws IOException { 1407 final ArticleInfo ai = ap2ai(pointer); 1408 final boolean b = selectNextArticle(ai); 1409 ai2ap(ai, pointer); 1410 return b; 1411 1412 } 1413 1414 /** 1415 * Same as <code> selectPreviousArticle((ArticleInfo) null) </code> 1416 * 1417 * @return true if successful 1418 * @throws IOException on error 1419 */ 1420 public boolean selectPreviousArticle() throws IOException { 1421 return selectPreviousArticle((ArticleInfo) null); 1422 } 1423 1424 // Helper methods 1425 1426 /** 1427 * Select the article preceding the currently selected article in the currently selected newsgroup and return its number and unique id through the pointer 1428 * parameter. Because of deviating server implementations, the articleId information cannot be trusted. To obtain the article identifier, issue a 1429 * <code> selectArticle(pointer.articleNumber, pointer) </code> immediately afterward. 1430 * 1431 * @param pointer A parameter through which to return the article's number and unique id. The articleId field cannot always be trusted because of server 1432 * deviations from RFC 977 reply formats. You may set this parameter to null if you do not desire to retrieve the returned article 1433 * information. 1434 * @return True if successful, false if not (e.g., there is no previous article). 1435 * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason 1436 * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or 1437 * independently as itself. 1438 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 1439 */ 1440 public boolean selectPreviousArticle(final ArticleInfo pointer) throws IOException { 1441 if (!NNTPReply.isPositiveCompletion(last())) { 1442 return false; 1443 } 1444 1445 if (pointer != null) { 1446 parseArticlePointer(getReplyString(), pointer); 1447 } 1448 1449 return true; 1450 } 1451 1452 /** 1453 * @param pointer A parameter through which to return the article's number and unique id 1454 * @return True if successful, false if not. 1455 * @throws IOException on error 1456 * @deprecated 3.0 use {@link #selectPreviousArticle(ArticleInfo)} instead 1457 */ 1458 @Deprecated 1459 public boolean selectPreviousArticle(final ArticlePointer pointer) throws IOException { 1460 final ArticleInfo ai = ap2ai(pointer); 1461 final boolean b = selectPreviousArticle(ai); 1462 ai2ap(ai, pointer); 1463 return b; 1464 } 1465 }