Fields Summary |
---|
private static final org.apache.mailet.dates.SimplifiedDateFormat | DF_RFC977used to calculate DATE from - see 11.3 |
private static final org.apache.mailet.dates.SimplifiedDateFormat | DF_RFC2980Date format for the DATE keyword - see 11.1.1 |
public static final long | UTC_OFFSETThe UTC offset for this time zone. |
private static final String | COMMAND_MODEThe text string for the NNTP MODE command. |
private static final String | COMMAND_LISTThe text string for the NNTP LIST command. |
private static final String | COMMAND_GROUPThe text string for the NNTP GROUP command. |
private static final String | COMMAND_NEXTThe text string for the NNTP NEXT command. |
private static final String | COMMAND_LASTThe text string for the NNTP LAST command. |
private static final String | COMMAND_ARTICLEThe text string for the NNTP ARTICLE command. |
private static final String | COMMAND_HEADThe text string for the NNTP HEAD command. |
private static final String | COMMAND_BODYThe text string for the NNTP BODY command. |
private static final String | COMMAND_STATThe text string for the NNTP STAT command. |
private static final String | COMMAND_POSTThe text string for the NNTP POST command. |
private static final String | COMMAND_IHAVEThe text string for the NNTP IHAVE command. |
private static final String | COMMAND_QUITThe text string for the NNTP QUIT command. |
private static final String | COMMAND_SLAVEThe text string for the NNTP SLAVE command. |
private static final String | COMMAND_DATEThe text string for the NNTP DATE command. |
private static final String | COMMAND_HELPThe text string for the NNTP HELP command. |
private static final String | COMMAND_NEWGROUPSThe text string for the NNTP NEWGROUPS command. |
private static final String | COMMAND_NEWNEWSThe text string for the NNTP NEWNEWS command. |
private static final String | COMMAND_LISTGROUPThe text string for the NNTP LISTGROUP command. |
private static final String | COMMAND_OVERThe text string for the NNTP OVER command. |
private static final String | COMMAND_XOVERThe text string for the NNTP XOVER command. |
private static final String | COMMAND_HDRThe text string for the NNTP HDR command. |
private static final String | COMMAND_XHDRThe text string for the NNTP XHDR command. |
private static final String | COMMAND_AUTHINFOThe text string for the NNTP AUTHINFO command. |
private static final String | COMMAND_PATThe text string for the NNTP PAT command. |
private static final String | MODE_TYPE_READERThe text string for the NNTP MODE READER parameter. |
private static final String | MODE_TYPE_STREAMThe text string for the NNTP MODE STREAM parameter. |
private static final String | AUTHINFO_PARAM_USERThe text string for the NNTP AUTHINFO USER parameter. |
private static final String | AUTHINFO_PARAM_PASSThe text string for the NNTP AUTHINFO PASS parameter. |
private static final char[] | NNTPTerminatorThe character array that indicates termination of an NNTP message |
private Thread | handlerThreadThe thread executing this handler |
private String | remoteHostThe remote host name obtained by lookup on the socket. |
private String | remoteIPThe remote IP address of the socket. |
private Socket | socketThe TCP/IP socket over which the POP3 interaction
is occurring |
private InputStream | inThe incoming stream of bytes coming from the socket. |
private BufferedReader | readerThe reader associated with incoming characters. |
private OutputStream | outsThe socket's output stream |
private PrintWriter | writerThe writer to which outgoing messages are written. |
private org.apache.james.nntpserver.repository.NNTPGroup | groupThe current newsgroup. |
private int | currentArticleNumberThe current newsgroup. |
private NNTPHandlerConfigurationData | theConfigDataPer-service configuration data that applies to all handlers
associated with the service. |
private String | userThe user id associated with the NNTP dialogue |
private String | passwordThe password associated with the NNTP dialogue |
boolean | isAlreadyAuthenticatedWhether the user for this session has already authenticated.
Used to optimize authentication checks |
private org.apache.james.util.watchdog.Watchdog | theWatchdogThe watchdog being used by this handler to deal with idle timeouts. |
private org.apache.james.util.watchdog.WatchdogTarget | theWatchdogTargetThe watchdog target that idles out this handler. |
Methods Summary |
---|
private void | createArticle()Handles the transaction for getting the article data.
try {
InputStream msgIn = new CharTerminatedInputStream(in, NNTPTerminator);
// Removes the dot stuffing
msgIn = new DotStuffingInputStream(msgIn);
MailHeaders headers = new MailHeaders(msgIn);
processMessageHeaders(headers);
processMessage(headers, msgIn);
} catch (MessagingException me) {
throw new NNTPException("MessagingException encountered when loading article.");
}
|
private void | doARTICLE(java.lang.String param)Executes the ARTICLE command. Sets the current article pointer,
returns article information and contents.
// section 9.2.1
NNTPArticle article = null;
if (isMessageId(param)) {
article = theConfigData.getNNTPRepository().getArticleFromID(param);
if ( article == null ) {
writeLoggedFlushedResponse("430 no such article");
return;
} else {
StringBuffer respBuffer =
new StringBuffer(64)
.append("220 0 ")
.append(param);
writeLoggedResponse(respBuffer.toString());
}
} else {
int newArticleNumber = currentArticleNumber;
if ( group == null ) {
writeLoggedFlushedResponse("412 no newsgroup selected");
return;
} else {
if ( param == null ) {
if ( currentArticleNumber < 0 ) {
writeLoggedFlushedResponse("420 no current article selected");
return;
} else {
article = group.getArticle(currentArticleNumber);
}
}
else {
newArticleNumber = Integer.parseInt(param);
article = group.getArticle(newArticleNumber);
}
if ( article == null ) {
writeLoggedFlushedResponse("423 no such article number in this group");
return;
} else {
currentArticleNumber = newArticleNumber;
String articleID = article.getUniqueID();
if (articleID == null) {
articleID = "<0>";
}
StringBuffer respBuffer =
new StringBuffer(128)
.append("220 ")
.append(article.getArticleNumber())
.append(" ")
.append(articleID);
writeLoggedFlushedResponse(respBuffer.toString());
}
}
}
if (article != null) {
writer.flush();
article.writeArticle(new ExtraDotOutputStream(outs));
// see jira JAMES-311 for an explanation of the "\r\n"
writeLoggedFlushedResponse("\r\n.");
}
|
private void | doAUTHINFO(java.lang.String argument)Implements only the originnal AUTHINFO.
for simple and generic AUTHINFO, 501 is sent back. This is as
per article 3.1.3 of RFC 2980
String command = null;
String value = null;
if (argument != null) {
int spaceIndex = argument.indexOf(" ");
if (spaceIndex >= 0) {
command = argument.substring(0, spaceIndex);
value = argument.substring(spaceIndex + 1);
}
}
if (command == null) {
writeLoggedFlushedResponse("501 Syntax error");
return;
}
command = command.toUpperCase(Locale.US);
if ( command.equals(AUTHINFO_PARAM_USER) ) {
// Reject re-authentication
if ( isAlreadyAuthenticated ) {
writeLoggedFlushedResponse("482 Already authenticated - rejecting new credentials");
}
// Reject doubly sent user
if (user != null) {
user = null;
password = null;
isAlreadyAuthenticated = false;
writeLoggedFlushedResponse("482 User already specified - rejecting new user");
return;
}
user = value;
writeLoggedFlushedResponse("381 More authentication information required");
} else if ( command.equals(AUTHINFO_PARAM_PASS) ) {
// Reject password sent before user
if (user == null) {
writeLoggedFlushedResponse("482 User not yet specified. Rejecting user.");
return;
}
// Reject doubly sent password
if (password != null) {
user = null;
password = null;
isAlreadyAuthenticated = false;
writeLoggedFlushedResponse("482 Password already specified - rejecting new password");
return;
}
password = value;
isAlreadyAuthenticated = isAuthenticated();
if ( isAlreadyAuthenticated ) {
writeLoggedFlushedResponse("281 Authentication accepted");
} else {
writeLoggedFlushedResponse("482 Authentication rejected");
// Clear bad authentication
user = null;
password = null;
}
} else {
writeLoggedFlushedResponse("501 Syntax error");
return;
}
|
private void | doBODY(java.lang.String param)Executes the BODY command. Sets the current article pointer,
returns article information and body.
// section 9.2.3
NNTPArticle article = null;
if (isMessageId(param)) {
article = theConfigData.getNNTPRepository().getArticleFromID(param);
if ( article == null ) {
writeLoggedFlushedResponse("430 no such article");
return;
} else {
StringBuffer respBuffer =
new StringBuffer(64)
.append("222 0 ")
.append(param);
writeLoggedFlushedResponse(respBuffer.toString());
}
} else {
int newArticleNumber = currentArticleNumber;
if ( group == null ) {
writeLoggedFlushedResponse("412 no newsgroup selected");
return;
} else {
if ( param == null ) {
if ( currentArticleNumber < 0 ) {
writeLoggedFlushedResponse("420 no current article selected");
return;
} else {
article = group.getArticle(currentArticleNumber);
}
}
else {
newArticleNumber = Integer.parseInt(param);
article = group.getArticle(newArticleNumber);
}
if ( article == null ) {
writeLoggedFlushedResponse("423 no such article number in this group");
return;
} else {
currentArticleNumber = newArticleNumber;
String articleID = article.getUniqueID();
if (articleID == null) {
articleID = "<0>";
}
StringBuffer respBuffer =
new StringBuffer(128)
.append("222 ")
.append(article.getArticleNumber())
.append(" ")
.append(articleID);
writeLoggedFlushedResponse(respBuffer.toString());
}
}
}
if (article != null) {
writer.flush();
article.writeBody(new ExtraDotOutputStream(outs));
writeLoggedFlushedResponse("\r\n.");
}
|
private void | doDATE(java.lang.String argument)Returns the current date according to the news server.
Date dt = new Date(System.currentTimeMillis()-UTC_OFFSET);
String dtStr = DF_RFC2980.format(new Date(dt.getTime() - UTC_OFFSET));
writeLoggedFlushedResponse("111 " + dtStr);
|
private void | doGROUP(java.lang.String groupName)Selects a group to be the current newsgroup.
if (groupName == null) {
writeLoggedFlushedResponse("501 Syntax error - missing required parameter");
return;
}
NNTPGroup newGroup = theConfigData.getNNTPRepository().getGroup(groupName);
// section 9.1.1.1
if ( newGroup == null ) {
writeLoggedFlushedResponse("411 no such newsgroup");
} else {
group = newGroup;
// if the number of articles in group == 0
// then the server may return this information in 3 ways,
// The clients must honor all those 3 ways.
// our response is:
// highWaterMark == lowWaterMark and number of articles == 0
int articleCount = group.getNumberOfArticles();
int lowWaterMark = group.getFirstArticleNumber();
int highWaterMark = group.getLastArticleNumber();
// Set the current article pointer. If no
// articles are in the group, the current article
// pointer should be explicitly unset.
if (articleCount != 0) {
currentArticleNumber = lowWaterMark;
} else {
currentArticleNumber = -1;
}
StringBuffer respBuffer =
new StringBuffer(128)
.append("211 ")
.append(articleCount)
.append(" ")
.append(lowWaterMark)
.append(" ")
.append(highWaterMark)
.append(" ")
.append(group.getName())
.append(" group selected");
writeLoggedFlushedResponse(respBuffer.toString());
}
|
private void | doHDR(java.lang.String argument)Get the values of the headers for the selected newsgroup,
with an optional range modifier.
// 9.5.3
if (argument == null) {
writeLoggedFlushedResponse("501 Syntax error - missing required parameter");
return;
}
String hdr = argument;
String range = null;
int spaceIndex = hdr.indexOf(" ");
if (spaceIndex >= 0 ) {
range = hdr.substring(spaceIndex + 1);
hdr = hdr.substring(0, spaceIndex);
}
if (group == null ) {
writeLoggedFlushedResponse("412 No news group currently selected.");
return;
}
if ((range == null) && (currentArticleNumber < 0)) {
writeLoggedFlushedResponse("420 No current article selected");
return;
}
NNTPArticle[] article = getRange(range);
if ( article == null ) {
writeLoggedFlushedResponse("412 no newsgroup selected");
} else if ( article.length == 0 ) {
writeLoggedFlushedResponse("430 no such article");
} else {
writeLoggedFlushedResponse("221 Header follows");
for ( int i = 0 ; i < article.length ; i++ ) {
String val = article[i].getHeader(hdr);
if ( val == null ) {
val = "";
}
StringBuffer hdrBuffer =
new StringBuffer(128)
.append(article[i].getArticleNumber())
.append(" ")
.append(val);
writeLoggedResponse(hdrBuffer.toString());
}
writeLoggedFlushedResponse(".");
}
|
private void | doHEAD(java.lang.String param)Executes the HEAD command. Sets the current article pointer,
returns article information and headers.
// section 9.2.2
NNTPArticle article = null;
if (isMessageId(param)) {
article = theConfigData.getNNTPRepository().getArticleFromID(param);
if ( article == null ) {
writeLoggedFlushedResponse("430 no such article");
return;
} else {
StringBuffer respBuffer =
new StringBuffer(64)
.append("221 0 ")
.append(param);
writeLoggedFlushedResponse(respBuffer.toString());
}
} else {
int newArticleNumber = currentArticleNumber;
if ( group == null ) {
writeLoggedFlushedResponse("412 no newsgroup selected");
return;
} else {
if ( param == null ) {
if ( currentArticleNumber < 0 ) {
writeLoggedFlushedResponse("420 no current article selected");
return;
} else {
article = group.getArticle(currentArticleNumber);
}
}
else {
newArticleNumber = Integer.parseInt(param);
article = group.getArticle(newArticleNumber);
}
if ( article == null ) {
writeLoggedFlushedResponse("423 no such article number in this group");
return;
} else {
currentArticleNumber = newArticleNumber;
String articleID = article.getUniqueID();
if (articleID == null) {
articleID = "<0>";
}
StringBuffer respBuffer =
new StringBuffer(128)
.append("221 ")
.append(article.getArticleNumber())
.append(" ")
.append(articleID);
writeLoggedFlushedResponse(respBuffer.toString());
}
}
}
if (article != null) {
writer.flush();
article.writeHead(new ExtraDotOutputStream(outs));
writeLoggedFlushedResponse(".");
}
|
private void | doHELP(java.lang.String argument)Lists the help text for the service.
writeLoggedResponse("100 Help text follows");
writeLoggedFlushedResponse(".");
|
private void | doIHAVE(java.lang.String id)Informs the server that the client has an article with the specified
message-ID.
// see section 9.3.2.1
if (!isMessageId(id)) {
writeLoggedFlushedResponse("501 command syntax error");
return;
}
NNTPArticle article = theConfigData.getNNTPRepository().getArticleFromID(id);
if ( article != null ) {
writeLoggedFlushedResponse("435 article not wanted - do not send it");
} else {
writeLoggedFlushedResponse("335 send article to be transferred. End with <CR-LF>.<CR-LF>");
try {
createArticle();
} catch (RuntimeException e) {
writeLoggedFlushedResponse("436 transfer failed - try again later");
throw e;
}
writeLoggedFlushedResponse("235 article received ok");
}
|
private void | doLAST(java.lang.String argument)Moves the currently selected article pointer to the article
previous to the currently selected article in the selected group.
// section 9.1.1.2.1
if ( argument != null ) {
writeLoggedFlushedResponse("501 Syntax error - unexpected parameter");
} else if ( group == null ) {
writeLoggedFlushedResponse("412 no newsgroup selected");
} else if ( currentArticleNumber < 0 ) {
writeLoggedFlushedResponse("420 no current article has been selected");
} else if ( currentArticleNumber <= group.getFirstArticleNumber() ) {
writeLoggedFlushedResponse("422 no previous article in this group");
} else {
currentArticleNumber--;
NNTPArticle article = group.getArticle(currentArticleNumber);
StringBuffer respBuffer =
new StringBuffer(64)
.append("223 ")
.append(article.getArticleNumber())
.append(" ")
.append(article.getUniqueID());
writeLoggedFlushedResponse(respBuffer.toString());
}
|
private void | doLIST(java.lang.String argument)Handles the LIST command and its assorted extensions.
// see section 9.4.1
String wildmat = "*";
boolean isListNewsgroups = false;
String extension = argument;
if (argument != null) {
int spaceIndex = argument.indexOf(" ");
if (spaceIndex >= 0) {
wildmat = argument.substring(spaceIndex + 1);
extension = argument.substring(0, spaceIndex);
}
extension = extension.toUpperCase(Locale.US);
}
if (extension != null) {
if (extension.equals("ACTIVE")) {
isListNewsgroups = false;
} else if (extension.equals("NEWSGROUPS") ) {
isListNewsgroups = true;
} else if (extension.equals("EXTENSIONS") ) {
doLISTEXTENSIONS();
return;
} else if (extension.equals("OVERVIEW.FMT") ) {
doLISTOVERVIEWFMT();
return;
} else if (extension.equals("ACTIVE.TIMES") ) {
// not supported - 9.4.2.1, 9.4.3.1, 9.4.4.1
writeLoggedFlushedResponse("503 program error, function not performed");
return;
} else if (extension.equals("DISTRIBUTIONS") ) {
// not supported - 9.4.2.1, 9.4.3.1, 9.4.4.1
writeLoggedFlushedResponse("503 program error, function not performed");
return;
} else if (extension.equals("DISTRIB.PATS") ) {
// not supported - 9.4.2.1, 9.4.3.1, 9.4.4.1
writeLoggedFlushedResponse("503 program error, function not performed");
return;
} else {
writeLoggedFlushedResponse("501 Syntax error");
return;
}
}
Iterator iter = theConfigData.getNNTPRepository().getMatchedGroups(wildmat);
writeLoggedFlushedResponse("215 list of newsgroups follows");
while ( iter.hasNext() ) {
NNTPGroup theGroup = (NNTPGroup)iter.next();
if (isListNewsgroups) {
writeLoggedResponse(theGroup.getListNewsgroupsFormat());
} else {
writeLoggedResponse(theGroup.getListFormat());
}
}
writeLoggedFlushedResponse(".");
|
private void | doLISTEXTENSIONS()Lists the extensions supported by this news server.
// 8.1.1
writeLoggedResponse("202 Extensions supported:");
writeLoggedResponse("LISTGROUP");
writeLoggedResponse("AUTHINFO");
writeLoggedResponse("OVER");
writeLoggedResponse("XOVER");
writeLoggedResponse("HDR");
writeLoggedResponse("XHDR");
writeLoggedFlushedResponse(".");
|
private void | doLISTGROUP(java.lang.String groupName)Gets a listing of article numbers in specified group name
or in the already selected group if the groupName is null.
// 9.5.1.1.1
if (groupName==null) {
if ( group == null) {
writeLoggedFlushedResponse("412 no news group currently selected");
return;
}
}
else {
group = theConfigData.getNNTPRepository().getGroup(groupName);
if ( group == null ) {
writeLoggedFlushedResponse("411 no such newsgroup");
return;
}
}
if ( group != null ) {
// this.group = group;
// Set the current article pointer. If no
// articles are in the group, the current article
// pointer should be explicitly unset.
if (group.getNumberOfArticles() > 0) {
currentArticleNumber = group.getFirstArticleNumber();
} else {
currentArticleNumber = -1;
}
writeLoggedFlushedResponse("211 list of article numbers follow");
Iterator iter = group.getArticles();
while (iter.hasNext()) {
NNTPArticle article = (NNTPArticle)iter.next();
writeLoggedResponse(article.getArticleNumber() + "");
}
writeLoggedFlushedResponse(".");
}
|
private void | doLISTOVERVIEWFMT()Handles the LIST OVERVIEW.FMT command. Not supported.
// 9.5.3.1.1
writeLoggedFlushedResponse("215 Information follows");
String[] overviewHeaders = theConfigData.getNNTPRepository().getOverviewFormat();
for (int i = 0; i < overviewHeaders.length; i++) {
writeLoggedResponse(overviewHeaders[i]);
}
writeLoggedFlushedResponse(".");
|
private void | doMODEREADER(java.lang.String argument)Informs the server that the client is a newsreader.
// 7.2
writeLoggedFlushedResponse(theConfigData.getNNTPRepository().isReadOnly()
? "201 Posting Not Permitted" : "200 Posting Permitted");
|
private void | doMODESTREAM(java.lang.String argument)Informs the server that the client is a news server.
// 7.2
writeLoggedFlushedResponse("500 Command not understood");
|
private void | doNEWGROUPS(java.lang.String argument)Lists the groups added since the date passed in as
an argument.
// see section 11.3
// both draft-ietf-nntpext-base-15.txt and rfc977 have only group names
// in response lines, but INN sends
// '<group name> <last article> <first article> <posting allowed>'
// NOTE: following INN over either document.
//
// TODO: Check this. Audit at http://www.academ.com/pipermail/ietf-nntp/2001-July/002185.html
// doesn't mention the supposed discrepancy. Consider changing code to
// be in line with spec.
Date theDate = null;
try {
theDate = getDateFrom(argument);
} catch (NNTPException nntpe) {
getLogger().error("NEWGROUPS had an invalid argument", nntpe);
writeLoggedFlushedResponse("501 Syntax error");
return;
}
Iterator iter = theConfigData.getNNTPRepository().getGroupsSince(theDate);
writeLoggedFlushedResponse("231 list of new newsgroups follows");
while ( iter.hasNext() ) {
NNTPGroup currentGroup = (NNTPGroup)iter.next();
StringBuffer iterBuffer =
new StringBuffer(128)
.append(currentGroup.getName())
.append(" ")
.append(currentGroup.getLastArticleNumber())
.append(" ")
.append(currentGroup.getFirstArticleNumber())
.append(" ")
.append((currentGroup.isPostAllowed()?"y":"n"));
writeLoggedResponse(iterBuffer.toString());
}
writeLoggedFlushedResponse(".");
|
private void | doNEWNEWS(java.lang.String argument)Lists the articles posted since the date passed in as
an argument.
// see section 11.4
String wildmat = "*";
if (argument != null) {
int spaceIndex = argument.indexOf(" ");
if (spaceIndex >= 0) {
wildmat = argument.substring(0, spaceIndex);
argument = argument.substring(spaceIndex + 1);
} else {
getLogger().error("NEWNEWS had an invalid argument");
writeLoggedFlushedResponse("501 Syntax error");
return;
}
} else {
getLogger().error("NEWNEWS had a null argument");
writeLoggedFlushedResponse("501 Syntax error");
return;
}
Date theDate = null;
try {
theDate = getDateFrom(argument);
} catch (NNTPException nntpe) {
getLogger().error("NEWNEWS had an invalid argument", nntpe);
writeLoggedFlushedResponse("501 Syntax error");
return;
}
writeLoggedFlushedResponse("230 list of new articles by message-id follows");
Iterator groupIter = theConfigData.getNNTPRepository().getMatchedGroups(wildmat);
while ( groupIter.hasNext() ) {
Iterator articleIter = ((NNTPGroup)(groupIter.next())).getArticlesSince(theDate);
while (articleIter.hasNext()) {
StringBuffer iterBuffer =
new StringBuffer(64)
.append(((NNTPArticle)articleIter.next()).getUniqueID());
writeLoggedResponse(iterBuffer.toString());
}
}
writeLoggedFlushedResponse(".");
|
private void | doNEXT(java.lang.String argument)Advances the current article pointer to the next article in the group.
// section 9.1.1.3.1
if ( argument != null ) {
writeLoggedFlushedResponse("501 Syntax error - unexpected parameter");
} else if ( group == null ) {
writeLoggedFlushedResponse("412 no newsgroup selected");
} else if ( currentArticleNumber < 0 ) {
writeLoggedFlushedResponse("420 no current article has been selected");
} else if ( currentArticleNumber >= group.getLastArticleNumber() ) {
writeLoggedFlushedResponse("421 no next article in this group");
} else {
currentArticleNumber++;
NNTPArticle article = group.getArticle(currentArticleNumber);
StringBuffer respBuffer =
new StringBuffer(64)
.append("223 ")
.append(article.getArticleNumber())
.append(" ")
.append(article.getUniqueID());
writeLoggedFlushedResponse(respBuffer.toString());
}
|
private void | doOVER(java.lang.String range)Returns information from the overview database regarding the
current article, or a range of articles.
// 9.5.2.2.1
if ( group == null ) {
writeLoggedFlushedResponse("412 No newsgroup selected");
return;
}
if ((range == null) && (currentArticleNumber < 0)) {
writeLoggedFlushedResponse("420 No current article selected");
return;
}
NNTPArticle[] article = getRange(range);
if ( article.length == 0 ) {
writeLoggedFlushedResponse("420 No article(s) selected");
} else {
writeLoggedResponse("224 Overview information follows");
for ( int i = 0 ; i < article.length ; i++ ) {
article[i].writeOverview(outs);
if (i % 100 == 0) {
// Reset the watchdog every hundred headers or so
// to ensure the connection doesn't timeout for slow
// clients
theWatchdog.reset();
}
}
writeLoggedFlushedResponse(".");
}
|
private void | doPAT(java.lang.String argument)Handles the PAT command. Not supported.
// 9.5.3.1.1 in draft-12
writeLoggedFlushedResponse("500 Command not recognized");
|
private void | doPOST(java.lang.String argument)Posts an article to the news server.
// see section 9.3.1.1
if ( argument != null ) {
writeLoggedFlushedResponse("501 Syntax error - unexpected parameter");
}
writeLoggedFlushedResponse("340 send article to be posted. End with <CR-LF>.<CR-LF>");
createArticle();
writeLoggedFlushedResponse("240 article received ok");
|
private void | doQUIT(java.lang.String argument)Quits the transaction.
writeLoggedFlushedResponse("205 closing connection");
|
private void | doSLAVE(java.lang.String argument)Acknowledges a SLAVE command. No special preference is given
to slave connections.
writeLoggedFlushedResponse("202 slave status noted");
|
private void | doSTAT(java.lang.String param)Executes the STAT command. Sets the current article pointer,
returns article information.
// section 9.2.4
NNTPArticle article = null;
if (isMessageId(param)) {
article = theConfigData.getNNTPRepository().getArticleFromID(param);
if ( article == null ) {
writeLoggedFlushedResponse("430 no such article");
return;
} else {
StringBuffer respBuffer =
new StringBuffer(64)
.append("223 0 ")
.append(param);
writeLoggedFlushedResponse(respBuffer.toString());
}
} else {
int newArticleNumber = currentArticleNumber;
if ( group == null ) {
writeLoggedFlushedResponse("412 no newsgroup selected");
return;
} else {
if ( param == null ) {
if ( currentArticleNumber < 0 ) {
writeLoggedFlushedResponse("420 no current article selected");
return;
} else {
article = group.getArticle(currentArticleNumber);
}
}
else {
newArticleNumber = Integer.parseInt(param);
article = group.getArticle(newArticleNumber);
}
if ( article == null ) {
writeLoggedFlushedResponse("423 no such article number in this group");
return;
} else {
currentArticleNumber = newArticleNumber;
String articleID = article.getUniqueID();
if (articleID == null) {
articleID = "<0>";
}
StringBuffer respBuffer =
new StringBuffer(128)
.append("223 ")
.append(article.getArticleNumber())
.append(" ")
.append(articleID);
writeLoggedFlushedResponse(respBuffer.toString());
}
}
}
|
private void | doUnknownCommand(java.lang.String command, java.lang.String argument)Handles an unrecognized command, logging that.
if (getLogger().isDebugEnabled()) {
StringBuffer logBuffer =
new StringBuffer(128)
.append("Received unknown command ")
.append(command)
.append(" with argument ")
.append(argument);
getLogger().debug(logBuffer.toString());
}
writeLoggedFlushedResponse("500 Unknown command");
|
private void | doXHDR(java.lang.String argument)Get the values of the headers for the selected newsgroup,
with an optional range modifier.
doHDR(argument);
|
private void | doXOVER(java.lang.String range)Returns information from the overview database regarding the
current article, or a range of articles.
doOVER(range);
|
private java.util.Date | getDateFrom(java.lang.String argument)Returns the date from @param input.
The input tokens are assumed to be in format date time [GMT|UTC] .
'date' is in format [XX]YYMMDD. 'time' is in format 'HHMMSS'
NOTE: This routine could do with some format checks.
if (argument == null) {
throw new NNTPException("Date argument was absent.");
}
StringTokenizer tok = new StringTokenizer(argument, " ");
if (tok.countTokens() < 2) {
throw new NNTPException("Date argument was ill-formed.");
}
String date = tok.nextToken();
String time = tok.nextToken();
boolean utc = ( tok.hasMoreTokens() );
Date d = new Date();
try {
StringBuffer dateStringBuffer =
new StringBuffer(64)
.append(date)
.append(" ")
.append(time);
Date dt = DF_RFC977.parse(dateStringBuffer.toString());
if ( utc ) {
dt = new Date(dt.getTime()+UTC_OFFSET);
}
return dt;
} catch ( ParseException pe ) {
StringBuffer exceptionBuffer =
new StringBuffer(128)
.append("Date extraction failed: ")
.append(date)
.append(",")
.append(time)
.append(",")
.append(utc);
throw new NNTPException(exceptionBuffer.toString());
}
|
private org.apache.james.nntpserver.repository.NNTPArticle[] | getRange(java.lang.String range)Returns the list of articles that match the range.
A precondition of this method is that the selected
group be non-null. The current article pointer must
be valid if no range is explicitly provided.
// check for msg id
if ( isMessageId(range)) {
NNTPArticle article = theConfigData.getNNTPRepository().getArticleFromID(range);
return ( article == null )
? new NNTPArticle[0] : new NNTPArticle[] { article };
}
if ( range == null ) {
range = "" + currentArticleNumber;
}
int start = -1;
int end = -1;
int idx = range.indexOf('-");
if ( idx == -1 ) {
start = Integer.parseInt(range);
end = start;
} else {
start = Integer.parseInt(range.substring(0,idx));
if ( (idx + 1) == range.length() ) {
end = group.getLastArticleNumber();
} else {
end = Integer.parseInt(range.substring(idx + 1));
}
}
List list = new ArrayList();
for ( int i = start ; i <= end ; i++ ) {
NNTPArticle article = group.getArticle(i);
if ( article != null ) {
list.add(article);
}
}
return (NNTPArticle[])list.toArray(new NNTPArticle[0]);
|
org.apache.james.util.watchdog.WatchdogTarget | getWatchdogTarget()Gets the Watchdog Target that should be used by Watchdogs managing
this connection.
return theWatchdogTarget;
|
public void | handleConnection(java.net.Socket connection)
try {
this.socket = connection;
synchronized (this) {
handlerThread = Thread.currentThread();
}
remoteIP = socket.getInetAddress().getHostAddress();
remoteHost = socket.getInetAddress().getHostName();
in = new BufferedInputStream(socket.getInputStream(), 1024);
// An ASCII encoding can be used because all transmissions other
// that those in the message body command are guaranteed
// to be ASCII
reader = new BufferedReader(new InputStreamReader(in, "ASCII"), 512);
outs = new BufferedOutputStream(socket.getOutputStream(), 1024);
writer = new InternetPrintWriter(outs, true);
} catch (Exception e) {
StringBuffer exceptionBuffer =
new StringBuffer(256)
.append("Cannot open connection from ")
.append(remoteHost)
.append(" (")
.append(remoteIP)
.append("): ")
.append(e.getMessage());
String exceptionString = exceptionBuffer.toString();
getLogger().error(exceptionString, e );
}
try {
// section 7.1
if ( theConfigData.getNNTPRepository().isReadOnly() ) {
StringBuffer respBuffer =
new StringBuffer(128)
.append("201 ")
.append(theConfigData.getHelloName())
.append(" NNTP Service Ready, posting prohibited");
writeLoggedFlushedResponse(respBuffer.toString());
} else {
StringBuffer respBuffer =
new StringBuffer(128)
.append("200 ")
.append(theConfigData.getHelloName())
.append(" NNTP Service Ready, posting permitted");
writeLoggedFlushedResponse(respBuffer.toString());
}
theWatchdog.start();
while (parseCommand(reader.readLine())) {
theWatchdog.reset();
}
theWatchdog.stop();
getLogger().info("Connection closed");
} catch (Exception e) {
// If the connection has been idled out, the
// socket will be closed and null. Do NOT
// log the exception or attempt to send the
// closing connection message
if (socket != null) {
try {
doQUIT(null);
} catch (Throwable t) {}
getLogger().error( "Exception during connection:" + e.getMessage(), e );
}
} finally {
resetHandler();
}
|
void | idleClose()Idle out this connection
if (getLogger() != null) {
getLogger().error("NNTP Connection has idled out.");
}
try {
if (socket != null) {
socket.close();
}
} catch (Exception e) {
// ignored
} finally {
socket = null;
}
synchronized (this) {
// Interrupt the thread to recover from internal hangs
if (handlerThread != null) {
handlerThread.interrupt();
handlerThread = null;
}
}
|
private boolean | isAuthenticated()Return whether the connection has been authenticated.
if ( theConfigData.isAuthRequired() ) {
if ((user != null) && (password != null) && (theConfigData.getUsersRepository() != null)) {
return theConfigData.getUsersRepository().test(user,password);
} else {
return false;
}
} else {
return true;
}
|
private boolean | isAuthorized(java.lang.String command)Return whether the user associated with the connection (possibly no
user) is authorized to execute the command.
isAlreadyAuthenticated = isAlreadyAuthenticated || isAuthenticated();
if (isAlreadyAuthenticated) {
return true;
}
// some commands are authorized, even if the user is not authenticated
boolean allowed = command.equals("AUTHINFO");
allowed = allowed || command.equals("MODE");
allowed = allowed || command.equals("QUIT");
return allowed;
|
private static boolean | isMessageId(java.lang.String testString)Tests a string to see whether it is formatted as a message
ID.
if ((testString != null) &&
(testString.startsWith("<")) &&
(testString.endsWith(">"))) {
return true;
} else {
return false;
}
|
private final void | logResponseString(java.lang.String responseString)This method logs at a "DEBUG" level the response string that
was sent to the SMTP client. The method is provided largely
as syntactic sugar to neaten up the code base. It is declared
private and final to encourage compiler inlining.
if (getLogger().isDebugEnabled()) {
getLogger().debug("Sent: " + responseString);
}
|
private boolean | parseCommand(java.lang.String commandRaw)This method parses NNTP commands read off the wire in handleConnection.
Actual processing of the command (possibly including additional back and
forth communication with the client) is delegated to one of a number of
command specific handler methods. The primary purpose of this method is
to parse the raw command string to determine exactly which handler should
be called. It returns true if expecting additional commands, false otherwise.
if (commandRaw == null) {
return false;
}
if (getLogger().isDebugEnabled()) {
getLogger().debug("Command received: " + commandRaw);
}
String command = commandRaw.trim();
String argument = null;
int spaceIndex = command.indexOf(" ");
if (spaceIndex >= 0) {
argument = command.substring(spaceIndex + 1);
command = command.substring(0, spaceIndex);
}
command = command.toUpperCase(Locale.US);
boolean returnValue = true;
if (!isAuthorized(command) ) {
writeLoggedFlushedResponse("480 User is not authenticated");
getLogger().debug("Command not allowed.");
return returnValue;
}
if ((command.equals(COMMAND_MODE)) && (argument != null)) {
if (argument.toUpperCase(Locale.US).equals(MODE_TYPE_READER)) {
doMODEREADER(argument);
} else if (argument.toUpperCase(Locale.US).equals(MODE_TYPE_STREAM)) {
doMODESTREAM(argument);
} else {
writeLoggedFlushedResponse("500 Command not understood");
}
} else if ( command.equals(COMMAND_LIST)) {
doLIST(argument);
} else if ( command.equals(COMMAND_GROUP) ) {
doGROUP(argument);
} else if ( command.equals(COMMAND_NEXT) ) {
doNEXT(argument);
} else if ( command.equals(COMMAND_LAST) ) {
doLAST(argument);
} else if ( command.equals(COMMAND_ARTICLE) ) {
doARTICLE(argument);
} else if ( command.equals(COMMAND_HEAD) ) {
doHEAD(argument);
} else if ( command.equals(COMMAND_BODY) ) {
doBODY(argument);
} else if ( command.equals(COMMAND_STAT) ) {
doSTAT(argument);
} else if ( command.equals(COMMAND_POST) ) {
doPOST(argument);
} else if ( command.equals(COMMAND_IHAVE) ) {
doIHAVE(argument);
} else if ( command.equals(COMMAND_QUIT) ) {
doQUIT(argument);
returnValue = false;
} else if ( command.equals(COMMAND_DATE) ) {
doDATE(argument);
} else if ( command.equals(COMMAND_HELP) ) {
doHELP(argument);
} else if ( command.equals(COMMAND_NEWGROUPS) ) {
doNEWGROUPS(argument);
} else if ( command.equals(COMMAND_NEWNEWS) ) {
doNEWNEWS(argument);
} else if ( command.equals(COMMAND_LISTGROUP) ) {
doLISTGROUP(argument);
} else if ( command.equals(COMMAND_OVER) ) {
doOVER(argument);
} else if ( command.equals(COMMAND_XOVER) ) {
doXOVER(argument);
} else if ( command.equals(COMMAND_HDR) ) {
doHDR(argument);
} else if ( command.equals(COMMAND_XHDR) ) {
doXHDR(argument);
} else if ( command.equals(COMMAND_AUTHINFO) ) {
doAUTHINFO(argument);
} else if ( command.equals(COMMAND_SLAVE) ) {
doSLAVE(argument);
} else if ( command.equals(COMMAND_PAT) ) {
doPAT(argument);
} else {
doUnknownCommand(command, argument);
}
return returnValue;
|
private void | processMessage(org.apache.james.core.MailHeaders headers, java.io.InputStream bodyIn)Processes the NNTP message coming in off the wire. Reads the
content and delivers to the spool.
InputStream messageIn = null;
try {
messageIn = new SequenceInputStream(new ByteArrayInputStream(headers.toByteArray()), bodyIn);
theConfigData.getNNTPRepository().createArticle(messageIn);
} finally {
if (messageIn != null) {
try {
messageIn.close();
} catch (IOException ioe) {
// Ignore exception on close.
}
messageIn = null;
}
}
|
private org.apache.james.core.MailHeaders | processMessageHeaders(org.apache.james.core.MailHeaders headers)Processes the NNTP message headers coming in off the wire.
return headers;
|
private void | resetHandler()Resets the handler data to a basic state.
// Clear the Watchdog
if (theWatchdog != null) {
ContainerUtil.dispose(theWatchdog);
theWatchdog = null;
}
// Clear the streams
try {
if (reader != null) {
reader.close();
}
} catch (IOException ioe) {
getLogger().warn("NNTPHandler: Unexpected exception occurred while closing reader: " + ioe);
} finally {
reader = null;
}
in = null;
if (writer != null) {
writer.close();
writer = null;
}
outs = null;
remoteHost = null;
remoteIP = null;
try {
if (socket != null) {
socket.close();
}
} catch (IOException ioe) {
getLogger().warn("NNTPHandler: Unexpected exception occurred while closing socket: " + ioe);
} finally {
socket = null;
}
synchronized (this) {
handlerThread = null;
}
// Clear the selected group, article info
group = null;
currentArticleNumber = -1;
// Clear the authentication info
user = null;
password = null;
isAlreadyAuthenticated = false;
// Clear the config data
theConfigData = null;
|
void | setConfigurationData(NNTPHandlerConfigurationData theData)Set the configuration data for the handler
theConfigData = theData;
|
void | setWatchdog(org.apache.james.util.watchdog.Watchdog theWatchdog)Set the Watchdog for use by this handler.
this.theWatchdog = theWatchdog;
|
final void | writeLoggedFlushedResponse(java.lang.String responseString)Write and flush a response string. The response is also logged.
Should be used for the last line of a multi-line response or
for a single line response.
writer.println(responseString);
writer.flush();
logResponseString(responseString);
|
final void | writeLoggedResponse(java.lang.String responseString)Write a response string. The response is also logged.
Used for multi-line responses.
writer.println(responseString);
logResponseString(responseString);
|