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