src/org/sonews/mlgw/SMTPTransport.java
author cli
Tue Sep 13 23:34:16 2011 +0200 (2011-09-13)
changeset 58 b2df305a13ce
parent 37 74139325d305
child 59 68a6825a4f4d
permissions -rwxr-xr-x
Work on SMTP authentification for ML sender
     1 /*
     2  *   SONEWS News Server
     3  *   see AUTHORS for the list of contributors
     4  *
     5  *   This program is free software: you can redistribute it and/or modify
     6  *   it under the terms of the GNU General Public License as published by
     7  *   the Free Software Foundation, either version 3 of the License, or
     8  *   (at your option) any later version.
     9  *
    10  *   This program is distributed in the hope that it will be useful,
    11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  *   GNU General Public License for more details.
    14  *
    15  *   You should have received a copy of the GNU General Public License
    16  *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  */
    18 package org.sonews.mlgw;
    19 
    20 import java.io.BufferedOutputStream;
    21 import java.io.BufferedReader;
    22 import java.io.IOException;
    23 import java.io.InputStreamReader;
    24 import java.net.Socket;
    25 import java.net.UnknownHostException;
    26 import org.sonews.config.Config;
    27 import org.sonews.storage.Article;
    28 import org.sonews.util.io.ArticleInputStream;
    29 
    30 /**
    31  * Connects to a SMTP server and sends a given Article to it.
    32  * @author Christian Lins
    33  * @since sonews/1.0
    34  */
    35 class SMTPTransport {
    36 
    37 	public static final String NEWLINE = "\r\n";
    38 
    39 	protected BufferedReader in;
    40 	protected BufferedOutputStream out;
    41 	protected Socket socket;
    42 
    43 	public SMTPTransport(String host, int port)
    44 			throws IOException, UnknownHostException {
    45 		this.socket = new Socket(host, port);
    46 		this.in = new BufferedReader(
    47 				new InputStreamReader(socket.getInputStream()));
    48 		this.out = new BufferedOutputStream(socket.getOutputStream());
    49 
    50 		// Read HELO from server
    51 		String line = this.in.readLine();
    52 		if (line == null || !line.startsWith("220 ")) {
    53 			throw new IOException("Invalid HELO from server: " + line);
    54 		}
    55 	}
    56 
    57 	public void close()
    58 			throws IOException {
    59 		this.out.write("QUIT".getBytes("UTF-8"));
    60 		this.out.flush();
    61 		this.in.readLine();
    62 
    63 		this.socket.close();
    64 	}
    65 
    66 	private void ehlo(String hostname) throws IOException {
    67 		StringBuilder strBuf = new StringBuilder();
    68 		strBuf.append("EHLO ");
    69 		strBuf.append(hostname);
    70 		strBuf.append(NEWLINE);
    71 
    72 		// Send EHLO to server
    73 		this.out.write(strBuf.toString().getBytes("UTF-8"));
    74 		this.out.flush();
    75 
    76 		// Read reply: "250-example.org Hello example.net"
    77 		String line = this.in.readLine();
    78 		if (line == null || !line.startsWith("250")) {
    79 			throw new IOException("Unexpected reply: " + line);
    80 		}
    81 
    82 		// FIXME: More 250- lines possible!
    83 
    84 		// Read reply: "250 AUTH CRAM-MD5 LOGIN PLAIN"
    85 		line = this.in.readLine();
    86 		if (line == null || !line.startsWith("250 ")) {
    87 			throw new IOException("Unexpected reply: " + line);
    88 		}
    89 		String[] authMethods = line.split(" ");
    90 		// TODO: Check for supported methods
    91 
    92 		// Do a PLAIN login
    93 		strBuf = new StringBuilder();
    94 		strBuf.append("AUTH PLAIN");
    95 		strBuf.append(NEWLINE);
    96 
    97 		// Send AUTH to server
    98 		this.out.write(strBuf.toString().getBytes("UTF-8"));
    99 		this.out.flush();
   100 
   101 		// Read reply
   102 		line = this.in.readLine();
   103 		if (line == null || !line.startsWith("250 ")) {
   104 			throw new IOException("Unexpected reply: " + line);
   105 		}
   106 	}
   107 
   108 	private void helo(String hostname) throws IOException {
   109 		StringBuilder heloStr = new StringBuilder();
   110 		heloStr.append("HELO ");
   111 		heloStr.append(hostname);
   112 		heloStr.append(NEWLINE);
   113 
   114 		// Send HELO to server
   115 		this.out.write(heloStr.toString().getBytes("UTF-8"));
   116 		this.out.flush();
   117 
   118 		// Read reply
   119 		String line = this.in.readLine();
   120 		if (line == null || !line.startsWith("250 ")) {
   121 			throw new IOException("Unexpected reply: " + line);
   122 		}
   123 	}
   124 
   125 	public void login() throws IOException {
   126 		String hostname = Config.inst().get(Config.HOSTNAME, "localhost");
   127 		String auth = Config.inst().get(Config.MLSEND_AUTH, "none");
   128 		if(auth.equals("none")) {
   129 			helo(hostname);
   130 		} else {
   131 			ehlo(hostname);
   132 		}
   133 	}
   134 
   135 	public void send(Article article, String mailFrom, String rcptTo)
   136 			throws IOException {
   137 		assert (article != null);
   138 		assert (mailFrom != null);
   139 		assert (rcptTo != null);
   140 
   141 		this.out.write(("MAIL FROM: " + mailFrom).getBytes("UTF-8"));
   142 		this.out.flush();
   143 		String line = this.in.readLine();
   144 		if (line == null || !line.startsWith("250 ")) {
   145 			throw new IOException("Unexpected reply: " + line);
   146 		}
   147 
   148 		this.out.write(("RCPT TO: " + rcptTo).getBytes("UTF-8"));
   149 		this.out.flush();
   150 		line = this.in.readLine();
   151 		if (line == null || !line.startsWith("250 ")) {
   152 			throw new IOException("Unexpected reply: " + line);
   153 		}
   154 
   155 		this.out.write("DATA".getBytes("UTF-8"));
   156 		this.out.flush();
   157 		line = this.in.readLine();
   158 		if (line == null || !line.startsWith("354 ")) {
   159 			throw new IOException("Unexpected reply: " + line);
   160 		}
   161 
   162 		ArticleInputStream artStream = new ArticleInputStream(article);
   163 		for (int b = artStream.read(); b >= 0; b = artStream.read()) {
   164 			this.out.write(b);
   165 		}
   166 
   167 		// Flush the binary stream; important because otherwise the output
   168 		// will be mixed with the PrintWriter.
   169 		this.out.flush();
   170 		this.out.write("\r\n.\r\n".getBytes("UTF-8"));
   171 		this.out.flush();
   172 		line = this.in.readLine();
   173 		if (line == null || !line.startsWith("250 ")) {
   174 			throw new IOException("Unexpected reply: " + line);
   175 		}
   176 	}
   177 }