geronimo-scm mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ge...@apache.org
Subject svn commit: r158693 - in geronimo/trunk/sandbox/mail/src/java/org/apache/geronimo/mail/smtp: MalformedSMTPReplyException.java SMTPReply.java SMTPTransport.java SMTPTransportException.java
Date Tue, 22 Mar 2005 22:53:54 GMT
Author: geirm
Date: Tue Mar 22 14:53:53 2005
New Revision: 158693

URL: http://svn.apache.org/viewcvs?view=rev&rev=158693
Log:
simple working version of primitive SMTP transport

Added:
    geronimo/trunk/sandbox/mail/src/java/org/apache/geronimo/mail/smtp/MalformedSMTPReplyException.java
    geronimo/trunk/sandbox/mail/src/java/org/apache/geronimo/mail/smtp/SMTPReply.java
    geronimo/trunk/sandbox/mail/src/java/org/apache/geronimo/mail/smtp/SMTPTransportException.java
Modified:
    geronimo/trunk/sandbox/mail/src/java/org/apache/geronimo/mail/smtp/SMTPTransport.java

Added: geronimo/trunk/sandbox/mail/src/java/org/apache/geronimo/mail/smtp/MalformedSMTPReplyException.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/sandbox/mail/src/java/org/apache/geronimo/mail/smtp/MalformedSMTPReplyException.java?view=auto&rev=158693
==============================================================================
--- geronimo/trunk/sandbox/mail/src/java/org/apache/geronimo/mail/smtp/MalformedSMTPReplyException.java
(added)
+++ geronimo/trunk/sandbox/mail/src/java/org/apache/geronimo/mail/smtp/MalformedSMTPReplyException.java
Tue Mar 22 14:53:53 2005
@@ -0,0 +1,38 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.geronimo.mail.smtp;
+
+/**
+ *  Exception for when a SMTP reply string has a problem
+ * 
+ * @version $Id: $
+ */
+class MalformedSMTPReplyException  extends Exception {
+
+    MalformedSMTPReplyException() {
+        super();
+    }
+
+    MalformedSMTPReplyException(String msg) {
+        super(msg);
+    }
+
+    MalformedSMTPReplyException(String msg, Throwable t) {
+        super(msg, t);
+    }
+}

Added: geronimo/trunk/sandbox/mail/src/java/org/apache/geronimo/mail/smtp/SMTPReply.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/sandbox/mail/src/java/org/apache/geronimo/mail/smtp/SMTPReply.java?view=auto&rev=158693
==============================================================================
--- geronimo/trunk/sandbox/mail/src/java/org/apache/geronimo/mail/smtp/SMTPReply.java (added)
+++ geronimo/trunk/sandbox/mail/src/java/org/apache/geronimo/mail/smtp/SMTPReply.java Tue
Mar 22 14:53:53 2005
@@ -0,0 +1,85 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.geronimo.mail.smtp;
+
+/**
+ * Util class to represent a reply from a SMTP server
+ *
+ * @version $Id: $
+ */
+class SMTPReply {
+
+    private final int code;
+    private final String message;
+    private final char firstChar;
+
+    SMTPReply(String s)
+        throws MalformedSMTPReplyException{
+
+        /*
+         * first three must be the return code
+         */
+
+        if (s == null || s.length() < 3) {
+            throw new MalformedSMTPReplyException("Too Short! : " + s);
+        }
+
+        try {
+            firstChar = s.charAt(0);
+            code = Integer.parseInt(s.substring(0, 3));
+
+            /*
+             * message should be separated by a space
+             */
+            if (s.length() > 4) {
+                message = s.substring(4);
+            }
+            else {
+                message = "";
+            }
+        }
+        catch (NumberFormatException e) {
+            throw new MalformedSMTPReplyException("error in parsing code", e);
+        }
+    }
+
+    int getCode() {
+        return this.code;
+    }
+
+    String getMessage() {
+        return this.message;
+    }
+
+    /**
+     *  Indicates if reply is an error condition
+     *
+     * @return
+     */
+    boolean isError() {
+        if (firstChar == '5' || firstChar == '4') {
+            return true;
+        }
+
+        return false;
+    }
+
+    public String toString() {
+        return "CODE = " + getCode() + " : MSG = " + getMessage();
+    }
+}

