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      */
  53     public void closeServer() throws IOException {
  54         if (serverIsOpen()) {
  55             closeMessage();
  56             issueCommand("QUIT\r\n", 221);
  57             super.closeServer();
  58         }
  59     }
  60 
  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 
  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 
  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 
 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>. */
 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 
 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 
 152     void closeMessage() throws IOException {
 153         if (message != null)
 154             message.close();
 155     }
 156 
 157     /** New SMTP client connected to host <i>host</i>. */
 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. */
 187     public SmtpClient () throws IOException {
 188         this(null);
 189     }
 190 
 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 
 212     public String getMailHost() {
 213         return mailhost;
 214     }
 215 
 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 
 225     SmtpPrintStream (OutputStream fos, SmtpClient cl) throws UnsupportedEncodingException {
 226         super(fos, false, cl.getEncoding());
 227         target = cl;
 228     }
 229 
 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 
 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 
 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     }
 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 }