// SmtpClient.java
/*******************************************************************************
* Copyright(c)1996-2005 GoodServer. http://www.goodserver.com *
* *
* This software is the property of GoodServer, with the information herein *
* reserved as proprietary to GoodServer, and is not to be published, *
* reproduced, copied, disclosed, or used without the expressed written *
* consent of a duly authorized representative of GoodServer. *
*******************************************************************************/
package com.goodserver.client.smtp;
import com.goodserver.net.*;
import com.goodserver.syntax.smtp.*;
import com.sun.mail.util.*;
import com.goodserver.protocol.*;
import com.goodserver.protocol.smtp.*;
import java.io.*;
import java.util.*;
import javax.net.*;
import javax.net.ssl.*;
/*******************************************************************************
* SMTP network client.
*
* @author David Rauschenbach
* @version 5/16/2002
*******************************************************************************/
public class SmtpClient
{
/** Socket client */
protected SocketClient aClient;
/** Pipeline of accumulated requests */
private List aPipeline = new ArrayList();
/** Request factory */
private RequestFactory aReqFactory;
/***************************************************************************
* Constructor
***************************************************************************/
public SmtpClient()
{
aClient = new SocketClient();
}
/***************************************************************************
* Closes the connection.
***************************************************************************/
public void
close()
throws IOException
{
try {
aClient.close();
} finally {
aReqFactory = null;
}
}
/***************************************************************************
* Connects to the server.
***************************************************************************/
public CommandResponse
connect(String strHost, int iPort)
throws IOException
{
aClient.connect(strHost, iPort);
aReqFactory = new RequestFactory(aClient.getTransport().getSocket());
return readResponse();
}
/***************************************************************************
* Flush outbound pending data. If pipelining is enabled, then this method
* causes all pipelined requests to be sent, and a reply for each pipelined
* request is returned. If pipelining is disabled, then this method returns
* null, but a flush is performed on the outbound data stream, which may
* be useful during command-continuation operations.
***************************************************************************/
public CommandResponse[]
flush()
throws IOException, ProtocolException
{
int iPipeline = aPipeline.size();
List aResponses = new ArrayList();
// Lock the pipeline for the long duration
synchronized (aPipeline) {
for (int i = 0; i < aPipeline.size(); i++) {
SmtpRequest aReq = (SmtpRequest)aPipeline.get(i);
aClient.write(aReq);
}
try {
aClient.flush();
for (int i = 0; i < iPipeline; i++) {
aResponses.add(readResponse());
}
} finally {
aPipeline.clear();
}
}
CommandResponse[] aaResponses = new CommandResponse[aResponses.size()];
aResponses.toArray(aaResponses);
return aaResponses;
}
/***************************************************************************
* Returns the host name of the host that is connected to, or null if no
* connection is active.
***************************************************************************/
public String
getHost()
{
try {
return aClient.getTransport().getInetAddress().getHostName();
} catch (NullPointerException e) {
return null;
}
}
/***************************************************************************
* Reads a reply from the server.
***************************************************************************/
OutputStream
getOutputStream()
throws IOException
{
return aClient.getTransport().getOutputStream();
}
/***************************************************************************
* Returns the port that was used to connect to the target server, or 0 if
* no connection is active.
***************************************************************************/
public int
getPort()
{
try {
return aClient.getTransport().getSocket().getPort();
} catch (NullPointerException e) {
return 0;
}
}
/***************************************************************************
* Returns the request factory.
***************************************************************************/
protected RequestFactory
getRequestFactory()
{
return aReqFactory;
}
/***************************************************************************
* Returns whether the client is connected.
***************************************************************************/
public boolean
isConnected()
{
return aClient.isConnected();
}
/***************************************************************************
* Reads a response from the server.
***************************************************************************/
protected CommandResponse
readResponse()
throws IOException
{
StringBuffer aMessage = new StringBuffer();
ResponseCode aResponseCode = null;
while (true) {
String strLine = aClient.readLine();
if (strLine.length() < 3)
throw new IOException("Malformed response");
aResponseCode = ResponseCode.create(
strLine.charAt(0), strLine.charAt(1), strLine.charAt(2));
if (aMessage.length() > 0) {
aMessage.append((char)SmtpSyntaxHelper.CR);
aMessage.append((char)SmtpSyntaxHelper.LF);
}
String strMessage = (strLine.length() < 5 ? "" : strLine.substring(4));
aMessage.append(strMessage);
if (strLine.length() >= 4 && strLine.charAt(3) == '-')
continue;
break;
}
CommandResponseImpl aRes = new CommandResponseImpl();
aRes.setCode(aResponseCode);
aRes.setMessage(aMessage.toString());
return aRes;
}
/***************************************************************************
* Sends a request.
*
* @return the server response, or null when pipeling is enabled.
***************************************************************************/
protected CommandResponse
send(SmtpRequest aRequest)
throws IOException, ProtocolException
{
if (aClient.isPipeliningEnabled()) {
aPipeline.add(aRequest);
return null;
} else {
aClient.write(aRequest);
return readResponse();
}
}
/***************************************************************************
* Sends the AUTH command.
*
* <p><table cols=1 width="90%" border=1 cellpadding=2 cellspacing=0>
* <tr bgcolor="#8080C0"><td><b>RFC 2554 p. 2</b></td></tr>
* <tr><td><pre>
* The AUTH command indicates an authenticateion mechanism to the
* server. If the server supports the requested authentication
* mechanism, it performs an authentication protocol exchange to
* authenticate and identify the user. Optionally, it also
* negotiates a security layer for subsequent protocol
* interactions. If the requested authentication mechanism is not
* supported, the server rejects the AUTH command with a 504
* reply.
*
* The authentication protocol exchange consists of a series of
* server challenges and client answers that are specific to the
* authentication mechanism. A server challenge, otherwise known
* as a ready response, is a 334 reply with the text part
* containing a BASE64 encoded string. The client answer consists
* of a line containing a BASE64 encoded string. If the client
* wishes to cancel an authentication exchange, it issues a line
* with a single "*". If the server receives such an answer, it
* MUST reject the AUTH command by sending a 501
评论0
最新资源