Modified: geronimo/trunk/sandbox/mail/src/java/org/apache/geronimo/mail/smtp/SMTPTransport.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/sandbox/mail/src/java/org/apache/geronimo/mail/smtp/SMTPTransport.java?view=diff&r1=158692&r2=158693
==============================================================================
--- geronimo/trunk/sandbox/mail/src/java/org/apache/geronimo/mail/smtp/SMTPTransport.java
(original)
+++ geronimo/trunk/sandbox/mail/src/java/org/apache/geronimo/mail/smtp/SMTPTransport.java
Tue Mar 22 14:53:53 2005
@@ -1,6 +1,6 @@
 /**
  *
- * Copyright 2003-2004 The Apache Software Foundation
+ * Copyright 2003-2005 The Apache Software Foundation
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -22,21 +22,69 @@
 import javax.mail.MessagingException;
 import javax.mail.Session;
 import javax.mail.URLName;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMultipart;
+import javax.mail.internet.MimeBodyPart;
 
 import org.apache.geronimo.mail.AbstractTransport;
 
+import java.net.Socket;
+import java.net.InetSocketAddress;
+import java.net.SocketException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Properties;
+import java.util.Date;
+
 /**
+ *  Simple implementation of SMTP transport.  Just does plain RFC821-ish
+ *  delivery.
+ *
+ *  Supported properties :
+ *
+ *  <ul>
+ *  <li> mail.host : to set the server to deliver to.  Default = localhost</li>
+ *  <li> mail.smtp.port : to set the port.  Default = 25</li>
+ *  <li> mail.smtp.locahost : name to use for HELO/EHLO - default getHostName()</li>
+ *  </ul>
+ *
+ *  There is no way to indicate failure for a given recipient (it's possible to have a
+ *  recipient address rejected).  The sun impl throws exceptions even if others successful),
+ *  but maybe we do a different way...
+ *
+ *  TODO : lots.  ESMTP, user/pass, indicate failure, etc...
+ *
  * @version $Rev$ $Date$
  */
 public class SMTPTransport extends AbstractTransport {
 
     /**
+     *  constants for EOL termination
+     */
+    private static final char CR = 0x0D;
+    private static final char LF = 0x0A;
+
+    /**
+     *  property key for SMTP server to talk to
+     */
+    private static final String MAIL_HOST = "mail.host";
+    private static final String MAIL_SMTP_LOCALHOST = "mail.smtp.localhost";
+    private static final String MAIL_SMTP_PORT = "mail.smtp.port";
+
+    private static final int MIN_MILLIS = 1000 * 60;
+    private static final String DEFAULT_MAIL_HOST = "localhost";
+    private static final int DEFAULT_MAIL_SMTP_PORT = 25;
+
+    /**
      * @param session
      * @param name
      */
     public SMTPTransport(Session session, URLName name) {
         super(session, name);
-        // TODO Auto-generated constructor stub
     }
 
     /* (non-Javadoc)
@@ -44,8 +92,536 @@
      */
     public void sendMessage(Message message, Address[] addresses)
         throws MessagingException {
-        // TODO Auto-generated method stub
 
+        /*
+         * do it and ignore the return
+         */
+        sendMessage(addresses, message);
+    }
+
+    public SendStatus[] sendMessage(Address[] addresses, Message message)
+        throws MessagingException {
+
+        /*
+         * don't bother me w/ null messages or no addreses
+         */
+
+        if (message == null ) {
+            throw new MessagingException("Null message");
+        }
+
+        if (addresses == null || addresses.length == 0) {
+            throw new MessagingException("Null or empty address array");
+        }
+
+
+        SendStatus[] stat = new SendStatus[addresses.length];
+
+        try {
+
+            /*
+             * create socket and connect to server.
+             */
+
+            Socket s = getConnectedSocket();
+
+            /*
+             *  receive welcoming message
+             */
+            if (!getWelcome(s)) {
+                throw new MessagingException("Error in getting welcome msg");
+            }
+
+            /*
+             * say hello
+             */
+            if (!sendHelo(s)) {
+                throw new MessagingException("Error in saying HELO to server");
+            }
+
+            /*
+             * send sender
+             */
+            if (!sendMailFrom(s, message.getFrom())) {
+                throw new MessagingException("Error in setting the MAIL FROM");
+            }
+
+            /*
+             * send recipients.  Only send if not null or "", and just ignore
+             * (but log) any errors
+             */
+
+            for (int i=0; i < addresses.length; i++) {
+                String to = addresses[i].toString();
+
+                int status = SendStatus.SUCCESS;
+
+                if (to != null && !"".equals(to)) {
+                    if (!sendRcptTo(s, to)) {
+
+                        // this means it didn't like our recipient.  I say we keep
+                        // going
+
+                        if (this.session.getDebug()) {
+                            this.session.getDebugOut().println("ERROR setting recipient "
+ to);
+                        }
+
+                        status = SendStatus.FAIL;
+                    }
+                }
+                else {
+                    status = SendStatus.FAIL;
+                }
+
+                stat[i] = new SendStatus(status, to);
+            }
+
+            /*
+             * send data
+             */
+            if (!sendData(s, message)) {
+                throw new MessagingException("Error sending data");
+            }
+
+            /*
+             * say goodbye
+             */
+
+            sendQuit(s);
+
+            try {
+                s.close();
+            }
+            catch (IOException e) {
+                //
+                // TODO - should we just eat this?  We have delivered the msg...
+                //
+                e.printStackTrace();
+            }
+        }
+        catch (SMTPTransportException e) {
+            throw new MessagingException("error", e);
+        }
+        catch (MalformedSMTPReplyException e) {
+            throw new MessagingException("error", e);
+        }
+
+        return stat;
     }
 
+    /**
+     * Sends the data in the message down the socket.  This presumes the
+     * server is in the right place and ready for getting the DATA message
+     * and the data right place in the sequence
+     *
+     * @param s
+     * @param msg
+     * @return
+     * @throws SMTPTransportException
+     * @throws MalformedSMTPReplyException
+     */
+    protected boolean sendData(Socket s, Message msg)
+        throws SMTPTransportException, MalformedSMTPReplyException {
+
+        if (msg == null) {
+            throw new SMTPTransportException("invalid message");
+        }
+
+        /*
+         * send the DATA command
+         */
+        sendLine(s, "DATA");
+
+        SMTPReply line = new SMTPReply(receiveLine(s, 5 * MIN_MILLIS));
+
+        if (this.session.getDebug()) {
+            this.session.getDebugOut().println(line);
+        }
+
+        if (line.isError()) {
+            return false;
+        }
+
+        /*
+         * now the data...  I could look at the type, but
+         */
+
+        try {
+            OutputStream os = s.getOutputStream();
+
+            // todo - be smarter here and send in chunks to let the other side
+            // digest easier.  There's a 3 min recommended timeout per chunk...
+
+            msg.writeTo(os);
+            os.flush();
+        }
+        catch (IOException e) {
+            throw new SMTPTransportException(e);
+        }
+        catch(MessagingException e) {
+            throw new SMTPTransportException(e);
+        }
+
+        /*
+         * now to finish
+         */
+        sendLine(s, "");
+        sendLine(s, ".");
+
+        line = new SMTPReply(receiveLine(s, 10 * MIN_MILLIS));
+
+        return !line.isError();
+    }
+
+    /**
+     *  Sends the QUIT message and receieves the response
+     *
+     * @param s
+     * @return
+     * @throws SMTPTransportException
+     * @throws MalformedSMTPReplyException
+     */
+    protected boolean sendQuit(Socket s)
+        throws SMTPTransportException, MalformedSMTPReplyException {
+
+        sendLine(s, "QUIT");
+
+        SMTPReply line = new SMTPReply(receiveLine(s, 5 * MIN_MILLIS));
+
+        return !line.isError();
+    }
+
+    /**
+     * Sets a receiver address for the current message
+     *
+     * @param s
+     * @param addr
+     * @return
+     * @throws SMTPTransportException
+     * @throws MalformedSMTPReplyException
+     */
+    protected boolean sendRcptTo(Socket s, String addr)
+        throws SMTPTransportException, MalformedSMTPReplyException {
+
+        if (addr == null || "".equals(addr)) {
+            throw new SMTPTransportException("invalid address");
+        }
+
+        String msg = "RCPT TO: " + addr;
+
+        sendLine(s, msg);
+
+        SMTPReply line = new SMTPReply(receiveLine(s, 5 * MIN_MILLIS));
+
+        return !line.isError();
+    }
+
+    /**
+     * Set the sender for this mail.
+     * @param s
+     * @param from
+     * @return
+     * @throws SMTPTransportException
+     * @throws MalformedSMTPReplyException
+     */
+    protected boolean sendMailFrom(Socket s, Address[] from)
+            throws SMTPTransportException, MalformedSMTPReplyException {
+
+        if (from == null || from.length == 0) {
+            throw new SMTPTransportException("no FROM address");
+        }
+
+        /*
+         * TODO - what do we do w/ more than one from???
+         */
+
+        String msg = "MAIL FROM: " + from[0].toString();
+
+        sendLine(s, msg);
+
+        SMTPReply line = new SMTPReply(receiveLine(s, 5 * MIN_MILLIS));
+
+        return !line.isError();
+    }
+
+    /**
+     *  Sends the initiating "HELO" message.  We're keeping it simple, just
+     * identifying ourselves as we dont' require service extensions, and
+     *  want to keep it simple for now
+     *
+     * @param s socket we are talking on.  It's assumed to be open and in
+     *    right state for this message
+     * @return
+     * @throws SMTPTransportException
+     * @throws MalformedSMTPReplyException
+     */
+    protected boolean sendHelo(Socket s)
+        throws SMTPTransportException, MalformedSMTPReplyException {
+
+        String fqdm = null;
+
+        try {
+            fqdm = InetAddress.getLocalHost().getHostName();
+        }
+        catch (UnknownHostException e) {
+            // fine, we're misconfigured - ignore
+        }
+
+        if (fqdm == null) {
+            fqdm = session.getProperty(MAIL_SMTP_LOCALHOST);
+        }
+
+        if (fqdm == null) {
+            throw new SMTPTransportException("Can't get local hostname. " +
+                    " Please correctly configure JDK/DNS or set mail.smtp.localhost");
+        }
+
+        sendLine(s, "HELO " + fqdm);
+
+        SMTPReply line = new SMTPReply(receiveLine(s, 5 * MIN_MILLIS));
+
+        return !line.isError();
+    }
+
+    /**
+     *  Get the servers welcome blob from the wire....
+     *
+     * @param s
+     * @return
+     * @throws SMTPTransportException
+     * @throws MalformedSMTPReplyException
+     */
+    protected boolean getWelcome(Socket s)
+        throws SMTPTransportException, MalformedSMTPReplyException {
+
+        SMTPReply line = new SMTPReply(receiveLine(s, 5 * MIN_MILLIS));
+
+        return !line.isError();
+    }
+
+    /**
+     *  Sends a  message down the socket and terminates with the
+     *  appropriate CRLF
+     *
+     * @param s
+     * @param data
+     * @throws SMTPTransportException
+     */
+    protected void sendLine(Socket s, String data)
+        throws SMTPTransportException {
+
+        if (s == null) {
+            throw new SMTPTransportException("bonehead...");
+        }
+
+        if (!s.isConnected()) {
+            throw new SMTPTransportException("no connection");
+        }
+
+        try {
+            OutputStream out = s.getOutputStream();
+
+            out.write(data.getBytes());
+            out.write(CR);
+            out.write(LF);
+            out.flush();
+
+            if (this.session.getDebug()) {
+                this.session.getDebugOut().println("sent: " + data);
+            }
+        }
+        catch (IOException e) {
+            throw new SMTPTransportException(e);
+        }
+    }
+
+    /**
+     *  Receives one line from the server.  A line is a sequence of bytes
+     *  terminated by a CRLF
+     *
+     * @param s socket to receive from
+     * @return the line from the server as String
+     */
+    protected String receiveLine(Socket s, int delayMillis)
+        throws SMTPTransportException {
+
+        if (s == null) {
+            throw new SMTPTransportException("bonehead...");
+        }
+
+        if (!s.isConnected()) {
+            throw new SMTPTransportException("no connection");
+        }
+
+        int timeout = 0;
+
+        try {
+
+            /*
+             * for now, read byte for byte, looking for a CRLF
+             */
+
+            timeout = s.getSoTimeout();
+
+            s.setSoTimeout(delayMillis);
+
+            InputStream is = s.getInputStream();
+
+            StringBuffer buff = new StringBuffer();
+            
+            int c;
+            boolean crFound = false, lfFound = false;
+
+            while((c = is.read()) != -1  && crFound == false && lfFound ==
false) {
+                buff.append((char) c);
+
+                if (c == CR) {
+                    crFound = true;
+                }
+                if (c == LF) {
+                    lfFound = true;
+                }
+            }
+
+            if (this.session.getDebug()) {
+                this.session.getDebugOut().println("received : " + buff.toString());
+            }
+
+            return buff.toString();
+        }
+        catch (SocketException e) {
+            throw new SMTPTransportException(e);
+        }
+        catch (IOException e) {
+            throw new SMTPTransportException(e);
+        }
+        finally {
+            try {
+                s.setSoTimeout(timeout);
+            }
+            catch(SocketException e) {
+                // ignore - was just trying to do the decent thing...
+            }
+        }
+    }
+
+
+    /**
+     *  Creates and returns a connected socket
+     *
+     * @return
+     * @throws MessagingException
+     */
+    protected Socket getConnectedSocket()
+        throws MessagingException {
+
+        Socket s = new Socket();
+
+        String mail_host = this.session.getProperty(MAIL_HOST);
+
+        if (mail_host == null || "".equals(mail_host)) {
+            mail_host = DEFAULT_MAIL_HOST;
+        }
+
+        String portString = this.session.getProperty(MAIL_SMTP_PORT);
+
+        int port = DEFAULT_MAIL_SMTP_PORT;
+
+        if (portString != null && !"".equals(portString)) {
+
+            try {
+                port = Integer.parseInt(portString);
+            }
+            catch (NumberFormatException e) {
+                // ignore - we don't care, leave as default
+            }
+        }
+
+        try {
+
+            if (this.session.getDebug()) {
+                this.session.getDebugOut().println("connecting to " + mail_host);
+            }
+
+            s.connect(new InetSocketAddress(mail_host, port));
+
+            if (this.session.getDebug()) {
+                this.session.getDebugOut().println("connected to " + mail_host);
+            }
+        }
+        catch (IOException e) {
+
+            if (this.session.getDebug()) {
+                this.session.getDebugOut().println("error connecting to " + mail_host);
+            }
+
+            throw new MessagingException("Error connecting to " + mail_host, e);
+        }
+
+        return s;
+    }
+
+    public static void main(String[] args)
+        throws Exception {
+
+        Properties props = new Properties();
+        props.setProperty("mail.host", "mail.optonline.net");
+
+        Session session = Session.getDefaultInstance(props);
+
+        session.setDebug(true);
+
+        SMTPTransport t = new SMTPTransport(session, null);
+
+        MimeMessage msg = new MimeMessage(session);
+
+        MimeMultipart mmpt = new MimeMultipart();
+        MimeBodyPart b1 = new MimeBodyPart();
+        b1.setContent("body 1", "text/plain");
+
+        mmpt.addBodyPart(b1);
+
+        b1 = new MimeBodyPart();
+        b1.setContent("body2", "text/plain");
+        mmpt.addBodyPart(b1);
+
+        Address[] list = new Address[1];
+        list[0] =  new InternetAddress("geirm@optonline.net");
+//        list[1] =  new InternetAddress("jeremy@boynes.com");
+
+        msg.setSubject("From SMTPTransport : this is the subject");
+        msg.setFrom(new InternetAddress("geirm@optonline.net"));
+//        msg.setContent("This is the content (via SMTPTransport)", "text/plain");
+        msg.setRecipients(Message.RecipientType.TO, list);
+        msg.setContent(mmpt);
+
+        msg.setSentDate(new Date());
+
+        t.sendMessage(msg, list);
+    }
+
+    /**
+     *  Simple holder class for the address/send status duple, as we can
+     *  have mixed success for a set of addresses and a message
+     *
+     */
+    public class SendStatus {
+        public final static int SUCCESS = 0;
+        public final static int FAIL = 1;
+        final int status;
+        final String address;
+
+        public SendStatus(int s, String a) {
+            this.status = s;
+            this.address = a;
+        }
+
+        public int getStatus() {
+            return this.status;
+        }
+
+        public String getAddress() {
+            return this.address;
+        }
+    }
 }

Added: geronimo/trunk/sandbox/mail/src/java/org/apache/geronimo/mail/smtp/SMTPTransportException.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/sandbox/mail/src/java/org/apache/geronimo/mail/smtp/SMTPTransportException.java?view=auto&rev=158693
==============================================================================
--- geronimo/trunk/sandbox/mail/src/java/org/apache/geronimo/mail/smtp/SMTPTransportException.java
(added)
+++ geronimo/trunk/sandbox/mail/src/java/org/apache/geronimo/mail/smtp/SMTPTransportException.java
Tue Mar 22 14:53:53 2005
@@ -0,0 +1,42 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.geronimo.mail.smtp;
+
+/**
+ *  General purpose Exception
+ *
+ * @version $Id: $
+ */
+class SMTPTransportException extends Exception {
+
+    SMTPTransportException() {
+        super();
+    }
+
+    SMTPTransportException(String s) {
+        super(s);
+    }
+
+    SMTPTransportException(String s, Throwable t) {
+        super(s,t);
+    }
+
+    SMTPTransportException(Throwable t) {
+        super(t);
+    }
+}



Mime
View raw message