1 /*
   2  * Copyright (c) 1995, 2011, 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     String mailhost;
  47     SmtpPrintStream message;
  48 
  49     /**
  50      * issue the QUIT command to the SMTP server and close the connection.
  51      */
  52     public void closeServer() throws IOException {
  53         if (serverIsOpen()) {
  54             closeMessage();
  55             issueCommand("QUIT\r\n", 221);
  56             super.closeServer();
  57         }
  58     }
  59 
  60     void issueCommand(String cmd, int expect) throws IOException {
  61         sendServer(cmd);
  62         int reply;
  63         while ((reply = readServerResponse()) != expect)
  64             if (reply != 220) {
  65                 throw new SmtpProtocolException(getResponseString());
  66             }
  67     }
  68 
  69     private void toCanonical(String s) throws IOException {
  70         if (s.startsWith("<"))
  71             issueCommand("rcpt to: " + s + "\r\n", 250);
  72         else
  73             issueCommand("rcpt to: <" + s + ">\r\n", 250);
  74     }
  75 
  76     public void to(String s) throws IOException {
  77         int st = 0;
  78         int limit = s.length();
  79         int pos = 0;
  80         int lastnonsp = 0;
  81         int parendepth = 0;
  82         boolean ignore = false;
  83         while (pos < limit) {
  84             int c = s.charAt(pos);
  85             if (parendepth > 0) {
  86                 if (c == '(')
  87                     parendepth++;
  88                 else if (c == ')')
  89                     parendepth--;
  90                 if (parendepth == 0)
  91                     if (lastnonsp > st)
  92                         ignore = true;
  93                     else
  94                         st = pos + 1;
  95             } else if (c == '(')
  96                 parendepth++;
  97             else if (c == '<')
  98                 st = lastnonsp = pos + 1;
  99             else if (c == '>')
 100                 ignore = true;
 101             else if (c == ',') {
 102                 if (lastnonsp > st)
 103                     toCanonical(s.substring(st, lastnonsp));
 104                 st = pos + 1;
 105                 ignore = false;
 106             } else {
 107                 if (c > ' ' && !ignore)
 108                     lastnonsp = pos + 1;
 109                 else if (st == pos)
 110                     st++;
 111             }
 112             pos++;
 113         }
 114         if (lastnonsp > st)
 115             toCanonical(s.substring(st, lastnonsp));
 116     }
 117 
 118     public void from(String s) throws IOException {
 119         if (s.startsWith("<"))
 120             issueCommand("mail from: " + s + "\r\n", 250);
 121         else
 122             issueCommand("mail from: <" + s + ">\r\n", 250);
 123     }
 124 
 125     /** open a SMTP connection to host <i>host</i>. */
 126     private void openServer(String host) throws IOException {
 127         mailhost = host;
 128         openServer(mailhost, 25);
 129         issueCommand("helo "+InetAddress.getLocalHost().getHostName()+"\r\n", 250);
 130     }
 131 
 132     public PrintStream startMessage() throws IOException {
 133         issueCommand("data\r\n", 354);
 134         try {
 135             message = new SmtpPrintStream(serverOutput, this);
 136         } catch (UnsupportedEncodingException e) {
 137             throw new InternalError(encoding+" encoding not found", e);
 138         }
 139         return message;
 140     }
 141 
 142     void closeMessage() throws IOException {
 143         if (message != null)
 144             message.close();
 145     }
 146 
 147     /** New SMTP client connected to host <i>host</i>. */
 148     public SmtpClient (String host) throws IOException {
 149         super();
 150         if (host != null) {
 151             try {
 152                 openServer(host);
 153                 mailhost = host;
 154                 return;
 155             } catch(Exception e) {
 156             }
 157         }
 158         try {
 159             String s;
 160             mailhost = GetPropertyAction.getProperty("mail.host");
 161             if (mailhost != null) {
 162                 openServer(mailhost);
 163                 return;
 164             }
 165         } catch(Exception e) {
 166         }
 167         try {
 168             mailhost = "localhost";
 169             openServer(mailhost);
 170         } catch(Exception e) {
 171             mailhost = "mailhost";
 172             openServer(mailhost);
 173         }
 174     }
 175 
 176     /** Create an uninitialized SMTP client. */
 177     public SmtpClient () throws IOException {
 178         this(null);
 179     }
 180 
 181     public SmtpClient(int to) throws IOException {
 182         super();
 183         setConnectTimeout(to);
 184         try {
 185             String s;
 186             mailhost = GetPropertyAction.getProperty("mail.host");
 187             if (mailhost != null) {
 188                 openServer(mailhost);
 189                 return;
 190             }
 191         } catch(Exception e) {
 192         }
 193         try {
 194             mailhost = "localhost";
 195             openServer(mailhost);
 196         } catch(Exception e) {
 197             mailhost = "mailhost";
 198             openServer(mailhost);
 199         }
 200     }
 201 
 202     public String getMailHost() {
 203         return mailhost;
 204     }
 205 
 206     String getEncoding () {
 207         return encoding;
 208     }
 209 }
 210 
 211 class SmtpPrintStream extends java.io.PrintStream {
 212     private SmtpClient target;
 213     private int lastc = '\n';
 214 
 215     SmtpPrintStream (OutputStream fos, SmtpClient cl) throws UnsupportedEncodingException {
 216         super(fos, false, cl.getEncoding());
 217         target = cl;
 218     }
 219 
 220     public void close() {
 221         if (target == null)
 222             return;
 223         if (lastc != '\n') {
 224             write('\n');
 225         }
 226         try {
 227             target.issueCommand(".\r\n", 250);
 228             target.message = null;
 229             out = null;
 230             target = null;
 231         } catch (IOException e) {
 232         }
 233     }
 234 
 235     public void write(int b) {
 236         try {
 237             // quote a dot at the beginning of a line
 238             if (lastc == '\n' && b == '.') {
 239                 out.write('.');
 240             }
 241 
 242             // translate NL to CRLF
 243             if (b == '\n' && lastc != '\r') {
 244                 out.write('\r');
 245             }
 246             out.write(b);
 247             lastc = b;
 248         } catch (IOException e) {
 249         }
 250     }
 251 
 252     public void write(byte b[], int off, int len) {
 253         try {
 254             int lc = lastc;
 255             while (--len >= 0) {
 256                 int c = b[off++];
 257 
 258                 // quote a dot at the beginning of a line
 259                 if (lc == '\n' && c == '.')
 260                     out.write('.');
 261 
 262                 // translate NL to CRLF
 263                 if (c == '\n' && lc != '\r') {
 264                     out.write('\r');
 265                 }
 266                 out.write(c);
 267                 lc = c;
 268             }
 269             lastc = lc;
 270         } catch (IOException e) {
 271         }
 272     }
 273     public void print(String s) {
 274         int len = s.length();
 275         for (int i = 0; i < len; i++) {
 276             write(s.charAt(i));
 277         }
 278     }
 279 }