1 /*
2  * Copyright (c) 1995, 2017, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.net.smtp;
27 
28 import java.io.*;
29 import java.net.*;
30 import sun.net.TransferProtocolClient;
31 import sun.security.action.GetPropertyAction;
32 
33 /**
34  * This class implements the SMTP client.
35  * You can send a piece of mail by creating a new SmtpClient, calling
36  * the "to" method to add destinations, calling "from" to name the
37  * sender, calling startMessage to return a stream to which you write
38  * the message (with RFC733 headers) and then you finally close the Smtp
39  * Client.
40  *
41  * @author      James Gosling
42  */
43 
44 public class SmtpClient extends TransferProtocolClient {
45 
46     private static int DEFAULT_SMTP_PORT = 25;
47     String mailhost;
48     SmtpPrintStream message;
49 
50     /**
51      * issue the QUIT command to the SMTP server and close the connection.
52      */
closeServer()53     public void closeServer() throws IOException {
54         if (serverIsOpen()) {
55             closeMessage();
56             issueCommand("QUIT\r\n", 221);
57             super.closeServer();
58         }
59     }
60 
issueCommand(String cmd, int expect)61     void issueCommand(String cmd, int expect) throws IOException {
62         sendServer(cmd);
63         int reply;
64         while ((reply = readServerResponse()) != expect)
65             if (reply != 220) {
66                 throw new SmtpProtocolException(getResponseString());
67             }
68     }
69 
toCanonical(String s)70     private void toCanonical(String s) throws IOException {
71         if (s.startsWith("<"))
72             issueCommand("rcpt to: " + s + "\r\n", 250);
73         else
74             issueCommand("rcpt to: <" + s + ">\r\n", 250);
75     }
76 
to(String s)77     public void to(String s) throws IOException {
78         if (s.indexOf('\n') != -1) {
79             throw new IOException("Illegal SMTP command",
80                     new IllegalArgumentException("Illegal carriage return"));
81         }
82         int st = 0;
83         int limit = s.length();
84         int pos = 0;
85         int lastnonsp = 0;
86         int parendepth = 0;
87         boolean ignore = false;
88         while (pos < limit) {
89             int c = s.charAt(pos);
90             if (parendepth > 0) {
91                 if (c == '(')
92                     parendepth++;
93                 else if (c == ')')
94                     parendepth--;
95                 if (parendepth == 0)
96                     if (lastnonsp > st)
97                         ignore = true;
98                     else
99                         st = pos + 1;
100             } else if (c == '(')
101                 parendepth++;
102             else if (c == '<')
103                 st = lastnonsp = pos + 1;
104             else if (c == '>')
105                 ignore = true;
106             else if (c == ',') {
107                 if (lastnonsp > st)
108                     toCanonical(s.substring(st, lastnonsp));
109                 st = pos + 1;
110                 ignore = false;
111             } else {
112                 if (c > ' ' && !ignore)
113                     lastnonsp = pos + 1;
114                 else if (st == pos)
115                     st++;
116             }
117             pos++;
118         }
119         if (lastnonsp > st)
120             toCanonical(s.substring(st, lastnonsp));
121     }
122 
from(String s)123     public void from(String s) throws IOException {
124         if (s.indexOf('\n') != -1) {
125             throw new IOException("Illegal SMTP command",
126                     new IllegalArgumentException("Illegal carriage return"));
127         }
128         if (s.startsWith("<")) {
129             issueCommand("mail from: " + s + "\r\n", 250);
130         } else {
131             issueCommand("mail from: <" + s + ">\r\n", 250);
132         }
133     }
134 
135     /** open a SMTP connection to host <i>host</i>. */
openServer(String host)136     private void openServer(String host) throws IOException {
137         mailhost = host;
138         openServer(mailhost, DEFAULT_SMTP_PORT);
139         issueCommand("helo "+InetAddress.getLocalHost().getHostName()+"\r\n", 250);
140     }
141 
startMessage()142     public PrintStream startMessage() throws IOException {
143         issueCommand("data\r\n", 354);
144         try {
145             message = new SmtpPrintStream(serverOutput, this);
146         } catch (UnsupportedEncodingException e) {
147             throw new InternalError(encoding+" encoding not found", e);
148         }
149         return message;
150     }
151 
closeMessage()152     void closeMessage() throws IOException {
153         if (message != null)
154             message.close();
155     }
156 
157     /** New SMTP client connected to host <i>host</i>. */
SmtpClient(String host)158     public SmtpClient (String host) throws IOException {
159         super();
160         if (host != null) {
161             try {
162                 openServer(host);
163                 mailhost = host;
164                 return;
165             } catch(Exception e) {
166             }
167         }
168         try {
169             String s;
170             mailhost = GetPropertyAction.privilegedGetProperty("mail.host");
171             if (mailhost != null) {
172                 openServer(mailhost);
173                 return;
174             }
175         } catch(Exception e) {
176         }
177         try {
178             mailhost = "localhost";
179             openServer(mailhost);
180         } catch(Exception e) {
181             mailhost = "mailhost";
182             openServer(mailhost);
183         }
184     }
185 
186     /** Create an uninitialized SMTP client. */
SmtpClient()187     public SmtpClient () throws IOException {
188         this(null);
189     }
190 
SmtpClient(int to)191     public SmtpClient(int to) throws IOException {
192         super();
193         setConnectTimeout(to);
194         try {
195             String s;
196             mailhost = GetPropertyAction.privilegedGetProperty("mail.host");
197             if (mailhost != null) {
198                 openServer(mailhost);
199                 return;
200             }
201         } catch(Exception e) {
202         }
203         try {
204             mailhost = "localhost";
205             openServer(mailhost);
206         } catch(Exception e) {
207             mailhost = "mailhost";
208             openServer(mailhost);
209         }
210     }
211 
getMailHost()212     public String getMailHost() {
213         return mailhost;
214     }
215 
getEncoding()216     String getEncoding () {
217         return encoding;
218     }
219 }
220 
221 class SmtpPrintStream extends java.io.PrintStream {
222     private SmtpClient target;
223     private int lastc = '\n';
224 
SmtpPrintStream(OutputStream fos, SmtpClient cl)225     SmtpPrintStream (OutputStream fos, SmtpClient cl) throws UnsupportedEncodingException {
226         super(fos, false, cl.getEncoding());
227         target = cl;
228     }
229 
close()230     public void close() {
231         if (target == null)
232             return;
233         if (lastc != '\n') {
234             write('\n');
235         }
236         try {
237             target.issueCommand(".\r\n", 250);
238             target.message = null;
239             out = null;
240             target = null;
241         } catch (IOException e) {
242         }
243     }
244 
write(int b)245     public void write(int b) {
246         try {
247             // quote a dot at the beginning of a line
248             if (lastc == '\n' && b == '.') {
249                 out.write('.');
250             }
251 
252             // translate NL to CRLF
253             if (b == '\n' && lastc != '\r') {
254                 out.write('\r');
255             }
256             out.write(b);
257             lastc = b;
258         } catch (IOException e) {
259         }
260     }
261 
write(byte b[], int off, int len)262     public void write(byte b[], int off, int len) {
263         try {
264             int lc = lastc;
265             while (--len >= 0) {
266                 int c = b[off++];
267 
268                 // quote a dot at the beginning of a line
269                 if (lc == '\n' && c == '.')
270                     out.write('.');
271 
272                 // translate NL to CRLF
273                 if (c == '\n' && lc != '\r') {
274                     out.write('\r');
275                 }
276                 out.write(c);
277                 lc = c;
278             }
279             lastc = lc;
280         } catch (IOException e) {
281         }
282     }
print(String s)283     public void print(String s) {
284         int len = s.length();
285         for (int i = 0; i < len; i++) {
286             write(s.charAt(i));
287         }
288     }
289 }
290