1 package org.cacert.gigi.email;
3 import java.io.BufferedReader;
4 import java.io.IOException;
5 import java.io.InputStreamReader;
6 import java.io.PrintWriter;
7 import java.net.Socket;
8 import java.security.GeneralSecurityException;
9 import java.security.Key;
10 import java.security.PrivateKey;
11 import java.security.cert.Certificate;
12 import java.security.cert.X509Certificate;
13 import java.util.Arrays;
14 import java.util.Comparator;
15 import java.util.Properties;
16 import java.util.regex.Pattern;
18 import javax.naming.NamingException;
19 import javax.net.ssl.SSLSocketFactory;
21 import org.cacert.gigi.crypto.SMIME;
22 import org.cacert.gigi.database.DatabaseConnection;
23 import org.cacert.gigi.database.GigiPreparedStatement;
24 import org.cacert.gigi.util.DNSUtil;
26 public abstract class EmailProvider {
28 public abstract void sendmail(String to, String subject, String message, String from, String replyto, String toname, String fromname, String errorsto, boolean extra) throws IOException;
30 private static EmailProvider instance;
32 private X509Certificate c;
36 protected final void init(Certificate c, Key k) {
37 this.c = (X509Certificate) c;
38 this.k = (PrivateKey) k;
41 protected final void sendSigned(String contents, PrintWriter output) throws IOException, GeneralSecurityException {
42 SMIME.smime(contents, k, c, output);
45 public static EmailProvider getInstance() {
49 protected static void setInstance(EmailProvider instance) {
50 EmailProvider.instance = instance;
53 public static void initSystem(Properties conf, Certificate cert, Key pk) {
55 Class<?> c = Class.forName(conf.getProperty("emailProvider"));
56 EmailProvider ep = (EmailProvider) c.getDeclaredConstructor(Properties.class).newInstance(conf);
59 } catch (ReflectiveOperationException e) {
64 public static final String OK = "OK";
66 public static final String FAIL = "FAIL";
68 public static final Pattern MAIL = Pattern.compile("^([a-zA-Z0-9])+([a-zA-Z0-9\\+\\._-])*@([a-zA-Z0-9_-])+([a-zA-Z0-9\\._-]+)+$");
70 public String checkEmailServer(int forUid, String address) throws IOException {
71 if (MAIL.matcher(address).matches()) {
72 String[] parts = address.split("@", 2);
73 String domain = parts[1];
77 mxhosts = DNSUtil.getMXEntries(domain);
78 } catch (NamingException e1) {
79 return "MX lookup for your hostname failed.";
83 for (String host : mxhosts) {
84 host = host.split(" ", 2)[1];
85 if (host.endsWith(".")) {
86 host = host.substring(0, host.length() - 1);
88 return "Strange MX records.";
90 try (Socket s = new Socket(host, 25); BufferedReader br0 = new BufferedReader(new InputStreamReader(s.getInputStream())); PrintWriter pw0 = new PrintWriter(s.getOutputStream())) {
91 BufferedReader br = br0;
94 if ( !Sendmail.readSMTPResponse(br, 220)) {
98 pw.print("EHLO www.cacert.org\r\n");
100 boolean starttls = false;
102 line = br.readLine();
105 starttls |= line.substring(4).equals("STARTTLS");
106 } while (line.startsWith("250-"));
107 if (line == null || !line.startsWith("250 ")) {
112 pw.print("STARTTLS\r\n");
114 if ( !Sendmail.readSMTPResponse(br, 220)) {
117 Socket s1 = ((SSLSocketFactory) SSLSocketFactory.getDefault()).createSocket(s, host, 25, true);
118 br = new BufferedReader(new InputStreamReader(s1.getInputStream()));
119 pw = new PrintWriter(s1.getOutputStream());
120 pw.print("EHLO www.cacert.org\r\n");
122 if ( !Sendmail.readSMTPResponse(br, 250)) {
127 pw.print("MAIL FROM: <returns@cacert.org>\r\n");
130 if ( !Sendmail.readSMTPResponse(br, 250)) {
133 pw.print("RCPT TO: <" + address + ">\r\n");
136 if ( !Sendmail.readSMTPResponse(br, 250)) {
139 pw.print("QUIT\r\n");
141 if ( !Sendmail.readSMTPResponse(br, 221)) {
145 GigiPreparedStatement statmt = DatabaseConnection.getInstance().prepare("insert into `emailPinglog` set `when`=NOW(), `email`=?, `result`=?, `uid`=?");
146 statmt.setString(1, address);
147 statmt.setString(2, line);
148 statmt.setInt(3, forUid);
151 if (line == null || !line.startsWith("250")) {
160 GigiPreparedStatement statmt = DatabaseConnection.getInstance().prepare("insert into `emailPinglog` set `when`=NOW(), `email`=?, `result`=?, `uid`=?");
161 statmt.setString(1, address);
162 statmt.setString(2, "Failed to make a connection to the mail server");
163 statmt.setInt(3, forUid);
168 private static void sortMX(String[] mxhosts) {
169 Arrays.sort(mxhosts, new Comparator<String>() {
172 public int compare(String o1, String o2) {
173 int i1 = Integer.parseInt(o1.split(" ")[0]);
174 int i2 = Integer.parseInt(o2.split(" ")[0]);
175 return Integer.compare(i1, i2);