1. /*
  2. * Copyright 2001-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.net.nntp;
  17. import java.io.BufferedReader;
  18. import java.io.IOException;
  19. import java.io.Reader;
  20. import java.io.StringWriter;
  21. import java.io.Writer;
  22. import java.util.StringTokenizer;
  23. import java.util.Vector;
  24. import org.apache.commons.net.io.DotTerminatedMessageReader;
  25. import org.apache.commons.net.io.DotTerminatedMessageWriter;
  26. import org.apache.commons.net.io.Util;
  27. import org.apache.commons.net.MalformedServerReplyException;
  28. /***
  29. * NNTPClient encapsulates all the functionality necessary to post and
  30. * retrieve articles from an NNTP server. As with all classes derived
  31. * from <a href="org.apache.commons.net.SocketClient.html"> SocketClient </a>,
  32. * you must first connect to the server with
  33. * <a href="org.apache.commons.net.SocketClient.html#connect"> connect </a>
  34. * before doing anything, and finally
  35. * <a href="org.apache.commons.net.nntp.NNTP.html#disconnect"> disconnect() </a>
  36. * after you're completely finished interacting with the server.
  37. * Remember that the
  38. * <a href="org.apache.commons.net.nntp.NNTP.html#isAllowedToPost">
  39. * isAllowedToPost()</a> method is defined in
  40. * <a href="org.apache.commons.net.nntp.NNTP.html"> NNTP </a>.
  41. * <p>
  42. * You should keep in mind that the NNTP server may choose to prematurely
  43. * close a connection if the client has been idle for longer than a
  44. * given time period or if the server is being shutdown by the operator or
  45. * some other reason. The NNTP class will detect a
  46. * premature NNTP server connection closing when it receives a
  47. * <a href="org.apache.commons.net.nntp.NNTPReply.html#SERVICE_DISCONTINUED">
  48. * NNTPReply.SERVICE_DISCONTINUED </a> response to a command.
  49. * When that occurs, the NNTP class method encountering that reply will throw
  50. * an <a href="org.apache.commons.net.nntp.NNTPConnectionClosedException.html">
  51. * NNTPConnectionClosedException </a>.
  52. * <code>NNTPConectionClosedException</code>
  53. * is a subclass of <code> IOException </code> and therefore need not be
  54. * caught separately, but if you are going to catch it separately, its
  55. * catch block must appear before the more general <code> IOException </code>
  56. * catch block. When you encounter an
  57. * <a href="org.apache.commons.net.nntp.NNTPConnectionClosedException.html">
  58. * NNTPConnectionClosedException </a>, you must disconnect the connection with
  59. * <a href="org.apache.commons.net.nntp.NNTP.html#disconnect"> disconnect() </a>
  60. * to properly clean up the
  61. * system resources used by NNTP. Before disconnecting, you may check the
  62. * last reply code and text with
  63. * <a href="org.apache.commons.net.nntp.NNTP.html#getReplyCode"> getReplyCode </a> and
  64. * <a href="org.apache.commons.net.nntp.NNTP.html#getReplyString"> getReplyString </a>.
  65. * <p>
  66. * Rather than list it separately for each method, we mention here that
  67. * every method communicating with the server and throwing an IOException
  68. * can also throw a
  69. * <a href="org.apache.commons.net.MalformedServerReplyException.html">
  70. * MalformedServerReplyException </a>, which is a subclass
  71. * of IOException. A MalformedServerReplyException will be thrown when
  72. * the reply received from the server deviates enough from the protocol
  73. * specification that it cannot be interpreted in a useful manner despite
  74. * attempts to be as lenient as possible.
  75. * <p>
  76. * <p>
  77. * @author Daniel F. Savarese
  78. * @author Rory Winston
  79. * @author Ted Wise
  80. * @see NNTP
  81. * @see NNTPConnectionClosedException
  82. * @see org.apache.commons.net.MalformedServerReplyException
  83. ***/
  84. public class NNTPClient extends NNTP
  85. {
  86. private void __parseArticlePointer(String reply, ArticlePointer pointer)
  87. throws MalformedServerReplyException
  88. {
  89. StringTokenizer tokenizer;
  90. // Do loop is a kluge to simulate goto
  91. do
  92. {
  93. tokenizer = new StringTokenizer(reply);
  94. if (tokenizer.countTokens() < 3)
  95. break;
  96. // Skip numeric response value
  97. tokenizer.nextToken();
  98. // Get article number
  99. try
  100. {
  101. pointer.articleNumber = Integer.parseInt(tokenizer.nextToken());
  102. }
  103. catch (NumberFormatException e)
  104. {
  105. break;
  106. }
  107. // Get article id
  108. pointer.articleId = tokenizer.nextToken();
  109. return ;
  110. }
  111. while (false);
  112. throw new MalformedServerReplyException(
  113. "Could not parse article pointer.\nServer reply: " + reply);
  114. }
  115. private void __parseGroupReply(String reply, NewsgroupInfo info)
  116. throws MalformedServerReplyException
  117. {
  118. String count, first, last;
  119. StringTokenizer tokenizer;
  120. // Do loop is a kluge to simulate goto
  121. do
  122. {
  123. tokenizer = new StringTokenizer(reply);
  124. if (tokenizer.countTokens() < 5)
  125. break;
  126. // Skip numeric response value
  127. tokenizer.nextToken();
  128. // Get estimated article count
  129. count = tokenizer.nextToken();
  130. // Get first article number
  131. first = tokenizer.nextToken();
  132. // Get last article number
  133. last = tokenizer.nextToken();
  134. // Get newsgroup name
  135. info._setNewsgroup(tokenizer.nextToken());
  136. try
  137. {
  138. info._setArticleCount(Integer.parseInt(count));
  139. info._setFirstArticle(Integer.parseInt(first));
  140. info._setLastArticle(Integer.parseInt(last));
  141. }
  142. catch (NumberFormatException e)
  143. {
  144. break;
  145. }
  146. info._setPostingPermission(NewsgroupInfo.UNKNOWN_POSTING_PERMISSION);
  147. return ;
  148. }
  149. while (false);
  150. throw new MalformedServerReplyException(
  151. "Could not parse newsgroup info.\nServer reply: " + reply);
  152. }
  153. private NewsgroupInfo __parseNewsgroupListEntry(String entry)
  154. {
  155. NewsgroupInfo result;
  156. StringTokenizer tokenizer;
  157. int lastNum, firstNum;
  158. String last, first, permission;
  159. result = new NewsgroupInfo();
  160. tokenizer = new StringTokenizer(entry);
  161. if (tokenizer.countTokens() < 4)
  162. return null;
  163. result._setNewsgroup(tokenizer.nextToken());
  164. last = tokenizer.nextToken();
  165. first = tokenizer.nextToken();
  166. permission = tokenizer.nextToken();
  167. try
  168. {
  169. lastNum = Integer.parseInt(last);
  170. firstNum = Integer.parseInt(first);
  171. result._setFirstArticle(firstNum);
  172. result._setLastArticle(lastNum);
  173. result._setArticleCount(lastNum - firstNum + 1);
  174. }
  175. catch (NumberFormatException e)
  176. {
  177. return null;
  178. }
  179. switch (permission.charAt(0))
  180. {
  181. case 'y':
  182. case 'Y':
  183. result._setPostingPermission(
  184. NewsgroupInfo.PERMITTED_POSTING_PERMISSION);
  185. break;
  186. case 'n':
  187. case 'N':
  188. result._setPostingPermission(
  189. NewsgroupInfo.PROHIBITED_POSTING_PERMISSION);
  190. break;
  191. case 'm':
  192. case 'M':
  193. result._setPostingPermission(
  194. NewsgroupInfo.MODERATED_POSTING_PERMISSION);
  195. break;
  196. default:
  197. result._setPostingPermission(
  198. NewsgroupInfo.UNKNOWN_POSTING_PERMISSION);
  199. break;
  200. }
  201. return result;
  202. }
  203. private NewsgroupInfo[] __readNewsgroupListing() throws IOException
  204. {
  205. int size;
  206. String line;
  207. Vector list;
  208. BufferedReader reader;
  209. NewsgroupInfo tmp, info[];
  210. reader = new BufferedReader(new DotTerminatedMessageReader(_reader_));
  211. // Start of with a big vector because we may be reading a very large
  212. // amount of groups.
  213. list = new Vector(2048);
  214. while ((line = reader.readLine()) != null)
  215. {
  216. tmp = __parseNewsgroupListEntry(line);
  217. if (tmp != null)
  218. list.addElement(tmp);
  219. else
  220. throw new MalformedServerReplyException(line);
  221. }
  222. if ((size = list.size()) < 1)
  223. return new NewsgroupInfo[0];
  224. info = new NewsgroupInfo[size];
  225. list.copyInto(info);
  226. return info;
  227. }
  228. private Reader __retrieve(int command,
  229. String articleId, ArticlePointer pointer)
  230. throws IOException
  231. {
  232. Reader reader;
  233. if (articleId != null)
  234. {
  235. if (!NNTPReply.isPositiveCompletion(sendCommand(command, articleId)))
  236. return null;
  237. }
  238. else
  239. {
  240. if (!NNTPReply.isPositiveCompletion(sendCommand(command)))
  241. return null;
  242. }
  243. if (pointer != null)
  244. __parseArticlePointer(getReplyString(), pointer);
  245. reader = new DotTerminatedMessageReader(_reader_);
  246. return reader;
  247. }
  248. private Reader __retrieve(int command,
  249. int articleNumber, ArticlePointer pointer)
  250. throws IOException
  251. {
  252. Reader reader;
  253. if (!NNTPReply.isPositiveCompletion(sendCommand(command,
  254. Integer.toString(articleNumber))))
  255. return null;
  256. if (pointer != null)
  257. __parseArticlePointer(getReplyString(), pointer);
  258. reader = new DotTerminatedMessageReader(_reader_);
  259. return reader;
  260. }
  261. /***
  262. * Retrieves an article from the NNTP server. The article is referenced
  263. * by its unique article identifier (including the enclosing < and >).
  264. * The article number and identifier contained in the server reply
  265. * are returned through an ArticlePointer. The <code> articleId </code>
  266. * field of the ArticlePointer cannot always be trusted because some
  267. * NNTP servers do not correctly follow the RFC 977 reply format.
  268. * <p>
  269. * A DotTerminatedMessageReader is returned from which the article can
  270. * be read. If the article does not exist, null is returned.
  271. * <p>
  272. * You must not issue any commands to the NNTP server (i.e., call any
  273. * other methods) until you finish reading the message from the returned
  274. * Reader instance.
  275. * The NNTP protocol uses the same stream for issuing commands as it does
  276. * for returning results. Therefore the returned Reader actually reads
  277. * directly from the NNTP connection. After the end of message has been
  278. * reached, new commands can be executed and their replies read. If
  279. * you do not follow these requirements, your program will not work
  280. * properly.
  281. * <p>
  282. * @param articleId The unique article identifier of the article to
  283. * retrieve. If this parameter is null, the currently selected
  284. * article is retrieved.
  285. * @param pointer A parameter through which to return the article's
  286. * number and unique id. The articleId field cannot always be trusted
  287. * because of server deviations from RFC 977 reply formats. You may
  288. * set this parameter to null if you do not desire to retrieve the
  289. * returned article information.
  290. * @return A DotTerminatedMessageReader instance from which the article
  291. * be read. null if the article does not exist.
  292. * @exception NNTPConnectionClosedException
  293. * If the NNTP server prematurely closes the connection as a result
  294. * of the client being idle or some other reason causing the server
  295. * to send NNTP reply code 400. This exception may be caught either
  296. * as an IOException or independently as itself.
  297. * @exception IOException If an I/O error occurs while either sending a
  298. * command to the server or receiving a reply from the server.
  299. ***/
  300. public Reader retrieveArticle(String articleId, ArticlePointer pointer)
  301. throws IOException
  302. {
  303. return __retrieve(NNTPCommand.ARTICLE, articleId, pointer);
  304. }
  305. /*** Same as <code> retrieveArticle(articleId, null) </code> ***/
  306. public Reader retrieveArticle(String articleId) throws IOException
  307. {
  308. return retrieveArticle(articleId, null);
  309. }
  310. /*** Same as <code> retrieveArticle(null) </code> ***/
  311. public Reader retrieveArticle() throws IOException
  312. {
  313. return retrieveArticle(null);
  314. }
  315. /***
  316. * Retrieves an article from the currently selected newsgroup. The
  317. * article is referenced by its article number.
  318. * The article number and identifier contained in the server reply
  319. * are returned through an ArticlePointer. The <code> articleId </code>
  320. * field of the ArticlePointer cannot always be trusted because some
  321. * NNTP servers do not correctly follow the RFC 977 reply format.
  322. * <p>
  323. * A DotTerminatedMessageReader is returned from which the article can
  324. * be read. If the article does not exist, null is returned.
  325. * <p>
  326. * You must not issue any commands to the NNTP server (i.e., call any
  327. * other methods) until you finish reading the message from the returned
  328. * Reader instance.
  329. * The NNTP protocol uses the same stream for issuing commands as it does
  330. * for returning results. Therefore the returned Reader actually reads
  331. * directly from the NNTP connection. After the end of message has been
  332. * reached, new commands can be executed and their replies read. If
  333. * you do not follow these requirements, your program will not work
  334. * properly.
  335. * <p>
  336. * @param articleNumber The number of the the article to
  337. * retrieve.
  338. * @param pointer A parameter through which to return the article's
  339. * number and unique id. The articleId field cannot always be trusted
  340. * because of server deviations from RFC 977 reply formats. You may
  341. * set this parameter to null if you do not desire to retrieve the
  342. * returned article information.
  343. * @return A DotTerminatedMessageReader instance from which the article
  344. * be read. null if the article does not exist.
  345. * @exception NNTPConnectionClosedException
  346. * If the NNTP server prematurely closes the connection as a result
  347. * of the client being idle or some other reason causing the server
  348. * to send NNTP reply code 400. This exception may be caught either
  349. * as an IOException or independently as itself.
  350. * @exception IOException If an I/O error occurs while either sending a
  351. * command to the server or receiving a reply from the server.
  352. ***/
  353. public Reader retrieveArticle(int articleNumber, ArticlePointer pointer)
  354. throws IOException
  355. {
  356. return __retrieve(NNTPCommand.ARTICLE, articleNumber, pointer);
  357. }
  358. /*** Same as <code> retrieveArticle(articleNumber, null) </code> ***/
  359. public Reader retrieveArticle(int articleNumber) throws IOException
  360. {
  361. return retrieveArticle(articleNumber, null);
  362. }
  363. /***
  364. * Retrieves an article header from the NNTP server. The article is
  365. * referenced
  366. * by its unique article identifier (including the enclosing < and >).
  367. * The article number and identifier contained in the server reply
  368. * are returned through an ArticlePointer. The <code> articleId </code>
  369. * field of the ArticlePointer cannot always be trusted because some
  370. * NNTP servers do not correctly follow the RFC 977 reply format.
  371. * <p>
  372. * A DotTerminatedMessageReader is returned from which the article can
  373. * be read. If the article does not exist, null is returned.
  374. * <p>
  375. * You must not issue any commands to the NNTP server (i.e., call any
  376. * other methods) until you finish reading the message from the returned
  377. * Reader instance.
  378. * The NNTP protocol uses the same stream for issuing commands as it does
  379. * for returning results. Therefore the returned Reader actually reads
  380. * directly from the NNTP connection. After the end of message has been
  381. * reached, new commands can be executed and their replies read. If
  382. * you do not follow these requirements, your program will not work
  383. * properly.
  384. * <p>
  385. * @param articleId The unique article identifier of the article whose
  386. * header is being retrieved. If this parameter is null, the
  387. * header of the currently selected article is retrieved.
  388. * @param pointer A parameter through which to return the article's
  389. * number and unique id. The articleId field cannot always be trusted
  390. * because of server deviations from RFC 977 reply formats. You may
  391. * set this parameter to null if you do not desire to retrieve the
  392. * returned article information.
  393. * @return A DotTerminatedMessageReader instance from which the article
  394. * header can be read. null if the article does not exist.
  395. * @exception NNTPConnectionClosedException
  396. * If the NNTP server prematurely closes the connection as a result
  397. * of the client being idle or some other reason causing the server
  398. * to send NNTP reply code 400. This exception may be caught either
  399. * as an IOException or independently as itself.
  400. * @exception IOException If an I/O error occurs while either sending a
  401. * command to the server or receiving a reply from the server.
  402. ***/
  403. public Reader retrieveArticleHeader(String articleId, ArticlePointer pointer)
  404. throws IOException
  405. {
  406. return __retrieve(NNTPCommand.HEAD, articleId, pointer);
  407. }
  408. /*** Same as <code> retrieveArticleHeader(articleId, null) </code> ***/
  409. public Reader retrieveArticleHeader(String articleId) throws IOException
  410. {
  411. return retrieveArticleHeader(articleId, null);
  412. }
  413. /*** Same as <code> retrieveArticleHeader(null) </code> ***/
  414. public Reader retrieveArticleHeader() throws IOException
  415. {
  416. return retrieveArticleHeader(null);
  417. }
  418. /***
  419. * Retrieves an article header from the currently selected newsgroup. The
  420. * article is referenced by its article number.
  421. * The article number and identifier contained in the server reply
  422. * are returned through an ArticlePointer. The <code> articleId </code>
  423. * field of the ArticlePointer cannot always be trusted because some
  424. * NNTP servers do not correctly follow the RFC 977 reply format.
  425. * <p>
  426. * A DotTerminatedMessageReader is returned from which the article can
  427. * be read. If the article does not exist, null is returned.
  428. * <p>
  429. * You must not issue any commands to the NNTP server (i.e., call any
  430. * other methods) until you finish reading the message from the returned
  431. * Reader instance.
  432. * The NNTP protocol uses the same stream for issuing commands as it does
  433. * for returning results. Therefore the returned Reader actually reads
  434. * directly from the NNTP connection. After the end of message has been
  435. * reached, new commands can be executed and their replies read. If
  436. * you do not follow these requirements, your program will not work
  437. * properly.
  438. * <p>
  439. * @param articleNumber The number of the the article whose header is
  440. * being retrieved.
  441. * @param pointer A parameter through which to return the article's
  442. * number and unique id. The articleId field cannot always be trusted
  443. * because of server deviations from RFC 977 reply formats. You may
  444. * set this parameter to null if you do not desire to retrieve the
  445. * returned article information.
  446. * @return A DotTerminatedMessageReader instance from which the article
  447. * header can be read. null if the article does not exist.
  448. * @exception NNTPConnectionClosedException
  449. * If the NNTP server prematurely closes the connection as a result
  450. * of the client being idle or some other reason causing the server
  451. * to send NNTP reply code 400. This exception may be caught either
  452. * as an IOException or independently as itself.
  453. * @exception IOException If an I/O error occurs while either sending a
  454. * command to the server or receiving a reply from the server.
  455. ***/
  456. public Reader retrieveArticleHeader(int articleNumber,
  457. ArticlePointer pointer)
  458. throws IOException
  459. {
  460. return __retrieve(NNTPCommand.HEAD, articleNumber, pointer);
  461. }
  462. /*** Same as <code> retrieveArticleHeader(articleNumber, null) </code> ***/
  463. public Reader retrieveArticleHeader(int articleNumber) throws IOException
  464. {
  465. return retrieveArticleHeader(articleNumber, null);
  466. }
  467. /***
  468. * Retrieves an article body from the NNTP server. The article is
  469. * referenced
  470. * by its unique article identifier (including the enclosing < and >).
  471. * The article number and identifier contained in the server reply
  472. * are returned through an ArticlePointer. The <code> articleId </code>
  473. * field of the ArticlePointer cannot always be trusted because some
  474. * NNTP servers do not correctly follow the RFC 977 reply format.
  475. * <p>
  476. * A DotTerminatedMessageReader is returned from which the article can
  477. * be read. If the article does not exist, null is returned.
  478. * <p>
  479. * You must not issue any commands to the NNTP server (i.e., call any
  480. * other methods) until you finish reading the message from the returned
  481. * Reader instance.
  482. * The NNTP protocol uses the same stream for issuing commands as it does
  483. * for returning results. Therefore the returned Reader actually reads
  484. * directly from the NNTP connection. After the end of message has been
  485. * reached, new commands can be executed and their replies read. If
  486. * you do not follow these requirements, your program will not work
  487. * properly.
  488. * <p>
  489. * @param articleId The unique article identifier of the article whose
  490. * body is being retrieved. If this parameter is null, the
  491. * body of the currently selected article is retrieved.
  492. * @param pointer A parameter through which to return the article's
  493. * number and unique id. The articleId field cannot always be trusted
  494. * because of server deviations from RFC 977 reply formats. You may
  495. * set this parameter to null if you do not desire to retrieve the
  496. * returned article information.
  497. * @return A DotTerminatedMessageReader instance from which the article
  498. * body can be read. null if the article does not exist.
  499. * @exception NNTPConnectionClosedException
  500. * If the NNTP server prematurely closes the connection as a result
  501. * of the client being idle or some other reason causing the server
  502. * to send NNTP reply code 400. This exception may be caught either
  503. * as an IOException or independently as itself.
  504. * @exception IOException If an I/O error occurs while either sending a
  505. * command to the server or receiving a reply from the server.
  506. ***/
  507. public Reader retrieveArticleBody(String articleId, ArticlePointer pointer)
  508. throws IOException
  509. {
  510. return __retrieve(NNTPCommand.BODY, articleId, pointer);
  511. }
  512. /*** Same as <code> retrieveArticleBody(articleId, null) </code> ***/
  513. public Reader retrieveArticleBody(String articleId) throws IOException
  514. {
  515. return retrieveArticleBody(articleId, null);
  516. }
  517. /*** Same as <code> retrieveArticleBody(null) </code> ***/
  518. public Reader retrieveArticleBody() throws IOException
  519. {
  520. return retrieveArticleBody(null);
  521. }
  522. /***
  523. * Retrieves an article body from the currently selected newsgroup. The
  524. * article is referenced by its article number.
  525. * The article number and identifier contained in the server reply
  526. * are returned through an ArticlePointer. The <code> articleId </code>
  527. * field of the ArticlePointer cannot always be trusted because some
  528. * NNTP servers do not correctly follow the RFC 977 reply format.
  529. * <p>
  530. * A DotTerminatedMessageReader is returned from which the article can
  531. * be read. If the article does not exist, null is returned.
  532. * <p>
  533. * You must not issue any commands to the NNTP server (i.e., call any
  534. * other methods) until you finish reading the message from the returned
  535. * Reader instance.
  536. * The NNTP protocol uses the same stream for issuing commands as it does
  537. * for returning results. Therefore the returned Reader actually reads
  538. * directly from the NNTP connection. After the end of message has been
  539. * reached, new commands can be executed and their replies read. If
  540. * you do not follow these requirements, your program will not work
  541. * properly.
  542. * <p>
  543. * @param articleNumber The number of the the article whose body is
  544. * being retrieved.
  545. * @param pointer A parameter through which to return the article's
  546. * number and unique id. The articleId field cannot always be trusted
  547. * because of server deviations from RFC 977 reply formats. You may
  548. * set this parameter to null if you do not desire to retrieve the
  549. * returned article information.
  550. * @return A DotTerminatedMessageReader instance from which the article
  551. * body can be read. null if the article does not exist.
  552. * @exception NNTPConnectionClosedException
  553. * If the NNTP server prematurely closes the connection as a result
  554. * of the client being idle or some other reason causing the server
  555. * to send NNTP reply code 400. This exception may be caught either
  556. * as an IOException or independently as itself.
  557. * @exception IOException If an I/O error occurs while either sending a
  558. * command to the server or receiving a reply from the server.
  559. ***/
  560. public Reader retrieveArticleBody(int articleNumber,
  561. ArticlePointer pointer)
  562. throws IOException
  563. {
  564. return __retrieve(NNTPCommand.BODY, articleNumber, pointer);
  565. }
  566. /*** Same as <code> retrieveArticleBody(articleNumber, null) </code> ***/
  567. public Reader retrieveArticleBody(int articleNumber) throws IOException
  568. {
  569. return retrieveArticleBody(articleNumber, null);
  570. }
  571. /***
  572. * Select the specified newsgroup to be the target of for future article
  573. * retrieval and posting operations. Also return the newsgroup
  574. * information contained in the server reply through the info parameter.
  575. * <p>
  576. * @param newsgroup The newsgroup to select.
  577. * @param info A parameter through which the newsgroup information of
  578. * the selected newsgroup contained in the server reply is returned.
  579. * Set this to null if you do not desire this information.
  580. * @return True if the newsgroup exists and was selected, false otherwise.
  581. * @exception NNTPConnectionClosedException
  582. * If the NNTP server prematurely closes the connection as a result
  583. * of the client being idle or some other reason causing the server
  584. * to send NNTP reply code 400. This exception may be caught either
  585. * as an IOException or independently as itself.
  586. * @exception IOException If an I/O error occurs while either sending a
  587. * command to the server or receiving a reply from the server.
  588. ***/
  589. public boolean selectNewsgroup(String newsgroup, NewsgroupInfo info)
  590. throws IOException
  591. {
  592. if (!NNTPReply.isPositiveCompletion(group(newsgroup)))
  593. return false;
  594. if (info != null)
  595. __parseGroupReply(getReplyString(), info);
  596. return true;
  597. }
  598. /*** Same as <code> selectNewsgroup(newsgroup, null) </code> ***/
  599. public boolean selectNewsgroup(String newsgroup) throws IOException
  600. {
  601. return selectNewsgroup(newsgroup, null);
  602. }
  603. /***
  604. * List the command help from the server.
  605. * <p>
  606. * @return The sever help information.
  607. * @exception NNTPConnectionClosedException
  608. * If the NNTP server prematurely closes the connection as a result
  609. * of the client being idle or some other reason causing the server
  610. * to send NNTP reply code 400. This exception may be caught either
  611. * as an IOException or independently as itself.
  612. * @exception IOException If an I/O error occurs while either sending a
  613. * command to the server or receiving a reply from the server.
  614. ***/
  615. public String listHelp() throws IOException
  616. {
  617. StringWriter help;
  618. Reader reader;
  619. if (!NNTPReply.isInformational(help()))
  620. return null;
  621. help = new StringWriter();
  622. reader = new DotTerminatedMessageReader(_reader_);
  623. Util.copyReader(reader, help);
  624. reader.close();
  625. help.close();
  626. return help.toString();
  627. }
  628. /***
  629. * Select an article by its unique identifier (including enclosing
  630. * < and >) and return its article number and id through the
  631. * pointer parameter. This is achieved through the STAT command.
  632. * According to RFC 977, this will NOT set the current article pointer
  633. * on the server. To do that, you must reference the article by its
  634. * number.
  635. * <p>
  636. * @param articleId The unique article identifier of the article that
  637. * is being selectedd. If this parameter is null, the
  638. * body of the current article is selected
  639. * @param pointer A parameter through which to return the article's
  640. * number and unique id. The articleId field cannot always be trusted
  641. * because of server deviations from RFC 977 reply formats. You may
  642. * set this parameter to null if you do not desire to retrieve the
  643. * returned article information.
  644. * @return True if successful, false if not.
  645. * @exception NNTPConnectionClosedException
  646. * If the NNTP server prematurely closes the connection as a result
  647. * of the client being idle or some other reason causing the server
  648. * to send NNTP reply code 400. This exception may be caught either
  649. * as an IOException or independently as itself.
  650. * @exception IOException If an I/O error occurs while either sending a
  651. * command to the server or receiving a reply from the server.
  652. ***/
  653. public boolean selectArticle(String articleId, ArticlePointer pointer)
  654. throws IOException
  655. {
  656. if (articleId != null)
  657. {
  658. if (!NNTPReply.isPositiveCompletion(stat(articleId)))
  659. return false;
  660. }
  661. else
  662. {
  663. if (!NNTPReply.isPositiveCompletion(stat()))
  664. return false;
  665. }
  666. if (pointer != null)
  667. __parseArticlePointer(getReplyString(), pointer);
  668. return true;
  669. }
  670. /**** Same as <code> selectArticle(articleId, null) </code> ***/
  671. public boolean selectArticle(String articleId) throws IOException
  672. {
  673. return selectArticle(articleId, null);
  674. }
  675. /****
  676. * Same as <code> selectArticle(null, articleId) </code>. Useful
  677. * for retrieving the current article number.
  678. ***/
  679. public boolean selectArticle(ArticlePointer pointer) throws IOException
  680. {
  681. return selectArticle(null, pointer);
  682. }
  683. /***
  684. * Select an article in the currently selected newsgroup by its number.
  685. * and return its article number and id through the
  686. * pointer parameter. This is achieved through the STAT command.
  687. * According to RFC 977, this WILL set the current article pointer
  688. * on the server. Use this command to select an article before retrieving
  689. * it, or to obtain an article's unique identifier given its number.
  690. * <p>
  691. * @param articleNumber The number of the article to select from the
  692. * currently selected newsgroup.
  693. * @param pointer A parameter through which to return the article's
  694. * number and unique id. Although the articleId field cannot always
  695. * be trusted because of server deviations from RFC 977 reply formats,
  696. * we haven't found a server that misformats this information in response
  697. * to this particular command. You may set this parameter to null if
  698. * you do not desire to retrieve the returned article information.
  699. * @return True if successful, false if not.
  700. * @exception NNTPConnectionClosedException
  701. * If the NNTP server prematurely closes the connection as a result
  702. * of the client being idle or some other reason causing the server
  703. * to send NNTP reply code 400. This exception may be caught either
  704. * as an IOException or independently as itself.
  705. * @exception IOException If an I/O error occurs while either sending a
  706. * command to the server or receiving a reply from the server.
  707. ***/
  708. public boolean selectArticle(int articleNumber, ArticlePointer pointer)
  709. throws IOException
  710. {
  711. if (!NNTPReply.isPositiveCompletion(stat(articleNumber)))
  712. return false;
  713. if (pointer != null)
  714. __parseArticlePointer(getReplyString(), pointer);
  715. return true;
  716. }
  717. /*** Same as <code> selectArticle(articleNumber, null) </code> ***/
  718. public boolean selectArticle(int articleNumber) throws IOException
  719. {
  720. return selectArticle(articleNumber, null);
  721. }
  722. /***
  723. * Select the article preceeding the currently selected article in the
  724. * currently selected newsgroup and return its number and unique id
  725. * through the pointer parameter. Because of deviating server
  726. * implementations, the articleId information cannot be trusted. To
  727. * obtain the article identifier, issue a
  728. * <code> selectArticle(pointer.articleNumber, pointer) </code> immediately
  729. * afterward.
  730. * <p>
  731. * @param pointer A parameter through which to return the article's
  732. * number and unique id. The articleId field cannot always be trusted
  733. * because of server deviations from RFC 977 reply formats. You may
  734. * set this parameter to null if you do not desire to retrieve the
  735. * returned article information.
  736. * @return True if successful, false if not (e.g., there is no previous
  737. * article).
  738. * @exception NNTPConnectionClosedException
  739. * If the NNTP server prematurely closes the connection as a result
  740. * of the client being idle or some other reason causing the server
  741. * to send NNTP reply code 400. This exception may be caught either
  742. * as an IOException or independently as itself.
  743. * @exception IOException If an I/O error occurs while either sending a
  744. * command to the server or receiving a reply from the server.
  745. ***/
  746. public boolean selectPreviousArticle(ArticlePointer pointer)
  747. throws IOException
  748. {
  749. if (!NNTPReply.isPositiveCompletion(last()))
  750. return false;
  751. if (pointer != null)
  752. __parseArticlePointer(getReplyString(), pointer);
  753. return true;
  754. }
  755. /*** Same as <code> selectPreviousArticle(null) </code> ***/
  756. public boolean selectPreviousArticle() throws IOException
  757. {
  758. return selectPreviousArticle(null);
  759. }
  760. /***
  761. * Select the article following the currently selected article in the
  762. * currently selected newsgroup and return its number and unique id
  763. * through the pointer parameter. Because of deviating server
  764. * implementations, the articleId information cannot be trusted. To
  765. * obtain the article identifier, issue a
  766. * <code> selectArticle(pointer.articleNumber, pointer) </code> immediately
  767. * afterward.
  768. * <p>
  769. * @param pointer A parameter through which to return the article's
  770. * number and unique id. The articleId field cannot always be trusted
  771. * because of server deviations from RFC 977 reply formats. You may
  772. * set this parameter to null if you do not desire to retrieve the
  773. * returned article information.
  774. * @return True if successful, false if not (e.g., there is no following
  775. * article).
  776. * @exception NNTPConnectionClosedException
  777. * If the NNTP server prematurely closes the connection as a result
  778. * of the client being idle or some other reason causing the server
  779. * to send NNTP reply code 400. This exception may be caught either
  780. * as an IOException or independently as itself.
  781. * @exception IOException If an I/O error occurs while either sending a
  782. * command to the server or receiving a reply from the server.
  783. ***/
  784. public boolean selectNextArticle(ArticlePointer pointer) throws IOException
  785. {
  786. if (!NNTPReply.isPositiveCompletion(next()))
  787. return false;
  788. if (pointer != null)
  789. __parseArticlePointer(getReplyString(), pointer);
  790. return true;
  791. }
  792. /*** Same as <code> selectNextArticle(null) </code> ***/
  793. public boolean selectNextArticle() throws IOException
  794. {
  795. return selectNextArticle(null);
  796. }
  797. /***
  798. * List all newsgroups served by the NNTP server. If no newsgroups
  799. * are served, a zero length array will be returned. If the command
  800. * fails, null will be returned.
  801. * <p>
  802. * @return An array of NewsgroupInfo instances containing the information
  803. * for each newsgroup served by the NNTP server. If no newsgroups
  804. * are served, a zero length array will be returned. If the command
  805. * fails, null will be returned.
  806. * @exception NNTPConnectionClosedException
  807. * If the NNTP server prematurely closes the connection as a result
  808. * of the client being idle or some other reason causing the server
  809. * to send NNTP reply code 400. This exception may be caught either
  810. * as an IOException or independently as itself.
  811. * @exception IOException If an I/O error occurs while either sending a
  812. * command to the server or receiving a reply from the server.
  813. ***/
  814. public NewsgroupInfo[] listNewsgroups() throws IOException
  815. {
  816. if (!NNTPReply.isPositiveCompletion(list()))
  817. return null;
  818. return __readNewsgroupListing();
  819. }
  820. /**
  821. * An overloaded listNewsgroups() command that allows us to
  822. * specify with a pattern what groups we want to list. Wraps the
  823. * LIST ACTIVE command.
  824. * <p>
  825. * @param wildmat a pseudo-regex pattern (cf. RFC 2980)
  826. * @return An array of NewsgroupInfo instances containing the information
  827. * for each newsgroup served by the NNTP server corresponding to the
  828. * supplied pattern. If no such newsgroups are served, a zero length
  829. * array will be returned. If the command fails, null will be returned.
  830. * @throws IOException
  831. */
  832. public NewsgroupInfo[] listNewsgroups(String wildmat) throws IOException
  833. {
  834. if(!NNTPReply.isPositiveCompletion(listActive(wildmat)))
  835. return null;
  836. return __readNewsgroupListing();
  837. }
  838. /***
  839. * List all new newsgroups added to the NNTP server since a particular
  840. * date subject to the conditions of the specified query. If no new
  841. * newsgroups were added, a zero length array will be returned. If the
  842. * command fails, null will be returned.
  843. * <p>
  844. * @param query The query restricting how to search for new newsgroups.
  845. * @return An array of NewsgroupInfo instances containing the information
  846. * for each new newsgroup added to the NNTP server. If no newsgroups
  847. * were added, a zero length array will be returned. If the command
  848. * fails, null will be returned.
  849. * @exception NNTPConnectionClosedException
  850. * If the NNTP server prematurely closes the connection as a result
  851. * of the client being idle or some other reason causing the server
  852. * to send NNTP reply code 400. This exception may be caught either
  853. * as an IOException or independently as itself.
  854. * @exception IOException If an I/O error occurs while either sending a
  855. * command to the server or receiving a reply from the server.
  856. ***/
  857. public NewsgroupInfo[] listNewNewsgroups(NewGroupsOrNewsQuery query)
  858. throws IOException
  859. {
  860. if (!NNTPReply.isPositiveCompletion(newgroups(
  861. query.getDate(), query.getTime(),
  862. query.isGMT(), query.getDistributions())))
  863. return null;
  864. return __readNewsgroupListing();
  865. }
  866. /***
  867. * List all new articles added to the NNTP server since a particular
  868. * date subject to the conditions of the specified query. If no new
  869. * new news is found, a zero length array will be returned. If the
  870. * command fails, null will be returned. You must add at least one
  871. * newsgroup to the query, else the command will fail. Each String
  872. * in the returned array is a unique message identifier including the
  873. * enclosing < and >.
  874. * <p>
  875. * @param query The query restricting how to search for new news. You
  876. * must add at least one newsgroup to the query.
  877. * @return An array of String instances containing the unique message
  878. * identifiers for each new article added to the NNTP server. If no
  879. * new news is found, a zero length array will be returned. If the
  880. * command fails, null will be returned.
  881. * @exception NNTPConnectionClosedException
  882. * If the NNTP server prematurely closes the connection as a result
  883. * of the client being idle or some other reason causing the server
  884. * to send NNTP reply code 400. This exception may be caught either
  885. * as an IOException or independently as itself.
  886. * @exception IOException If an I/O error occurs while either sending a
  887. * command to the server or receiving a reply from the server.
  888. ***/
  889. public String[] listNewNews(NewGroupsOrNewsQuery query)
  890. throws IOException
  891. {
  892. int size;
  893. String line;
  894. Vector list;
  895. String[] result;
  896. BufferedReader reader;
  897. if (!NNTPReply.isPositiveCompletion(newnews(
  898. query.getNewsgroups(), query.getDate(), query.getTime(),
  899. query.isGMT(), query.getDistributions())))
  900. return null;
  901. list = new Vector();
  902. reader = new BufferedReader(new DotTerminatedMessageReader(_reader_));
  903. while ((line = reader.readLine()) != null)
  904. list.addElement(line);
  905. size = list.size();
  906. if (size < 1)
  907. return new String[0];
  908. result = new String[size];
  909. list.copyInto(result);
  910. return result;
  911. }
  912. /***
  913. * There are a few NNTPClient methods that do not complete the
  914. * entire sequence of NNTP commands to complete a transaction. These
  915. * commands require some action by the programmer after the reception
  916. * of a positive preliminary command. After the programmer's code
  917. * completes its actions, it must call this method to receive
  918. * the completion reply from the server and verify the success of the
  919. * entire transaction.
  920. * <p>
  921. * For example
  922. * <pre>
  923. * writer = client.postArticle();
  924. * if(writer == null) // failure
  925. * return false;
  926. * header = new SimpleNNTPHeader("foobar@foo.com", "Just testing");
  927. * header.addNewsgroup("alt.test");
  928. * writer.write(header.toString());
  929. * writer.write("This is just a test");
  930. * writer.close();
  931. * if(!client.completePendingCommand()) // failure
  932. * return false;
  933. * </pre>
  934. * <p>
  935. * @return True if successfully completed, false if not.
  936. * @exception NNTPConnectionClosedException
  937. * If the NNTP server prematurely closes the connection as a result
  938. * of the client being idle or some other reason causing the server
  939. * to send NNTP reply code 400. This exception may be caught either
  940. * as an IOException or independently as itself.
  941. * @exception IOException If an I/O error occurs while either sending a
  942. * command to the server or receiving a reply from the server.
  943. ***/
  944. public boolean completePendingCommand() throws IOException
  945. {
  946. return NNTPReply.isPositiveCompletion(getReply());
  947. }
  948. /***
  949. * Post an article to the NNTP server. This method returns a
  950. * DotTerminatedMessageWriter instance to which the article can be
  951. * written. Null is returned if the posting attempt fails. You
  952. * should check <a href="org.apache.commons.nntp.NNTP.html#isAllowedToPost">
  953. * isAllowedToPost() </a> before trying to post. However, a posting
  954. * attempt can fail due to malformed headers.
  955. * <p>
  956. * You must not issue any commands to the NNTP server (i.e., call any
  957. * (other methods) until you finish writing to the returned Writer
  958. * instance and close it. The NNTP protocol uses the same stream for
  959. * issuing commands as it does for returning results. Therefore the
  960. * returned Writer actually writes directly to the NNTP connection.
  961. * After you close the writer, you can execute new commands. If you
  962. * do not follow these requirements your program will not work properly.
  963. * <p>
  964. * Different NNTP servers will require different header formats, but
  965. * you can use the provided
  966. * <a href="org.apache.commons.net.nntp.SimpleNNTPHeader.html"> SimpleNNTPHeader </a>
  967. * class to construct the bare minimum acceptable header for most
  968. * news readers. To construct more complicated headers you should
  969. * refer to RFC 822. When the Java Mail API is finalized, you will be
  970. * able to use it to compose fully compliant Internet text messages.
  971. * The DotTerminatedMessageWriter takes care of doubling line-leading
  972. * dots and ending the message with a single dot upon closing, so all
  973. * you have to worry about is writing the header and the message.
  974. * <p>
  975. * Upon closing the returned Writer, you need to call
  976. * <a href="#completePendingCommand"> completePendingCommand() </a>
  977. * to finalize the posting and verify its success or failure from
  978. * the server reply.
  979. * <p>
  980. * @return A DotTerminatedMessageWriter to which the article (including
  981. * header) can be written. Returns null if the command fails.
  982. * @exception IOException If an I/O error occurs while either sending a
  983. * command to the server or receiving a reply from the server.
  984. ***/
  985. public Writer postArticle() throws IOException
  986. {
  987. if (!NNTPReply.isPositiveIntermediate(post()))
  988. return null;
  989. return new DotTerminatedMessageWriter(_writer_);
  990. }
  991. public Writer forwardArticle(String articleId) throws IOException
  992. {
  993. if (!NNTPReply.isPositiveIntermediate(ihave(articleId)))
  994. return null;
  995. return new DotTerminatedMessageWriter(_writer_);
  996. }
  997. /***
  998. * Logs out of the news server gracefully by sending the QUIT command.
  999. * However, you must still disconnect from the server before you can open
  1000. * a new connection.
  1001. * <p>
  1002. * @return True if successfully completed, false if not.
  1003. * @exception IOException If an I/O error occurs while either sending a
  1004. * command to the server or receiving a reply from the server.
  1005. ***/
  1006. public boolean logout() throws IOException
  1007. {
  1008. return NNTPReply.isPositiveCompletion(quit());
  1009. }
  1010. /**
  1011. * Log into a news server by sending the AUTHINFO USER/AUTHINFO
  1012. * PASS command sequence. This is usually sent in response to a
  1013. * 480 reply code from the NNTP server.
  1014. * <p>
  1015. * @param username a valid username
  1016. * @param password the corresponding password
  1017. * @return True for successful login, false for a failure
  1018. * @throws IOException
  1019. */
  1020. public boolean authenticate(String username, String password)
  1021. throws IOException
  1022. {
  1023. int replyCode = authinfoUser(username);
  1024. if (replyCode == NNTPReply.MORE_AUTH_INFO_REQUIRED)
  1025. {
  1026. replyCode = authinfoPass(password);
  1027. if (replyCode == NNTPReply.AUTHENTICATION_ACCEPTED)
  1028. {
  1029. _isAllowedToPost = true;
  1030. return true;
  1031. }
  1032. }
  1033. return false;
  1034. }
  1035. /***
  1036. * Private implementation of XOVER functionality.
  1037. *
  1038. * See <a href="org.apache.commons.nntp.NNTP.html#xover">
  1039. * for legal agument formats. Alternatively, read RFC 2980 :-)
  1040. * <p>
  1041. * @param articleRange
  1042. * @return Returns a DotTerminatedMessageReader if successful, null
  1043. * otherwise
  1044. * @exception IOException
  1045. */
  1046. private Reader __retrieveArticleInfo(String articleRange)
  1047. throws IOException
  1048. {
  1049. if (!NNTPReply.isPositiveCompletion(xover(articleRange)))
  1050. return null;
  1051. return new DotTerminatedMessageReader(_reader_);
  1052. }
  1053. /**
  1054. * Return article headers for a specified post.
  1055. * <p>
  1056. * @param articleNumber the article to retrieve headers for
  1057. * @return a DotTerminatedReader if successful, null otherwise
  1058. * @throws IOException
  1059. */
  1060. public Reader retrieveArticleInfo(int articleNumber) throws IOException
  1061. {
  1062. return __retrieveArticleInfo(Integer.toString(articleNumber));
  1063. }
  1064. /**
  1065. * Return article headers for all articles between lowArticleNumber
  1066. * and highArticleNumber, inclusively.
  1067. * <p>
  1068. * @param lowArticleNumber
  1069. * @param highArticleNumber
  1070. * @return a DotTerminatedReader if successful, null otherwise
  1071. * @throws IOException
  1072. */
  1073. public Reader retrieveArticleInfo(int lowArticleNumber,
  1074. int highArticleNumber)
  1075. throws IOException
  1076. {
  1077. return
  1078. __retrieveArticleInfo(new String(lowArticleNumber + "-" +
  1079. highArticleNumber));
  1080. }
  1081. /***
  1082. * Private implementation of XHDR functionality.
  1083. *
  1084. * See <a href="org.apache.commons.nntp.NNTP.html#xhdr">
  1085. * for legal agument formats. Alternatively, read RFC 1036.
  1086. * <p>
  1087. * @param header
  1088. * @param articleRange
  1089. * @return Returns a DotTerminatedMessageReader if successful, null
  1090. * otherwise
  1091. * @exception IOException
  1092. */
  1093. private Reader __retrieveHeader(String header, String articleRange)
  1094. throws IOException
  1095. {
  1096. if (!NNTPReply.isPositiveCompletion(xhdr(header, articleRange)))
  1097. return null;
  1098. return new DotTerminatedMessageReader(_reader_);
  1099. }
  1100. /**
  1101. * Return an article header for a specified post.
  1102. * <p>
  1103. * @param header the header to retrieve
  1104. * @param articleNumber the article to retrieve the header for
  1105. * @return a DotTerminatedReader if successful, null otherwise
  1106. * @throws IOException
  1107. */
  1108. public Reader retrieveHeader(String header, int articleNumber)
  1109. throws IOException
  1110. {
  1111. return __retrieveHeader(header, Integer.toString(articleNumber));
  1112. }
  1113. /**
  1114. * Return an article header for all articles between lowArticleNumber
  1115. * and highArticleNumber, inclusively.
  1116. * <p>
  1117. * @param header
  1118. * @param lowArticleNumber
  1119. * @param highArticleNumber
  1120. * @return a DotTerminatedReader if successful, null otherwise
  1121. * @throws IOException
  1122. */
  1123. public Reader retrieveHeader(String header, int lowArticleNumber,
  1124. int highArticleNumber)
  1125. throws IOException
  1126. {
  1127. return
  1128. __retrieveHeader(header,
  1129. new String(lowArticleNumber + "-" +
  1130. highArticleNumber));
  1131. }
  1132. }
  1133. /* Emacs configuration
  1134. * Local variables: **
  1135. * mode: java **
  1136. * c-basic-offset: 4 **
  1137. * indent-tabs-mode: nil **
  1138. * End: **
  1139. */