0001: /*
0002: * Copyright 2001-2005 The Apache Software Foundation
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016: package org.apache.commons.net.nntp;
0017:
0018: import java.io.BufferedReader;
0019: import java.io.IOException;
0020: import java.io.Reader;
0021: import java.io.StringWriter;
0022: import java.io.Writer;
0023: import java.util.StringTokenizer;
0024: import java.util.Vector;
0025: import org.apache.commons.net.io.DotTerminatedMessageReader;
0026: import org.apache.commons.net.io.DotTerminatedMessageWriter;
0027: import org.apache.commons.net.io.Util;
0028: import org.apache.commons.net.MalformedServerReplyException;
0029:
0030: /***
0031: * NNTPClient encapsulates all the functionality necessary to post and
0032: * retrieve articles from an NNTP server. As with all classes derived
0033: * from {@link org.apache.commons.net.SocketClient},
0034: * you must first connect to the server with
0035: * {@link org.apache.commons.net.SocketClient#connect connect }
0036: * before doing anything, and finally
0037: * {@link org.apache.commons.net.nntp.NNTP#disconnect disconnect() }
0038: * after you're completely finished interacting with the server.
0039: * Remember that the
0040: * {@link org.apache.commons.net.nntp.NNTP#isAllowedToPost isAllowedToPost()}
0041: * method is defined in
0042: * {@link org.apache.commons.net.nntp.NNTP}.
0043: * <p>
0044: * You should keep in mind that the NNTP server may choose to prematurely
0045: * close a connection if the client has been idle for longer than a
0046: * given time period or if the server is being shutdown by the operator or
0047: * some other reason. The NNTP class will detect a
0048: * premature NNTP server connection closing when it receives a
0049: * {@link org.apache.commons.net.nntp.NNTPReply#SERVICE_DISCONTINUED NNTPReply.SERVICE_DISCONTINUED }
0050: * response to a command.
0051: * When that occurs, the NNTP class method encountering that reply will throw
0052: * an {@link org.apache.commons.net.nntp.NNTPConnectionClosedException}
0053: * .
0054: * <code>NNTPConectionClosedException</code>
0055: * is a subclass of <code> IOException </code> and therefore need not be
0056: * caught separately, but if you are going to catch it separately, its
0057: * catch block must appear before the more general <code> IOException </code>
0058: * catch block. When you encounter an
0059: * {@link org.apache.commons.net.nntp.NNTPConnectionClosedException}
0060: * , you must disconnect the connection with
0061: * {@link org.apache.commons.net.nntp.NNTP#disconnect disconnect() }
0062: * to properly clean up the
0063: * system resources used by NNTP. Before disconnecting, you may check the
0064: * last reply code and text with
0065: * {@link org.apache.commons.net.nntp.NNTP#getReplyCode getReplyCode } and
0066: * {@link org.apache.commons.net.nntp.NNTP#getReplyString getReplyString }.
0067: * <p>
0068: * Rather than list it separately for each method, we mention here that
0069: * every method communicating with the server and throwing an IOException
0070: * can also throw a
0071: * {@link org.apache.commons.net.MalformedServerReplyException}
0072: * , which is a subclass
0073: * of IOException. A MalformedServerReplyException will be thrown when
0074: * the reply received from the server deviates enough from the protocol
0075: * specification that it cannot be interpreted in a useful manner despite
0076: * attempts to be as lenient as possible.
0077: * <p>
0078: * <p>
0079: * @author Daniel F. Savarese
0080: * @author Rory Winston
0081: * @author Ted Wise
0082: * @see NNTP
0083: * @see NNTPConnectionClosedException
0084: * @see org.apache.commons.net.MalformedServerReplyException
0085: ***/
0086:
0087: public class NNTPClient extends NNTP {
0088:
0089: private void __parseArticlePointer(String reply,
0090: ArticlePointer pointer)
0091: throws MalformedServerReplyException {
0092: StringTokenizer tokenizer;
0093:
0094: // Do loop is a kluge to simulate goto
0095: do {
0096: tokenizer = new StringTokenizer(reply);
0097:
0098: if (tokenizer.countTokens() < 3)
0099: break;
0100:
0101: // Skip numeric response value
0102: tokenizer.nextToken();
0103: // Get article number
0104: try {
0105: pointer.articleNumber = Integer.parseInt(tokenizer
0106: .nextToken());
0107: } catch (NumberFormatException e) {
0108: break;
0109: }
0110:
0111: // Get article id
0112: pointer.articleId = tokenizer.nextToken();
0113: return;
0114: } while (false);
0115:
0116: throw new MalformedServerReplyException(
0117: "Could not parse article pointer.\nServer reply: "
0118: + reply);
0119: }
0120:
0121: private void __parseGroupReply(String reply, NewsgroupInfo info)
0122: throws MalformedServerReplyException {
0123: String count, first, last;
0124: StringTokenizer tokenizer;
0125:
0126: // Do loop is a kluge to simulate goto
0127: do {
0128: tokenizer = new StringTokenizer(reply);
0129:
0130: if (tokenizer.countTokens() < 5)
0131: break;
0132:
0133: // Skip numeric response value
0134: tokenizer.nextToken();
0135: // Get estimated article count
0136: count = tokenizer.nextToken();
0137: // Get first article number
0138: first = tokenizer.nextToken();
0139: // Get last article number
0140: last = tokenizer.nextToken();
0141: // Get newsgroup name
0142: info._setNewsgroup(tokenizer.nextToken());
0143:
0144: try {
0145: info._setArticleCount(Integer.parseInt(count));
0146: info._setFirstArticle(Integer.parseInt(first));
0147: info._setLastArticle(Integer.parseInt(last));
0148: } catch (NumberFormatException e) {
0149: break;
0150: }
0151:
0152: info
0153: ._setPostingPermission(NewsgroupInfo.UNKNOWN_POSTING_PERMISSION);
0154: return;
0155: } while (false);
0156:
0157: throw new MalformedServerReplyException(
0158: "Could not parse newsgroup info.\nServer reply: "
0159: + reply);
0160: }
0161:
0162: private NewsgroupInfo __parseNewsgroupListEntry(String entry) {
0163: NewsgroupInfo result;
0164: StringTokenizer tokenizer;
0165: int lastNum, firstNum;
0166: String last, first, permission;
0167:
0168: result = new NewsgroupInfo();
0169: tokenizer = new StringTokenizer(entry);
0170:
0171: if (tokenizer.countTokens() < 4)
0172: return null;
0173:
0174: result._setNewsgroup(tokenizer.nextToken());
0175: last = tokenizer.nextToken();
0176: first = tokenizer.nextToken();
0177: permission = tokenizer.nextToken();
0178:
0179: try {
0180: lastNum = Integer.parseInt(last);
0181: firstNum = Integer.parseInt(first);
0182: result._setFirstArticle(firstNum);
0183: result._setLastArticle(lastNum);
0184:
0185: if ((firstNum == 0) && (lastNum == 0))
0186: result._setArticleCount(0);
0187: else
0188: result._setArticleCount(lastNum - firstNum + 1);
0189: } catch (NumberFormatException e) {
0190: return null;
0191: }
0192:
0193: switch (permission.charAt(0)) {
0194: case 'y':
0195: case 'Y':
0196: result
0197: ._setPostingPermission(NewsgroupInfo.PERMITTED_POSTING_PERMISSION);
0198: break;
0199: case 'n':
0200: case 'N':
0201: result
0202: ._setPostingPermission(NewsgroupInfo.PROHIBITED_POSTING_PERMISSION);
0203: break;
0204: case 'm':
0205: case 'M':
0206: result
0207: ._setPostingPermission(NewsgroupInfo.MODERATED_POSTING_PERMISSION);
0208: break;
0209: default:
0210: result
0211: ._setPostingPermission(NewsgroupInfo.UNKNOWN_POSTING_PERMISSION);
0212: break;
0213: }
0214:
0215: return result;
0216: }
0217:
0218: private NewsgroupInfo[] __readNewsgroupListing() throws IOException {
0219: int size;
0220: String line;
0221: Vector list;
0222: BufferedReader reader;
0223: NewsgroupInfo tmp, info[];
0224:
0225: reader = new BufferedReader(new DotTerminatedMessageReader(
0226: _reader_));
0227: // Start of with a big vector because we may be reading a very large
0228: // amount of groups.
0229: list = new Vector(2048);
0230:
0231: while ((line = reader.readLine()) != null) {
0232: tmp = __parseNewsgroupListEntry(line);
0233: if (tmp != null)
0234: list.addElement(tmp);
0235: else
0236: throw new MalformedServerReplyException(line);
0237: }
0238:
0239: if ((size = list.size()) < 1)
0240: return new NewsgroupInfo[0];
0241:
0242: info = new NewsgroupInfo[size];
0243: list.copyInto(info);
0244:
0245: return info;
0246: }
0247:
0248: private Reader __retrieve(int command, String articleId,
0249: ArticlePointer pointer) throws IOException {
0250: Reader reader;
0251:
0252: if (articleId != null) {
0253: if (!NNTPReply.isPositiveCompletion(sendCommand(command,
0254: articleId)))
0255: return null;
0256: } else {
0257: if (!NNTPReply.isPositiveCompletion(sendCommand(command)))
0258: return null;
0259: }
0260:
0261: if (pointer != null)
0262: __parseArticlePointer(getReplyString(), pointer);
0263:
0264: reader = new DotTerminatedMessageReader(_reader_);
0265: return reader;
0266: }
0267:
0268: private Reader __retrieve(int command, int articleNumber,
0269: ArticlePointer pointer) throws IOException {
0270: Reader reader;
0271:
0272: if (!NNTPReply.isPositiveCompletion(sendCommand(command,
0273: Integer.toString(articleNumber))))
0274: return null;
0275:
0276: if (pointer != null)
0277: __parseArticlePointer(getReplyString(), pointer);
0278:
0279: reader = new DotTerminatedMessageReader(_reader_);
0280: return reader;
0281: }
0282:
0283: /***
0284: * Retrieves an article from the NNTP server. The article is referenced
0285: * by its unique article identifier (including the enclosing < and >).
0286: * The article number and identifier contained in the server reply
0287: * are returned through an ArticlePointer. The <code> articleId </code>
0288: * field of the ArticlePointer cannot always be trusted because some
0289: * NNTP servers do not correctly follow the RFC 977 reply format.
0290: * <p>
0291: * A DotTerminatedMessageReader is returned from which the article can
0292: * be read. If the article does not exist, null is returned.
0293: * <p>
0294: * You must not issue any commands to the NNTP server (i.e., call any
0295: * other methods) until you finish reading the message from the returned
0296: * Reader instance.
0297: * The NNTP protocol uses the same stream for issuing commands as it does
0298: * for returning results. Therefore the returned Reader actually reads
0299: * directly from the NNTP connection. After the end of message has been
0300: * reached, new commands can be executed and their replies read. If
0301: * you do not follow these requirements, your program will not work
0302: * properly.
0303: * <p>
0304: * @param articleId The unique article identifier of the article to
0305: * retrieve. If this parameter is null, the currently selected
0306: * article is retrieved.
0307: * @param pointer A parameter through which to return the article's
0308: * number and unique id. The articleId field cannot always be trusted
0309: * because of server deviations from RFC 977 reply formats. You may
0310: * set this parameter to null if you do not desire to retrieve the
0311: * returned article information.
0312: * @return A DotTerminatedMessageReader instance from which the article
0313: * be read. null if the article does not exist.
0314: * @exception NNTPConnectionClosedException
0315: * If the NNTP server prematurely closes the connection as a result
0316: * of the client being idle or some other reason causing the server
0317: * to send NNTP reply code 400. This exception may be caught either
0318: * as an IOException or independently as itself.
0319: * @exception IOException If an I/O error occurs while either sending a
0320: * command to the server or receiving a reply from the server.
0321: ***/
0322: public Reader retrieveArticle(String articleId,
0323: ArticlePointer pointer) throws IOException {
0324: return __retrieve(NNTPCommand.ARTICLE, articleId, pointer);
0325:
0326: }
0327:
0328: /*** Same as <code> retrieveArticle(articleId, null) </code> ***/
0329: public Reader retrieveArticle(String articleId) throws IOException {
0330: return retrieveArticle(articleId, null);
0331: }
0332:
0333: /*** Same as <code> retrieveArticle(null) </code> ***/
0334: public Reader retrieveArticle() throws IOException {
0335: return retrieveArticle(null);
0336: }
0337:
0338: /***
0339: * Retrieves an article from the currently selected newsgroup. The
0340: * article is referenced by its article number.
0341: * The article number and identifier contained in the server reply
0342: * are returned through an ArticlePointer. The <code> articleId </code>
0343: * field of the ArticlePointer cannot always be trusted because some
0344: * NNTP servers do not correctly follow the RFC 977 reply format.
0345: * <p>
0346: * A DotTerminatedMessageReader is returned from which the article can
0347: * be read. If the article does not exist, null is returned.
0348: * <p>
0349: * You must not issue any commands to the NNTP server (i.e., call any
0350: * other methods) until you finish reading the message from the returned
0351: * Reader instance.
0352: * The NNTP protocol uses the same stream for issuing commands as it does
0353: * for returning results. Therefore the returned Reader actually reads
0354: * directly from the NNTP connection. After the end of message has been
0355: * reached, new commands can be executed and their replies read. If
0356: * you do not follow these requirements, your program will not work
0357: * properly.
0358: * <p>
0359: * @param articleNumber The number of the the article to
0360: * retrieve.
0361: * @param pointer A parameter through which to return the article's
0362: * number and unique id. The articleId field cannot always be trusted
0363: * because of server deviations from RFC 977 reply formats. You may
0364: * set this parameter to null if you do not desire to retrieve the
0365: * returned article information.
0366: * @return A DotTerminatedMessageReader instance from which the article
0367: * be read. null if the article does not exist.
0368: * @exception NNTPConnectionClosedException
0369: * If the NNTP server prematurely closes the connection as a result
0370: * of the client being idle or some other reason causing the server
0371: * to send NNTP reply code 400. This exception may be caught either
0372: * as an IOException or independently as itself.
0373: * @exception IOException If an I/O error occurs while either sending a
0374: * command to the server or receiving a reply from the server.
0375: ***/
0376: public Reader retrieveArticle(int articleNumber,
0377: ArticlePointer pointer) throws IOException {
0378: return __retrieve(NNTPCommand.ARTICLE, articleNumber, pointer);
0379: }
0380:
0381: /*** Same as <code> retrieveArticle(articleNumber, null) </code> ***/
0382: public Reader retrieveArticle(int articleNumber) throws IOException {
0383: return retrieveArticle(articleNumber, null);
0384: }
0385:
0386: /***
0387: * Retrieves an article header from the NNTP server. The article is
0388: * referenced
0389: * by its unique article identifier (including the enclosing < and >).
0390: * The article number and identifier contained in the server reply
0391: * are returned through an ArticlePointer. The <code> articleId </code>
0392: * field of the ArticlePointer cannot always be trusted because some
0393: * NNTP servers do not correctly follow the RFC 977 reply format.
0394: * <p>
0395: * A DotTerminatedMessageReader is returned from which the article can
0396: * be read. If the article does not exist, null is returned.
0397: * <p>
0398: * You must not issue any commands to the NNTP server (i.e., call any
0399: * other methods) until you finish reading the message from the returned
0400: * Reader instance.
0401: * The NNTP protocol uses the same stream for issuing commands as it does
0402: * for returning results. Therefore the returned Reader actually reads
0403: * directly from the NNTP connection. After the end of message has been
0404: * reached, new commands can be executed and their replies read. If
0405: * you do not follow these requirements, your program will not work
0406: * properly.
0407: * <p>
0408: * @param articleId The unique article identifier of the article whose
0409: * header is being retrieved. If this parameter is null, the
0410: * header of the currently selected article is retrieved.
0411: * @param pointer A parameter through which to return the article's
0412: * number and unique id. The articleId field cannot always be trusted
0413: * because of server deviations from RFC 977 reply formats. You may
0414: * set this parameter to null if you do not desire to retrieve the
0415: * returned article information.
0416: * @return A DotTerminatedMessageReader instance from which the article
0417: * header can be read. null if the article does not exist.
0418: * @exception NNTPConnectionClosedException
0419: * If the NNTP server prematurely closes the connection as a result
0420: * of the client being idle or some other reason causing the server
0421: * to send NNTP reply code 400. This exception may be caught either
0422: * as an IOException or independently as itself.
0423: * @exception IOException If an I/O error occurs while either sending a
0424: * command to the server or receiving a reply from the server.
0425: ***/
0426: public Reader retrieveArticleHeader(String articleId,
0427: ArticlePointer pointer) throws IOException {
0428: return __retrieve(NNTPCommand.HEAD, articleId, pointer);
0429:
0430: }
0431:
0432: /*** Same as <code> retrieveArticleHeader(articleId, null) </code> ***/
0433: public Reader retrieveArticleHeader(String articleId)
0434: throws IOException {
0435: return retrieveArticleHeader(articleId, null);
0436: }
0437:
0438: /*** Same as <code> retrieveArticleHeader(null) </code> ***/
0439: public Reader retrieveArticleHeader() throws IOException {
0440: return retrieveArticleHeader(null);
0441: }
0442:
0443: /***
0444: * Retrieves an article header from the currently selected newsgroup. The
0445: * article is referenced by its article number.
0446: * The article number and identifier contained in the server reply
0447: * are returned through an ArticlePointer. The <code> articleId </code>
0448: * field of the ArticlePointer cannot always be trusted because some
0449: * NNTP servers do not correctly follow the RFC 977 reply format.
0450: * <p>
0451: * A DotTerminatedMessageReader is returned from which the article can
0452: * be read. If the article does not exist, null is returned.
0453: * <p>
0454: * You must not issue any commands to the NNTP server (i.e., call any
0455: * other methods) until you finish reading the message from the returned
0456: * Reader instance.
0457: * The NNTP protocol uses the same stream for issuing commands as it does
0458: * for returning results. Therefore the returned Reader actually reads
0459: * directly from the NNTP connection. After the end of message has been
0460: * reached, new commands can be executed and their replies read. If
0461: * you do not follow these requirements, your program will not work
0462: * properly.
0463: * <p>
0464: * @param articleNumber The number of the the article whose header is
0465: * being retrieved.
0466: * @param pointer A parameter through which to return the article's
0467: * number and unique id. The articleId field cannot always be trusted
0468: * because of server deviations from RFC 977 reply formats. You may
0469: * set this parameter to null if you do not desire to retrieve the
0470: * returned article information.
0471: * @return A DotTerminatedMessageReader instance from which the article
0472: * header can be read. null if the article does not exist.
0473: * @exception NNTPConnectionClosedException
0474: * If the NNTP server prematurely closes the connection as a result
0475: * of the client being idle or some other reason causing the server
0476: * to send NNTP reply code 400. This exception may be caught either
0477: * as an IOException or independently as itself.
0478: * @exception IOException If an I/O error occurs while either sending a
0479: * command to the server or receiving a reply from the server.
0480: ***/
0481: public Reader retrieveArticleHeader(int articleNumber,
0482: ArticlePointer pointer) throws IOException {
0483: return __retrieve(NNTPCommand.HEAD, articleNumber, pointer);
0484: }
0485:
0486: /*** Same as <code> retrieveArticleHeader(articleNumber, null) </code> ***/
0487: public Reader retrieveArticleHeader(int articleNumber)
0488: throws IOException {
0489: return retrieveArticleHeader(articleNumber, null);
0490: }
0491:
0492: /***
0493: * Retrieves an article body from the NNTP server. The article is
0494: * referenced
0495: * by its unique article identifier (including the enclosing < and >).
0496: * The article number and identifier contained in the server reply
0497: * are returned through an ArticlePointer. The <code> articleId </code>
0498: * field of the ArticlePointer cannot always be trusted because some
0499: * NNTP servers do not correctly follow the RFC 977 reply format.
0500: * <p>
0501: * A DotTerminatedMessageReader is returned from which the article can
0502: * be read. If the article does not exist, null is returned.
0503: * <p>
0504: * You must not issue any commands to the NNTP server (i.e., call any
0505: * other methods) until you finish reading the message from the returned
0506: * Reader instance.
0507: * The NNTP protocol uses the same stream for issuing commands as it does
0508: * for returning results. Therefore the returned Reader actually reads
0509: * directly from the NNTP connection. After the end of message has been
0510: * reached, new commands can be executed and their replies read. If
0511: * you do not follow these requirements, your program will not work
0512: * properly.
0513: * <p>
0514: * @param articleId The unique article identifier of the article whose
0515: * body is being retrieved. If this parameter is null, the
0516: * body of the currently selected article is retrieved.
0517: * @param pointer A parameter through which to return the article's
0518: * number and unique id. The articleId field cannot always be trusted
0519: * because of server deviations from RFC 977 reply formats. You may
0520: * set this parameter to null if you do not desire to retrieve the
0521: * returned article information.
0522: * @return A DotTerminatedMessageReader instance from which the article
0523: * body can be read. null if the article does not exist.
0524: * @exception NNTPConnectionClosedException
0525: * If the NNTP server prematurely closes the connection as a result
0526: * of the client being idle or some other reason causing the server
0527: * to send NNTP reply code 400. This exception may be caught either
0528: * as an IOException or independently as itself.
0529: * @exception IOException If an I/O error occurs while either sending a
0530: * command to the server or receiving a reply from the server.
0531: ***/
0532: public Reader retrieveArticleBody(String articleId,
0533: ArticlePointer pointer) throws IOException {
0534: return __retrieve(NNTPCommand.BODY, articleId, pointer);
0535:
0536: }
0537:
0538: /*** Same as <code> retrieveArticleBody(articleId, null) </code> ***/
0539: public Reader retrieveArticleBody(String articleId)
0540: throws IOException {
0541: return retrieveArticleBody(articleId, null);
0542: }
0543:
0544: /*** Same as <code> retrieveArticleBody(null) </code> ***/
0545: public Reader retrieveArticleBody() throws IOException {
0546: return retrieveArticleBody(null);
0547: }
0548:
0549: /***
0550: * Retrieves an article body from the currently selected newsgroup. The
0551: * article is referenced by its article number.
0552: * The article number and identifier contained in the server reply
0553: * are returned through an ArticlePointer. The <code> articleId </code>
0554: * field of the ArticlePointer cannot always be trusted because some
0555: * NNTP servers do not correctly follow the RFC 977 reply format.
0556: * <p>
0557: * A DotTerminatedMessageReader is returned from which the article can
0558: * be read. If the article does not exist, null is returned.
0559: * <p>
0560: * You must not issue any commands to the NNTP server (i.e., call any
0561: * other methods) until you finish reading the message from the returned
0562: * Reader instance.
0563: * The NNTP protocol uses the same stream for issuing commands as it does
0564: * for returning results. Therefore the returned Reader actually reads
0565: * directly from the NNTP connection. After the end of message has been
0566: * reached, new commands can be executed and their replies read. If
0567: * you do not follow these requirements, your program will not work
0568: * properly.
0569: * <p>
0570: * @param articleNumber The number of the the article whose body is
0571: * being retrieved.
0572: * @param pointer A parameter through which to return the article's
0573: * number and unique id. The articleId field cannot always be trusted
0574: * because of server deviations from RFC 977 reply formats. You may
0575: * set this parameter to null if you do not desire to retrieve the
0576: * returned article information.
0577: * @return A DotTerminatedMessageReader instance from which the article
0578: * body can be read. null if the article does not exist.
0579: * @exception NNTPConnectionClosedException
0580: * If the NNTP server prematurely closes the connection as a result
0581: * of the client being idle or some other reason causing the server
0582: * to send NNTP reply code 400. This exception may be caught either
0583: * as an IOException or independently as itself.
0584: * @exception IOException If an I/O error occurs while either sending a
0585: * command to the server or receiving a reply from the server.
0586: ***/
0587: public Reader retrieveArticleBody(int articleNumber,
0588: ArticlePointer pointer) throws IOException {
0589: return __retrieve(NNTPCommand.BODY, articleNumber, pointer);
0590: }
0591:
0592: /*** Same as <code> retrieveArticleBody(articleNumber, null) </code> ***/
0593: public Reader retrieveArticleBody(int articleNumber)
0594: throws IOException {
0595: return retrieveArticleBody(articleNumber, null);
0596: }
0597:
0598: /***
0599: * Select the specified newsgroup to be the target of for future article
0600: * retrieval and posting operations. Also return the newsgroup
0601: * information contained in the server reply through the info parameter.
0602: * <p>
0603: * @param newsgroup The newsgroup to select.
0604: * @param info A parameter through which the newsgroup information of
0605: * the selected newsgroup contained in the server reply is returned.
0606: * Set this to null if you do not desire this information.
0607: * @return True if the newsgroup exists and was selected, false otherwise.
0608: * @exception NNTPConnectionClosedException
0609: * If the NNTP server prematurely closes the connection as a result
0610: * of the client being idle or some other reason causing the server
0611: * to send NNTP reply code 400. This exception may be caught either
0612: * as an IOException or independently as itself.
0613: * @exception IOException If an I/O error occurs while either sending a
0614: * command to the server or receiving a reply from the server.
0615: ***/
0616: public boolean selectNewsgroup(String newsgroup, NewsgroupInfo info)
0617: throws IOException {
0618: if (!NNTPReply.isPositiveCompletion(group(newsgroup)))
0619: return false;
0620:
0621: if (info != null)
0622: __parseGroupReply(getReplyString(), info);
0623:
0624: return true;
0625: }
0626:
0627: /*** Same as <code> selectNewsgroup(newsgroup, null) </code> ***/
0628: public boolean selectNewsgroup(String newsgroup) throws IOException {
0629: return selectNewsgroup(newsgroup, null);
0630: }
0631:
0632: /***
0633: * List the command help from the server.
0634: * <p>
0635: * @return The sever help information.
0636: * @exception NNTPConnectionClosedException
0637: * If the NNTP server prematurely closes the connection as a result
0638: * of the client being idle or some other reason causing the server
0639: * to send NNTP reply code 400. This exception may be caught either
0640: * as an IOException or independently as itself.
0641: * @exception IOException If an I/O error occurs while either sending a
0642: * command to the server or receiving a reply from the server.
0643: ***/
0644: public String listHelp() throws IOException {
0645: StringWriter help;
0646: Reader reader;
0647:
0648: if (!NNTPReply.isInformational(help()))
0649: return null;
0650:
0651: help = new StringWriter();
0652: reader = new DotTerminatedMessageReader(_reader_);
0653: Util.copyReader(reader, help);
0654: reader.close();
0655: help.close();
0656: return help.toString();
0657: }
0658:
0659: /***
0660: * Select an article by its unique identifier (including enclosing
0661: * < and >) and return its article number and id through the
0662: * pointer parameter. This is achieved through the STAT command.
0663: * According to RFC 977, this will NOT set the current article pointer
0664: * on the server. To do that, you must reference the article by its
0665: * number.
0666: * <p>
0667: * @param articleId The unique article identifier of the article that
0668: * is being selectedd. If this parameter is null, the
0669: * body of the current article is selected
0670: * @param pointer A parameter through which to return the article's
0671: * number and unique id. The articleId field cannot always be trusted
0672: * because of server deviations from RFC 977 reply formats. You may
0673: * set this parameter to null if you do not desire to retrieve the
0674: * returned article information.
0675: * @return True if successful, false if not.
0676: * @exception NNTPConnectionClosedException
0677: * If the NNTP server prematurely closes the connection as a result
0678: * of the client being idle or some other reason causing the server
0679: * to send NNTP reply code 400. This exception may be caught either
0680: * as an IOException or independently as itself.
0681: * @exception IOException If an I/O error occurs while either sending a
0682: * command to the server or receiving a reply from the server.
0683: ***/
0684: public boolean selectArticle(String articleId,
0685: ArticlePointer pointer) throws IOException {
0686: if (articleId != null) {
0687: if (!NNTPReply.isPositiveCompletion(stat(articleId)))
0688: return false;
0689: } else {
0690: if (!NNTPReply.isPositiveCompletion(stat()))
0691: return false;
0692: }
0693:
0694: if (pointer != null)
0695: __parseArticlePointer(getReplyString(), pointer);
0696:
0697: return true;
0698: }
0699:
0700: /**** Same as <code> selectArticle(articleId, null) </code> ***/
0701: public boolean selectArticle(String articleId) throws IOException {
0702: return selectArticle(articleId, null);
0703: }
0704:
0705: /****
0706: * Same as <code> selectArticle(null, articleId) </code>. Useful
0707: * for retrieving the current article number.
0708: ***/
0709: public boolean selectArticle(ArticlePointer pointer)
0710: throws IOException {
0711: return selectArticle(null, pointer);
0712: }
0713:
0714: /***
0715: * Select an article in the currently selected newsgroup by its number.
0716: * and return its article number and id through the
0717: * pointer parameter. This is achieved through the STAT command.
0718: * According to RFC 977, this WILL set the current article pointer
0719: * on the server. Use this command to select an article before retrieving
0720: * it, or to obtain an article's unique identifier given its number.
0721: * <p>
0722: * @param articleNumber The number of the article to select from the
0723: * currently selected newsgroup.
0724: * @param pointer A parameter through which to return the article's
0725: * number and unique id. Although the articleId field cannot always
0726: * be trusted because of server deviations from RFC 977 reply formats,
0727: * we haven't found a server that misformats this information in response
0728: * to this particular command. You may set this parameter to null if
0729: * you do not desire to retrieve the returned article information.
0730: * @return True if successful, false if not.
0731: * @exception NNTPConnectionClosedException
0732: * If the NNTP server prematurely closes the connection as a result
0733: * of the client being idle or some other reason causing the server
0734: * to send NNTP reply code 400. This exception may be caught either
0735: * as an IOException or independently as itself.
0736: * @exception IOException If an I/O error occurs while either sending a
0737: * command to the server or receiving a reply from the server.
0738: ***/
0739: public boolean selectArticle(int articleNumber,
0740: ArticlePointer pointer) throws IOException {
0741: if (!NNTPReply.isPositiveCompletion(stat(articleNumber)))
0742: return false;
0743:
0744: if (pointer != null)
0745: __parseArticlePointer(getReplyString(), pointer);
0746:
0747: return true;
0748: }
0749:
0750: /*** Same as <code> selectArticle(articleNumber, null) </code> ***/
0751: public boolean selectArticle(int articleNumber) throws IOException {
0752: return selectArticle(articleNumber, null);
0753: }
0754:
0755: /***
0756: * Select the article preceeding the currently selected article in the
0757: * currently selected newsgroup and return its number and unique id
0758: * through the pointer parameter. Because of deviating server
0759: * implementations, the articleId information cannot be trusted. To
0760: * obtain the article identifier, issue a
0761: * <code> selectArticle(pointer.articleNumber, pointer) </code> immediately
0762: * afterward.
0763: * <p>
0764: * @param pointer A parameter through which to return the article's
0765: * number and unique id. The articleId field cannot always be trusted
0766: * because of server deviations from RFC 977 reply formats. You may
0767: * set this parameter to null if you do not desire to retrieve the
0768: * returned article information.
0769: * @return True if successful, false if not (e.g., there is no previous
0770: * article).
0771: * @exception NNTPConnectionClosedException
0772: * If the NNTP server prematurely closes the connection as a result
0773: * of the client being idle or some other reason causing the server
0774: * to send NNTP reply code 400. This exception may be caught either
0775: * as an IOException or independently as itself.
0776: * @exception IOException If an I/O error occurs while either sending a
0777: * command to the server or receiving a reply from the server.
0778: ***/
0779: public boolean selectPreviousArticle(ArticlePointer pointer)
0780: throws IOException {
0781: if (!NNTPReply.isPositiveCompletion(last()))
0782: return false;
0783:
0784: if (pointer != null)
0785: __parseArticlePointer(getReplyString(), pointer);
0786:
0787: return true;
0788: }
0789:
0790: /*** Same as <code> selectPreviousArticle(null) </code> ***/
0791: public boolean selectPreviousArticle() throws IOException {
0792: return selectPreviousArticle(null);
0793: }
0794:
0795: /***
0796: * Select the article following the currently selected article in the
0797: * currently selected newsgroup and return its number and unique id
0798: * through the pointer parameter. Because of deviating server
0799: * implementations, the articleId information cannot be trusted. To
0800: * obtain the article identifier, issue a
0801: * <code> selectArticle(pointer.articleNumber, pointer) </code> immediately
0802: * afterward.
0803: * <p>
0804: * @param pointer A parameter through which to return the article's
0805: * number and unique id. The articleId field cannot always be trusted
0806: * because of server deviations from RFC 977 reply formats. You may
0807: * set this parameter to null if you do not desire to retrieve the
0808: * returned article information.
0809: * @return True if successful, false if not (e.g., there is no following
0810: * article).
0811: * @exception NNTPConnectionClosedException
0812: * If the NNTP server prematurely closes the connection as a result
0813: * of the client being idle or some other reason causing the server
0814: * to send NNTP reply code 400. This exception may be caught either
0815: * as an IOException or independently as itself.
0816: * @exception IOException If an I/O error occurs while either sending a
0817: * command to the server or receiving a reply from the server.
0818: ***/
0819: public boolean selectNextArticle(ArticlePointer pointer)
0820: throws IOException {
0821: if (!NNTPReply.isPositiveCompletion(next()))
0822: return false;
0823:
0824: if (pointer != null)
0825: __parseArticlePointer(getReplyString(), pointer);
0826:
0827: return true;
0828: }
0829:
0830: /*** Same as <code> selectNextArticle(null) </code> ***/
0831: public boolean selectNextArticle() throws IOException {
0832: return selectNextArticle(null);
0833: }
0834:
0835: /***
0836: * List all newsgroups served by the NNTP server. If no newsgroups
0837: * are served, a zero length array will be returned. If the command
0838: * fails, null will be returned.
0839: * <p>
0840: * @return An array of NewsgroupInfo instances containing the information
0841: * for each newsgroup served by the NNTP server. If no newsgroups
0842: * are served, a zero length array will be returned. If the command
0843: * fails, null will be returned.
0844: * @exception NNTPConnectionClosedException
0845: * If the NNTP server prematurely closes the connection as a result
0846: * of the client being idle or some other reason causing the server
0847: * to send NNTP reply code 400. This exception may be caught either
0848: * as an IOException or independently as itself.
0849: * @exception IOException If an I/O error occurs while either sending a
0850: * command to the server or receiving a reply from the server.
0851: ***/
0852: public NewsgroupInfo[] listNewsgroups() throws IOException {
0853: if (!NNTPReply.isPositiveCompletion(list()))
0854: return null;
0855:
0856: return __readNewsgroupListing();
0857: }
0858:
0859: /**
0860: * An overloaded listNewsgroups() command that allows us to
0861: * specify with a pattern what groups we want to list. Wraps the
0862: * LIST ACTIVE command.
0863: * <p>
0864: * @param wildmat a pseudo-regex pattern (cf. RFC 2980)
0865: * @return An array of NewsgroupInfo instances containing the information
0866: * for each newsgroup served by the NNTP server corresponding to the
0867: * supplied pattern. If no such newsgroups are served, a zero length
0868: * array will be returned. If the command fails, null will be returned.
0869: * @throws IOException
0870: */
0871: public NewsgroupInfo[] listNewsgroups(String wildmat)
0872: throws IOException {
0873: if (!NNTPReply.isPositiveCompletion(listActive(wildmat)))
0874: return null;
0875: return __readNewsgroupListing();
0876: }
0877:
0878: /***
0879: * List all new newsgroups added to the NNTP server since a particular
0880: * date subject to the conditions of the specified query. If no new
0881: * newsgroups were added, a zero length array will be returned. If the
0882: * command fails, null will be returned.
0883: * <p>
0884: * @param query The query restricting how to search for new newsgroups.
0885: * @return An array of NewsgroupInfo instances containing the information
0886: * for each new newsgroup added to the NNTP server. If no newsgroups
0887: * were added, a zero length array will be returned. If the command
0888: * fails, null will be returned.
0889: * @exception NNTPConnectionClosedException
0890: * If the NNTP server prematurely closes the connection as a result
0891: * of the client being idle or some other reason causing the server
0892: * to send NNTP reply code 400. This exception may be caught either
0893: * as an IOException or independently as itself.
0894: * @exception IOException If an I/O error occurs while either sending a
0895: * command to the server or receiving a reply from the server.
0896: ***/
0897: public NewsgroupInfo[] listNewNewsgroups(NewGroupsOrNewsQuery query)
0898: throws IOException {
0899: if (!NNTPReply.isPositiveCompletion(newgroups(query.getDate(),
0900: query.getTime(), query.isGMT(), query
0901: .getDistributions())))
0902: return null;
0903:
0904: return __readNewsgroupListing();
0905: }
0906:
0907: /***
0908: * List all new articles added to the NNTP server since a particular
0909: * date subject to the conditions of the specified query. If no new
0910: * new news is found, a zero length array will be returned. If the
0911: * command fails, null will be returned. You must add at least one
0912: * newsgroup to the query, else the command will fail. Each String
0913: * in the returned array is a unique message identifier including the
0914: * enclosing < and >.
0915: * <p>
0916: * @param query The query restricting how to search for new news. You
0917: * must add at least one newsgroup to the query.
0918: * @return An array of String instances containing the unique message
0919: * identifiers for each new article added to the NNTP server. If no
0920: * new news is found, a zero length array will be returned. If the
0921: * command fails, null will be returned.
0922: * @exception NNTPConnectionClosedException
0923: * If the NNTP server prematurely closes the connection as a result
0924: * of the client being idle or some other reason causing the server
0925: * to send NNTP reply code 400. This exception may be caught either
0926: * as an IOException or independently as itself.
0927: * @exception IOException If an I/O error occurs while either sending a
0928: * command to the server or receiving a reply from the server.
0929: ***/
0930: public String[] listNewNews(NewGroupsOrNewsQuery query)
0931: throws IOException {
0932: int size;
0933: String line;
0934: Vector list;
0935: String[] result;
0936: BufferedReader reader;
0937:
0938: if (!NNTPReply.isPositiveCompletion(newnews(query
0939: .getNewsgroups(), query.getDate(), query.getTime(),
0940: query.isGMT(), query.getDistributions())))
0941: return null;
0942:
0943: list = new Vector();
0944: reader = new BufferedReader(new DotTerminatedMessageReader(
0945: _reader_));
0946:
0947: while ((line = reader.readLine()) != null)
0948: list.addElement(line);
0949:
0950: size = list.size();
0951:
0952: if (size < 1)
0953: return new String[0];
0954:
0955: result = new String[size];
0956: list.copyInto(result);
0957:
0958: return result;
0959: }
0960:
0961: /***
0962: * There are a few NNTPClient methods that do not complete the
0963: * entire sequence of NNTP commands to complete a transaction. These
0964: * commands require some action by the programmer after the reception
0965: * of a positive preliminary command. After the programmer's code
0966: * completes its actions, it must call this method to receive
0967: * the completion reply from the server and verify the success of the
0968: * entire transaction.
0969: * <p>
0970: * For example
0971: * <pre>
0972: * writer = client.postArticle();
0973: * if(writer == null) // failure
0974: * return false;
0975: * header = new SimpleNNTPHeader("foobar@foo.com", "Just testing");
0976: * header.addNewsgroup("alt.test");
0977: * writer.write(header.toString());
0978: * writer.write("This is just a test");
0979: * writer.close();
0980: * if(!client.completePendingCommand()) // failure
0981: * return false;
0982: * </pre>
0983: * <p>
0984: * @return True if successfully completed, false if not.
0985: * @exception NNTPConnectionClosedException
0986: * If the NNTP server prematurely closes the connection as a result
0987: * of the client being idle or some other reason causing the server
0988: * to send NNTP reply code 400. This exception may be caught either
0989: * as an IOException or independently as itself.
0990: * @exception IOException If an I/O error occurs while either sending a
0991: * command to the server or receiving a reply from the server.
0992: ***/
0993: public boolean completePendingCommand() throws IOException {
0994: return NNTPReply.isPositiveCompletion(getReply());
0995: }
0996:
0997: /***
0998: * Post an article to the NNTP server. This method returns a
0999: * DotTerminatedMessageWriter instance to which the article can be
1000: * written. Null is returned if the posting attempt fails. You
1001: * should check {@link NNTP#isAllowedToPost isAllowedToPost() }
1002: * before trying to post. However, a posting
1003: * attempt can fail due to malformed headers.
1004: * <p>
1005: * You must not issue any commands to the NNTP server (i.e., call any
1006: * (other methods) until you finish writing to the returned Writer
1007: * instance and close it. The NNTP protocol uses the same stream for
1008: * issuing commands as it does for returning results. Therefore the
1009: * returned Writer actually writes directly to the NNTP connection.
1010: * After you close the writer, you can execute new commands. If you
1011: * do not follow these requirements your program will not work properly.
1012: * <p>
1013: * Different NNTP servers will require different header formats, but
1014: * you can use the provided
1015: * {@link org.apache.commons.net.nntp.SimpleNNTPHeader}
1016: * class to construct the bare minimum acceptable header for most
1017: * news readers. To construct more complicated headers you should
1018: * refer to RFC 822. When the Java Mail API is finalized, you will be
1019: * able to use it to compose fully compliant Internet text messages.
1020: * The DotTerminatedMessageWriter takes care of doubling line-leading
1021: * dots and ending the message with a single dot upon closing, so all
1022: * you have to worry about is writing the header and the message.
1023: * <p>
1024: * Upon closing the returned Writer, you need to call
1025: * {@link #completePendingCommand completePendingCommand() }
1026: * to finalize the posting and verify its success or failure from
1027: * the server reply.
1028: * <p>
1029: * @return A DotTerminatedMessageWriter to which the article (including
1030: * header) can be written. Returns null if the command fails.
1031: * @exception IOException If an I/O error occurs while either sending a
1032: * command to the server or receiving a reply from the server.
1033: ***/
1034:
1035: public Writer postArticle() throws IOException {
1036: if (!NNTPReply.isPositiveIntermediate(post()))
1037: return null;
1038:
1039: return new DotTerminatedMessageWriter(_writer_);
1040: }
1041:
1042: public Writer forwardArticle(String articleId) throws IOException {
1043: if (!NNTPReply.isPositiveIntermediate(ihave(articleId)))
1044: return null;
1045:
1046: return new DotTerminatedMessageWriter(_writer_);
1047: }
1048:
1049: /***
1050: * Logs out of the news server gracefully by sending the QUIT command.
1051: * However, you must still disconnect from the server before you can open
1052: * a new connection.
1053: * <p>
1054: * @return True if successfully completed, false if not.
1055: * @exception IOException If an I/O error occurs while either sending a
1056: * command to the server or receiving a reply from the server.
1057: ***/
1058: public boolean logout() throws IOException {
1059: return NNTPReply.isPositiveCompletion(quit());
1060: }
1061:
1062: /**
1063: * Log into a news server by sending the AUTHINFO USER/AUTHINFO
1064: * PASS command sequence. This is usually sent in response to a
1065: * 480 reply code from the NNTP server.
1066: * <p>
1067: * @param username a valid username
1068: * @param password the corresponding password
1069: * @return True for successful login, false for a failure
1070: * @throws IOException
1071: */
1072: public boolean authenticate(String username, String password)
1073: throws IOException {
1074: int replyCode = authinfoUser(username);
1075:
1076: if (replyCode == NNTPReply.MORE_AUTH_INFO_REQUIRED) {
1077: replyCode = authinfoPass(password);
1078:
1079: if (replyCode == NNTPReply.AUTHENTICATION_ACCEPTED) {
1080: _isAllowedToPost = true;
1081: return true;
1082: }
1083: }
1084: return false;
1085: }
1086:
1087: /***
1088: * Private implementation of XOVER functionality.
1089: *
1090: * See {@link NNTP#xover}
1091: * for legal agument formats. Alternatively, read RFC 2980 :-)
1092: * <p>
1093: * @param articleRange
1094: * @return Returns a DotTerminatedMessageReader if successful, null
1095: * otherwise
1096: * @exception IOException
1097: */
1098: private Reader __retrieveArticleInfo(String articleRange)
1099: throws IOException {
1100: if (!NNTPReply.isPositiveCompletion(xover(articleRange)))
1101: return null;
1102:
1103: return new DotTerminatedMessageReader(_reader_);
1104: }
1105:
1106: /**
1107: * Return article headers for a specified post.
1108: * <p>
1109: * @param articleNumber the article to retrieve headers for
1110: * @return a DotTerminatedReader if successful, null otherwise
1111: * @throws IOException
1112: */
1113: public Reader retrieveArticleInfo(int articleNumber)
1114: throws IOException {
1115: return __retrieveArticleInfo(Integer.toString(articleNumber));
1116: }
1117:
1118: /**
1119: * Return article headers for all articles between lowArticleNumber
1120: * and highArticleNumber, inclusively.
1121: * <p>
1122: * @param lowArticleNumber
1123: * @param highArticleNumber
1124: * @return a DotTerminatedReader if successful, null otherwise
1125: * @throws IOException
1126: */
1127: public Reader retrieveArticleInfo(int lowArticleNumber,
1128: int highArticleNumber) throws IOException {
1129: return __retrieveArticleInfo(new String(lowArticleNumber + "-"
1130: + highArticleNumber));
1131: }
1132:
1133: /***
1134: * Private implementation of XHDR functionality.
1135: *
1136: * See {@link NNTP#xhdr}
1137: * for legal agument formats. Alternatively, read RFC 1036.
1138: * <p>
1139: * @param header
1140: * @param articleRange
1141: * @return Returns a DotTerminatedMessageReader if successful, null
1142: * otherwise
1143: * @exception IOException
1144: */
1145: private Reader __retrieveHeader(String header, String articleRange)
1146: throws IOException {
1147: if (!NNTPReply.isPositiveCompletion(xhdr(header, articleRange)))
1148: return null;
1149:
1150: return new DotTerminatedMessageReader(_reader_);
1151: }
1152:
1153: /**
1154: * Return an article header for a specified post.
1155: * <p>
1156: * @param header the header to retrieve
1157: * @param articleNumber the article to retrieve the header for
1158: * @return a DotTerminatedReader if successful, null otherwise
1159: * @throws IOException
1160: */
1161: public Reader retrieveHeader(String header, int articleNumber)
1162: throws IOException {
1163: return __retrieveHeader(header, Integer.toString(articleNumber));
1164: }
1165:
1166: /**
1167: * Return an article header for all articles between lowArticleNumber
1168: * and highArticleNumber, inclusively.
1169: * <p>
1170: * @param header
1171: * @param lowArticleNumber
1172: * @param highArticleNumber
1173: * @return a DotTerminatedReader if successful, null otherwise
1174: * @throws IOException
1175: */
1176: public Reader retrieveHeader(String header, int lowArticleNumber,
1177: int highArticleNumber) throws IOException {
1178: return __retrieveHeader(header, new String(lowArticleNumber
1179: + "-" + highArticleNumber));
1180: }
1181: }
1182:
1183: /* Emacs configuration
1184: * Local variables: **
1185: * mode: java **
1186: * c-basic-offset: 4 **
1187: * indent-tabs-mode: nil **
1188: * End: **
1189: */
|