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.util.StringTokenizer;
29 import java.io.*;
30 import java.net.*;
31 import sun.net.TransferProtocolClient;
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 = java.security.AccessController.doPrivileged(
171                     new sun.security.action.GetPropertyAction("mail.host"));
172             if (mailhost != null) {
173                 openServer(mailhost);
174                 return;
175             }
176         } catch(Exception e) {
177         }
178         try {
179             mailhost = "localhost";
180             openServer(mailhost);
181         } catch(Exception e) {
182             mailhost = "mailhost";
183             openServer(mailhost);
184         }
185     }
186 
187     /** Create an uninitialized SMTP client. */
SmtpClient()188     public SmtpClient () throws IOException {
189         this(null);
190     }
191 
SmtpClient(int to)192     public SmtpClient(int to) throws IOException {
193         super();
194         setConnectTimeout(to);
195         try {
196             String s;
197             mailhost = java.security.AccessController.doPrivileged(
198                     new sun.security.action.GetPropertyAction("mail.host"));
199             if (mailhost != null) {
200                 openServer(mailhost);
201                 return;
202             }
203         } catch(Exception e) {
204         }
205         try {
206             mailhost = "localhost";
207             openServer(mailhost);
208         } catch(Exception e) {
209             mailhost = "mailhost";
210             openServer(mailhost);
211         }
212     }
213 
getMailHost()214     public String getMailHost() {
215         return mailhost;
216     }
217 
getEncoding()218     String getEncoding () {
219         return encoding;
220     }
221 }
222 
223 class SmtpPrintStream extends java.io.PrintStream {
224     private SmtpClient target;
225     private int lastc = '\n';
226 
SmtpPrintStream(OutputStream fos, SmtpClient cl)227     SmtpPrintStream (OutputStream fos, SmtpClient cl) throws UnsupportedEncodingException {
228         super(fos, false, cl.getEncoding());
229         target = cl;
230     }
231 
close()232     public void close() {
233         if (target == null)
234             return;
235         if (lastc != '\n') {
236             write('\n');
237         }
238         try {
239             target.issueCommand(".\r\n", 250);
240             target.message = null;
241             out = null;
242             target = null;
243         } catch (IOException e) {
244         }
245     }
246 
write(int b)247     public void write(int b) {
248         try {
249             // quote a dot at the beginning of a line
250             if (lastc == '\n' && b == '.') {
251                 out.write('.');
252             }
253 
254             // translate NL to CRLF
255             if (b == '\n' && lastc != '\r') {
256                 out.write('\r');
257             }
258             out.write(b);
259             lastc = b;
260         } catch (IOException e) {
261         }
262     }
263 
write(byte b[], int off, int len)264     public void write(byte b[], int off, int len) {
265         try {
266             int lc = lastc;
267             while (--len >= 0) {
268                 int c = b[off++];
269 
270                 // quote a dot at the beginning of a line
271                 if (lc == '\n' && c == '.')
272                     out.write('.');
273 
274                 // translate NL to CRLF
275                 if (c == '\n' && lc != '\r') {
276                     out.write('\r');
277                 }
278                 out.write(c);
279                 lc = c;
280             }
281             lastc = lc;
282         } catch (IOException e) {
283         }
284     }
print(String s)285     public void print(String s) {
286         int len = s.length();
287         for (int i = 0; i < len; i++) {
288             write(s.charAt(i));
289         }
290     }
291 }
292