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