📄 nntpconnection.java
字号:
/* * NNTPConnection.java * Copyright (C) 2002 The Free Software Foundation * * This file is part of GNU inetlib, a library. * * GNU inetlib is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * GNU inetlib is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Linking this library statically or dynamically with other modules is * making a combined work based on this library. Thus, the terms and * conditions of the GNU General Public License cover the whole * combination. * * As a special exception, the copyright holders of this library give you * permission to link this library with independent modules to produce an * executable, regardless of the license terms of these independent * modules, and to copy and distribute the resulting executable under * terms of your choice, provided that you also meet, for each linked * independent module, the terms and conditions of the license of that * module. An independent module is a module which is not derived from * or based on this library. If you modify this library, you may extend * this exception to your version of the library, but you are not * obliged to do so. If you do not wish to do so, delete this * exception statement from your version. */package gnu.inet.nntp;import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.InputStream;import java.io.IOException;import java.io.OutputStream;import java.lang.reflect.Constructor;import java.net.InetSocketAddress;import java.net.ProtocolException;import java.net.Socket;import java.text.DateFormat;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Calendar;import java.util.Date;import java.util.GregorianCalendar;import java.util.HashMap;import java.util.TimeZone;import java.util.logging.Level;import java.util.logging.Logger;import javax.security.auth.callback.CallbackHandler;import javax.security.sasl.Sasl;import javax.security.sasl.SaslClient;import javax.security.sasl.SaslException;import gnu.inet.util.CRLFInputStream;import gnu.inet.util.CRLFOutputStream;import gnu.inet.util.LineInputStream;import gnu.inet.util.SaslCallbackHandler;import gnu.inet.util.SaslInputStream;import gnu.inet.util.SaslOutputStream;import gnu.inet.util.TraceLevel;/** * An NNTP client. * This object is used to establish and manage a connection to an NNTP * server. * * @author <a hef='mailto:dog@gnu.org'>Chris Burdess</a> */public class NNTPConnection implements NNTPConstants{ /** * The logger used for NNTP protocol traces. */ public static final Logger logger = Logger.getLogger("gnu.inet.nntp"); /** * The network trace level. */ public static final Level NNTP_TRACE = new TraceLevel("nntp"); /** * The default NNTP port. */ public static final int DEFAULT_PORT = 119; /** * The hostname of the host we are connected to. */ protected String hostname; /** * The port on the host we are connected to. */ protected int port; /** * The socket used for network communication. */ protected Socket socket; /** * The socket input stream. */ protected LineInputStream in; /** * The socket output stream. */ protected CRLFOutputStream out; /** * Whether the host permits posting of articles. */ protected boolean canPost; /** * The greeting issued by the host when we connected. */ protected String welcome; /** * Pending data, if any. */ protected PendingData pendingData; private static final String DOT = "."; private static final String US_ASCII = "US-ASCII"; /** * Creates a new connection object. * @param hostname the hostname or IP address of the news server */ public NNTPConnection(String hostname) throws IOException { this(hostname, DEFAULT_PORT, 0, 0); } /** * Creates a new connection object. * @param hostname the hostname or IP address of the news server * @param port the port to connect to */ public NNTPConnection(String hostname, int port) throws IOException { this(hostname, port, 0, 0); } /** * Creates a new connection object. * @param hostname the hostname or IP address of the news server * @param port the port to connect to * @param connectionTimeout the socket connection timeout * @param timeout the read timeout on the socket */ public NNTPConnection(String hostname, int port, int connectionTimeout, int timeout) throws IOException { if (port < 0) { port = DEFAULT_PORT; } this.hostname = hostname; this.port = port; // Set up the socket and streams socket = new Socket(); InetSocketAddress address = new InetSocketAddress(hostname, port); if (connectionTimeout > 0) { socket.connect(address, connectionTimeout); } else { socket.connect(address); } if (timeout > 0) { socket.setSoTimeout(timeout); } InputStream in = socket.getInputStream(); in = new CRLFInputStream(in); this.in = new LineInputStream(in); OutputStream out = socket.getOutputStream(); out = new BufferedOutputStream(out); this.out = new CRLFOutputStream(out); // Read the welcome message(RFC977:2.4.3) StatusResponse response = parseResponse(read()); switch (response.status) { case POSTING_ALLOWED: canPost = true; case NO_POSTING_ALLOWED: welcome = response.getMessage(); break; default: throw new NNTPException(response); } } /** * Return the welcome message sent by the server in reply to the initial * connection. * This message sometimes contains disclaimers or help information that * may be relevant to the user. */ public String getWelcome() { return welcome; } /* * Returns an NNTP-format date string. * This is only required when clients use the NEWGROUPS or NEWNEWS * methods, therefore rarely: we don't cache any of the variables here. */ String formatDate(Date date) { DateFormat df = new SimpleDateFormat("yyMMdd HHmmss 'GMT'"); Calendar cal = new GregorianCalendar(); TimeZone gmt = TimeZone.getTimeZone("GMT"); cal.setTimeZone(gmt); df.setCalendar(cal); cal.setTime(date); return df.format(date); } /* * Parse the specfied NNTP date text. */ Date parseDate(String text) throws ParseException { DateFormat df = new SimpleDateFormat("yyMMdd HHmmss 'GMT'"); return df.parse(text); } // RFC977:3.1 The ARTICLE, BODY, HEAD, and STAT commands /** * Send an article retrieval request to the server. * @param articleNumber the article number of the article to retrieve * @return an article response consisting of the article number and * message-id, and an iterator over the lines of the article header and * body, separated by an empty line */ public ArticleResponse article(int articleNumber) throws IOException { return articleImpl(ARTICLE, Integer.toString(articleNumber)); } /** * Send an article retrieval request to the server. * @param messageId the message-id of the article to retrieve * @return an article response consisting of the article number and * message-id, and an iterator over the lines of the article header and * body, separated by an empty line */ public ArticleResponse article(String messageId) throws IOException { return articleImpl(ARTICLE, messageId); } /** * Send an article head retrieval request to the server. * @param articleNumber the article number of the article to head * @return an article response consisting of the article number and * message-id, and an iterator over the lines of the article header */ public ArticleResponse head(int articleNumber) throws IOException { return articleImpl(HEAD, Integer.toString(articleNumber)); } /** * Send an article head retrieval request to the server. * @param messageId the message-id of the article to head * @return an article response consisting of the article number and * message-id, and an iterator over the lines of the article header */ public ArticleResponse head(String messageId) throws IOException { return articleImpl(HEAD, messageId); } /** * Send an article body retrieval request to the server. * @param articleNumber the article number of the article to body * @return an article response consisting of the article number and * message-id, and an iterator over the lines of the article body */ public ArticleResponse body(int articleNumber) throws IOException { return articleImpl(BODY, Integer.toString(articleNumber)); } /** * Send an article body retrieval request to the server. * @param messageId the message-id of the article to body * @return an article response consisting of the article number and * message-id, and an iterator over the lines of the article body */ public ArticleResponse body(String messageId) throws IOException { return articleImpl(BODY, messageId); } /** * Send an article status request to the server. * @param articleNumber the article number of the article to stat * @return an article response consisting of the article number and * message-id */ public ArticleResponse stat(int articleNumber) throws IOException { return articleImpl(STAT, Integer.toString(articleNumber)); } /** * Send an article status request to the server. * @param messageId the message-id of the article to stat * @return an article response consisting of the article number and * message-id */ public ArticleResponse stat(String messageId) throws IOException { return articleImpl(STAT, messageId); } /** * Performs an ARTICLE, BODY, HEAD, or STAT command. * @param command one of the above commands * @param messageId the article-number or message-id in string form */ protected ArticleResponse articleImpl(String command, String messageId) throws IOException { if (messageId != null) { StringBuffer line = new StringBuffer(command); line.append(' '); line.append(messageId); send(line.toString()); } else { send(command); } StatusResponse response = parseResponse(read()); switch (response.status) { case ARTICLE_FOLLOWS: case HEAD_FOLLOWS: case BODY_FOLLOWS: ArticleResponse aresponse = (ArticleResponse) response; ArticleStream astream = new ArticleStream(in); pendingData = astream; aresponse.in = astream; return aresponse; case ARTICLE_RETRIEVED: return (ArticleResponse) response; default: // NO_GROUP_SELECTED // NO_ARTICLE_SELECTED // NO_SUCH_ARTICLE_NUMBER // NO_SUCH_ARTICLE // NO_PREVIOUS_ARTICLE // NO_NEXT_ARTICLE throw new NNTPException(response); } } // RFC977:3.2 The GROUP command /** * Send a group selection command to the server. * Returns a group status response. * @param name the name of the group to select */ public GroupResponse group(String name) throws IOException { send(GROUP + ' ' + name); StatusResponse response = parseResponse(read()); switch (response.status) { case GROUP_SELECTED: return (GroupResponse) response; default: // NO_SUCH_GROUP throw new NNTPException(response); } } // RFC977:3.3 The HELP command /** * Requests a help listing. * @return an iterator over a collection of help lines. */ public LineIterator help() throws IOException { send(HELP); StatusResponse response = parseResponse(read()); switch (response.status) { case HELP_TEXT: LineIterator li = new LineIterator(this); pendingData = li; return li; default: throw new NNTPException(response); } } // RFC977:3.4 The IHAVE command /** * Sends an ihave command indicating that the client has an article with * the specified message-id. * @param messageId the article message-id * @return a PostStream if the server wants the specified article, null * otherwise */ public PostStream ihave(String messageId) throws IOException { send(IHAVE + ' ' + messageId); StatusResponse response = parseResponse(read()); switch (response.status) { case SEND_TRANSFER_ARTICLE: return new PostStream(this, false); case ARTICLE_NOT_WANTED: return null; default: throw new NNTPException(response); } } // RFC(77:3.5 The LAST command /** * Sends a previous article positioning command to the server.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -