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