📄 nntpstore.java
字号:
/*
* NNTPStore.java
* Copyright (C) 1999 dog <dog@dog.net.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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
*
* You may retrieve the latest version of this library from
* http://www.dog.net.uk/knife/
*/
package dog.mail.nntp;
import java.io.*;
import java.net.*;
import java.util.*;
import javax.mail.*;
import javax.mail.event.*;
import javax.mail.internet.*;
import dog.mail.util.*;
import dog.util.*;
/**
* The storage class implementing the NNTP Usenet news protocol.
*
* @author dog@dog.net.uk
* @version 1.2
*/
public class NNTPStore extends Store implements StatusSource {
/**
* The default NNTP port.
*/
public static final int DEFAULT_PORT = 119;
static int fetchsize = 1024;
Socket socket;
CRLFInputStream in;
CRLFOutputStream out;
String hostname;
String response;
static final int HELP = 100;
static final int READY = 200;
static final int READ_ONLY = 201;
static final int STREAMING_OK = 203;
static final int CLOSING = 205;
static final int GROUP_SELECTED = 211; // or list of article numbers follows
static final int LISTING = 215; // list of newsgroups follows
static final int ARTICLE_RETRIEVED_BOTH = 220;
static final int ARTICLE_RETRIEVED_HEAD = 221; // or header follows
static final int ARTICLE_RETRIEVED_BODY = 222;
static final int ARTICLE_RETRIEVED = 223;
static final int LISTING_OVERVIEW = 224;
static final int LISTING_ARTICLES = 230; // list of new articles by message-id follows
static final int LISTING_NEW = 231; // list of new newsgroups follows
static final int ARTICLE_POSTED = 240; // article posted ok
static final int AUTHINFO_OK = 281;
static final int SEND_ARTICLE = 340; // send article to be posted. End with <CR-LF>.<CR-LF>
static final int SEND_AUTHINFOPASS = 381; // send password (response to user name)
static final int SERVICE_DISCONTINUED = 400;
static final int NO_SUCH_GROUP = 411;
static final int NO_GROUP_SELECTED = 412; // no newsgroup has been selected
static final int NO_ARTICLE_SELECTED = 420; // no current article has been selected
static final int NO_SUCH_ARTICLE_IN_GROUP = 423; // no such article number in this group
static final int NO_SUCH_ARTICLE = 430; // no such article found
static final int POSTING_NOT_ALLOWED = 440; // posting not allowed
static final int POSTING_FAILED = 441; // posting failed
static final int COMMAND_NOT_RECOGNIZED = 500;
static final int COMMAND_SYNTAX_ERROR = 501;
static final int PERMISSION_DENIED = 502;
static final int SERVER_ERROR = 503;
boolean postingAllowed = false;
Root root;
Newsgroup current;
Date lastNewGroup;
Hashtable newsgroups = new Hashtable(); // hashtable of newsgroups by name
Hashtable articles = new Hashtable(); // hashtable of articles by message-id
Vector statusListeners = new Vector();
/**
* Constructor.
*/
public NNTPStore(Session session, URLName urlname) {
super(session, urlname);
String ccs = session.getProperty("mail.nntp.fetchsize");
if (ccs!=null) try { fetchsize = Math.max(Integer.parseInt(ccs), 1024); } catch (NumberFormatException e) {}
}
/**
* Connects to the NNTP server and authenticates with the specified parameters.
*/
protected boolean protocolConnect(String host, int port, String username, String password) throws MessagingException {
if (port<0) port = DEFAULT_PORT;
if (host==null)
return false;
try {
boolean debug = session.getDebug();
hostname = host;
if (debug)
System.err.println("DEBUG: nntp: opening connection to "+hostname);
socket = new Socket(host, port);
in = new CRLFInputStream(new BufferedInputStream(socket.getInputStream()), fetchsize);
out = new CRLFOutputStream(new BufferedOutputStream(socket.getOutputStream()));
switch (getResponse()) {
case READY:
postingAllowed = true;
case READ_ONLY:
//StringTokenizer st = new StringTokenizer(response);
//if (st.hasMoreTokens()) hostname = st.nextToken();
break;
default:
throw new MessagingException("unexpected server response: "+response);
}
send("MODE READER"); // newsreader extension
switch (getResponse()) {
case READY:
postingAllowed = true;
case READ_ONLY:
break;
}
// NNTP basic authentication
// introduced by Volker Schmidt <vs75@gmx.de>
// NOTE: need to handle multiple varities of authentication (kerberos, etc)
if (username!=null && password!=null) {
send("AUTHINFO USER "+username);
if (getResponse()!=SEND_AUTHINFOPASS)
throw new AuthenticationFailedException(response);
send("AUTHINFO PASS "+password);
if (getResponse()!=AUTHINFO_OK)
throw new AuthenticationFailedException(response);
}
// end authentication
if (debug)
System.err.println("DEBUG: nntp: connected to "+hostname+", posting is "+(postingAllowed ? "" : "not ")+"allowed");
addStore(this);
readNewsrc();
return true;
} catch(UnknownHostException e) {
throw new MessagingException("unknown host", e);
} catch(IOException e) {
throw new MessagingException("I/O error", e);
}
}
/**
* Closes the connection.
*/
public synchronized void close() throws MessagingException {
if (socket!=null)
try {
boolean debug = session.getDebug();
if (debug)
System.err.println("DEBUG: nntp: closing connection to "+hostname);
send("QUIT");
switch (getResponse()) {
case CLOSING:
break;
case SERVER_ERROR:
if (response.toLowerCase().indexOf("timeout")>-1)
break;
default:
throw new MessagingException("unexpected server response: "+response);
}
removeStore(this);
socket.close();
socket = null;
if (debug)
System.err.println("DEBUG: nntp: closed connection to "+hostname);
} catch (IOException e) {
// socket.close() seems to throw an exception!
//throw new MessagingException("Close failed", e);
}
super.close();
}
/**
* Returns the hostname of the server.
*/
public String getHostName() { return hostname; }
int getResponse() throws IOException {
response = in.readLine();
boolean debug = session.getDebug();
if (debug)
System.err.println("DEBUG: nntp: <"+response);
if (response==null)
response = SERVER_ERROR+" timeout";
try {
int index = response.indexOf(' ');
if (index>-1) {
int code = Integer.parseInt(response.substring(0, index));
response = response.substring(index+1);
return code;
} else {
int code = Integer.parseInt(response);
return code;
}
} catch (StringIndexOutOfBoundsException e) {
throw new ProtocolException("NNTP protocol exception: "+response);
} catch (NumberFormatException e) {
throw new ProtocolException("NNTP protocol exception: "+response);
}
}
void send(String command) throws IOException {
boolean debug = session.getDebug();
if (debug)
System.err.println("DEBUG: nntp: >"+command);
out.write(command.getBytes());
out.writeln();
out.flush();
}
// Opens a newsgroup.
synchronized void open(Newsgroup group) throws MessagingException {
if (current!=null) {
if (current.equals(group))
return;
else
close(current);
}
String name = group.getName();
try {
send("GROUP "+name);
int r = getResponse();
switch (r) {
case GROUP_SELECTED:
try {
updateGroup(group, response);
group.open = true;
current = group;
} catch (NumberFormatException e) {
throw new MessagingException("NNTP protocol exception: "+response, e);
}
break;
case NO_SUCH_GROUP:
throw new MessagingException(response);
case SERVER_ERROR:
if (response.toLowerCase().indexOf("timeout")>-1) {
close();
connect();
open(group);
break;
}
default:
throw new MessagingException("unexpected server response ("+r+"): "+response);
}
} catch (IOException e) {
throw new MessagingException("I/O error", e);
}
}
// Updates a newsgroup with the most recent article counts.
void updateGroup(Newsgroup newsgroup, String response) throws IOException {
try {
StringTokenizer st = new StringTokenizer(response, " ");
newsgroup.count = Integer.parseInt(st.nextToken());
newsgroup.first = Integer.parseInt(st.nextToken());
newsgroup.last = Integer.parseInt(st.nextToken());
boolean debug = session.getDebug();
if (debug)
System.err.println("DEBUG: nntp: "+newsgroup.name+": "+newsgroup.count+" articles");
} catch (NumberFormatException e) {
throw new ProtocolException("NNTP protocol exception");
} catch (NoSuchElementException e) {
throw new ProtocolException("NNTP protocol exception");
}
}
// Closes a newsgroup.
synchronized void close(Newsgroup group) throws MessagingException {
if (current!=null && !current.equals(group)) close(current);
group.open = false;
}
// Returns the (approximate) number of articles in a newsgroup.
synchronized int getMessageCount(Newsgroup group) throws MessagingException {
String name = group.getName();
try {
send("GROUP "+name);
switch (getResponse()) {
case GROUP_SELECTED:
try {
updateGroup(group, response);
current = group;
return group.count;
} catch(NumberFormatException e) {
throw new MessagingException("NNTP protocol exception: "+response, e);
}
case NO_SUCH_GROUP:
throw new MessagingException("No such group");
case SERVER_ERROR:
if (response.toLowerCase().indexOf("timeout")>-1) {
close();
connect();
return getMessageCount(group);
}
default:
throw new MessagingException("unexpected server response: "+response);
}
} catch (IOException e) {
throw new MessagingException("I/O error", e);
}
}
// Returns the headers for an article.
synchronized InternetHeaders getHeaders(Article article) throws MessagingException {
new Throwable().printStackTrace();
String mid = article.messageId;
if (mid==null) {
Newsgroup group = (Newsgroup)article.getFolder();
if (!current.equals(group)) open(group);
mid = Integer.toString(article.getMessageNumber());
}
try {
send("HEAD "+mid);
switch (getResponse()) {
case ARTICLE_RETRIEVED_HEAD:
return new InternetHeaders(new MessageInputStream(in));
case NO_GROUP_SELECTED:
throw new MessagingException("No group selected");
case NO_ARTICLE_SELECTED:
throw new MessagingException("No article selected");
case NO_SUCH_ARTICLE_IN_GROUP:
//throw new MessagingException("No such article in group");
return null;
case NO_SUCH_ARTICLE:
throw new MessagingException("No such article");
case SERVER_ERROR:
if (response.toLowerCase().indexOf("timeout")>-1) {
close();
connect();
return getHeaders(article);
}
default:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -