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.OutputStreamWriter;
7 import java.io.PrintWriter;
8 import java.net.Socket;
9 import java.security.GeneralSecurityException;
10 import java.security.Key;
11 import java.security.PrivateKey;
12 import java.security.cert.Certificate;
13 import java.security.cert.X509Certificate;
14 import java.util.Arrays;
15 import java.util.Comparator;
16 import java.util.Properties;
17 import java.util.regex.Pattern;
19 import javax.naming.NamingException;
20 import javax.net.ssl.SSLSocketFactory;
22 import org.cacert.gigi.crypto.SMIME;
23 import org.cacert.gigi.database.DatabaseConnection;
24 import org.cacert.gigi.database.GigiPreparedStatement;
25 import org.cacert.gigi.util.DNSUtil;
27 public abstract class EmailProvider {
29 public abstract void sendmail(String to, String subject, String message, String from, String replyto, String toname, String fromname, String errorsto, boolean extra) throws IOException;
31 private static EmailProvider instance;
33 private X509Certificate c;
37 protected void init(Certificate c, Key k) {
38 this.c = (X509Certificate) c;
39 this.k = (PrivateKey) k;
42 protected final void sendSigned(String contents, PrintWriter output) throws IOException, GeneralSecurityException {
43 SMIME.smime(contents, k, c, output);
46 public static EmailProvider getInstance() {
50 protected static void setInstance(EmailProvider instance) {
51 EmailProvider.instance = instance;
54 public static void initSystem(Properties conf, Certificate cert, Key pk) {
56 Class<?> c = Class.forName(conf.getProperty("emailProvider"));
57 EmailProvider ep = (EmailProvider) c.getDeclaredConstructor(Properties.class).newInstance(conf);
60 } catch (ReflectiveOperationException e) {
65 public static final String OK = "OK";
67 public static final String FAIL = "FAIL";
69 public static final Pattern MAIL = Pattern.compile("^([a-zA-Z0-9])+([a-zA-Z0-9\\+\\._-])*@([a-zA-Z0-9_-])+([a-zA-Z0-9\\._-]+)+$");
71 public String checkEmailServer(int forUid, String address) throws IOException {
72 if (MAIL.matcher(address).matches()) {
73 String[] parts = address.split("@", 2);
74 String domain = parts[1];
78 mxhosts = DNSUtil.getMXEntries(domain);
79 } catch (NamingException e1) {
80 return "MX lookup for your hostname failed.";
84 for (String host : mxhosts) {
85 host = host.split(" ", 2)[1];
86 if (host.endsWith(".")) {
87 host = host.substring(0, host.length() - 1);
89 return "Strange MX records.";
91 try (Socket s = new Socket(host, 25); BufferedReader br0 = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8"));//
92 PrintWriter pw0 = new PrintWriter(new OutputStreamWriter(s.getOutputStream(), "UTF-8"))) {
93 BufferedReader br = br0;
96 if ( !Sendmail.readSMTPResponse(br, 220)) {
100 pw.print("EHLO www.cacert.org\r\n");
102 boolean starttls = false;
104 line = br.readLine();
108 starttls |= line.substring(4).equals("STARTTLS");
109 } while (line.startsWith("250-"));
110 if (line == null || !line.startsWith("250 ")) {
115 pw.print("STARTTLS\r\n");
117 if ( !Sendmail.readSMTPResponse(br, 220)) {
120 Socket s1 = ((SSLSocketFactory) SSLSocketFactory.getDefault()).createSocket(s, host, 25, true);
121 br = new BufferedReader(new InputStreamReader(s1.getInputStream(), "UTF-8"));
122 pw = new PrintWriter(new OutputStreamWriter(s1.getOutputStream(), "UTF-8"));
123 pw.print("EHLO www.cacert.org\r\n");
125 if ( !Sendmail.readSMTPResponse(br, 250)) {
130 pw.print("MAIL FROM: <returns@cacert.org>\r\n");
133 if ( !Sendmail.readSMTPResponse(br, 250)) {
136 pw.print("RCPT TO: <" + address + ">\r\n");
139 if ( !Sendmail.readSMTPResponse(br, 250)) {
142 pw.print("QUIT\r\n");
144 if ( !Sendmail.readSMTPResponse(br, 221)) {
148 GigiPreparedStatement statmt = DatabaseConnection.getInstance().prepare("INSERT INTO `emailPinglog` SET `when`=NOW(), `email`=?, `result`=?, `uid`=?, `type`='fast', `status`=?::`pingState`");
149 statmt.setString(1, address);
150 statmt.setString(2, line);
151 statmt.setInt(3, forUid);
152 statmt.setString(4, "success");
155 if (line == null || !line.startsWith("250")) {
164 GigiPreparedStatement statmt = DatabaseConnection.getInstance().prepare("INSERT INTO `emailPinglog` SET `when`=NOW(), `email`=?, `result`=?, `uid`=?, `type`='fast', `status`=?::`pingState`");
165 statmt.setString(1, address);
166 statmt.setString(2, "Failed to make a connection to the mail server");
167 statmt.setInt(3, forUid);
168 statmt.setString(4, "failed");
173 private static void sortMX(String[] mxhosts) {
174 Arrays.sort(mxhosts, new Comparator<String>() {
177 public int compare(String o1, String o2) {
178 int i1 = Integer.parseInt(o1.split(" ")[0]);
179 int i2 = Integer.parseInt(o2.split(" ")[0]);
180 return Integer.compare(i1, i2);