001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.mail.smtp;
031:
032: import com.caucho.server.util.CauchoSystem;
033: import com.caucho.util.L10N;
034: import com.caucho.vfs.ReadStream;
035: import com.caucho.vfs.SocketStream;
036: import com.caucho.vfs.WriteStream;
037:
038: import javax.mail.Address;
039: import javax.mail.Message;
040: import javax.mail.MessagingException;
041: import javax.mail.Session;
042: import javax.mail.Transport;
043: import javax.mail.URLName;
044: import javax.mail.internet.InternetAddress;
045: import javax.mail.internet.MimeMessage;
046: import java.io.IOException;
047: import java.io.OutputStream;
048: import java.net.Socket;
049: import java.util.logging.Level;
050: import java.util.logging.Logger;
051:
052: /**
053: * Resin's SMTP transport implementation.
054: */
055: public class SmtpTransport extends Transport {
056: private static final L10N L = new L10N(SmtpTransport.class);
057: private static final Logger log = Logger
058: .getLogger(SmtpTransport.class.getName());
059:
060: private Socket _socket;
061:
062: private ReadStream _is;
063: private WriteStream _os;
064:
065: public SmtpTransport(Session session, URLName urlName) {
066: super (session, urlName);
067: }
068:
069: /**
070: * Connect for the protocol.
071: */
072: protected boolean protocolConnect(String host, int port,
073: String user, String password) throws MessagingException {
074: if (host == null)
075: host = "localhost";
076:
077: if (port < 0)
078: port = 25;
079:
080: // XXX: pooling
081: if (_socket != null)
082: throw new MessagingException(L
083: .l("Attempted to connect to open connection."));
084:
085: try {
086: _socket = new Socket(host, port);
087: _socket.setSoTimeout(10000);
088: SocketStream s = new SocketStream(_socket);
089:
090: _os = new WriteStream(s);
091: _is = new ReadStream(s, _os);
092:
093: String line = _is.readLine();
094:
095: log.fine("smtp connection to " + host + ":" + port
096: + " succeeded");
097: log.fine("smtp: " + line);
098:
099: _os.print("EHLO " + CauchoSystem.getLocalHost() + "\r\n");
100: _os.flush();
101:
102: readResponse();
103:
104: setConnected(true);
105: } catch (IOException e) {
106: log.fine("smtp connection to " + host + ":" + port
107: + " failed: " + e);
108:
109: log.log(Level.FINER, e.toString(), e);
110:
111: throw new MessagingException("smtp connection to " + host
112: + ":" + port + " failed.\n" + e);
113: }
114:
115: return true;
116: }
117:
118: /**
119: * Sends a message to the specified recipients.
120: *
121: * @param msg the message to send
122: * @param addresses the destination addresses
123: */
124: public void sendMessage(Message msg, Address[] addresses)
125: throws MessagingException {
126: if (!isConnected())
127: throw new MessagingException(
128: "Transport does not have an active connection.");
129:
130: if (!(msg instanceof MimeMessage))
131: throw new MessagingException(
132: "message must be a MimeMessage at '"
133: + msg.getClass().getName() + "'");
134:
135: MimeMessage mimeMsg = (MimeMessage) msg;
136:
137: try {
138: // XXX: EHLO to resync? or RSET?
139: // XXX: FROM
140:
141: String[] fromList = mimeMsg.getHeader("From");
142: String from;
143:
144: if (fromList == null || fromList.length < 1) {
145: // XXX: possible should have a default
146: throw new MessagingException(
147: "message should have a sender");
148: } else
149: from = fromList[0];
150:
151: _os.print("MAIL FROM:<" + from + ">\r\n");
152: _os.flush();
153:
154: if (log.isLoggable(Level.FINER))
155: log.finer("mail from:<" + from + ">");
156:
157: readResponse();
158:
159: for (int i = 0; i < addresses.length; i++) {
160: InternetAddress addr = (InternetAddress) addresses[i];
161:
162: if (log.isLoggable(Level.FINER))
163: log.finer("mail to:<" + addr.getAddress() + ">");
164:
165: _os.print("RCPT TO:<" + addr.getAddress() + ">\r\n");
166: _os.flush();
167:
168: readResponse();
169: }
170:
171: _os.print("DATA\r\n");
172: _os.flush();
173:
174: String line = _is.readLine();
175: if (!line.startsWith("354 "))
176: throw new MessagingException("Data not accepted: "
177: + line);
178:
179: mimeMsg.writeTo(new DataFilter(_os));
180:
181: _os.print("\r\n.\r\n");
182: _os.flush();
183: } catch (IOException e) {
184: log.log(Level.FINER, e.toString(), e);
185:
186: throw new MessagingException(e.toString());
187: }
188: }
189:
190: private int readResponse() throws IOException, MessagingException {
191: while (true) {
192: String line = _is.readLine();
193: if (line.length() < 4)
194: throw new MessagingException(line);
195:
196: int status = 0;
197: for (int i = 0; i < 3; i++) {
198: char ch;
199:
200: if ('0' <= (ch = line.charAt(i)) && ch <= '9')
201: status = 10 * status + ch - '0';
202: }
203:
204: if ((status / 100) % 10 != 2)
205: throw new MessagingException(line);
206:
207: if (line.charAt(3) != '-')
208: return status;
209: }
210: }
211:
212: /**
213: * Close connection.
214: */
215: public void close() throws MessagingException {
216: Socket socket = _socket;
217: _socket = null;
218:
219: WriteStream os = _os;
220: _os = null;
221:
222: setConnected(false);
223:
224: try {
225: if (os != null) {
226: os.print("QUIT\r\n");
227: os.flush();
228: }
229: } catch (IOException e) {
230: }
231:
232: try {
233: if (socket != null)
234: socket.close();
235: } catch (IOException e) {
236: }
237: }
238:
239: private class DataFilter extends OutputStream {
240: private OutputStream _os;
241:
242: private boolean _isCr;
243: private boolean _isLf;
244:
245: DataFilter(OutputStream os) {
246: _os = os;
247: }
248:
249: public void write(int ch) throws IOException {
250: switch (ch) {
251: case '\r':
252: _isCr = true;
253: _isLf = false;
254: break;
255: case '\n':
256: _isLf = _isCr;
257: _isCr = false;
258: break;
259: case '.':
260: if (_isLf)
261: _os.write('.');
262: _isLf = false;
263: _isCr = false;
264: break;
265: default:
266: _isLf = false;
267: _isCr = false;
268: break;
269: }
270:
271: _os.write(ch);
272: }
273: }
274: }
|