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.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 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(), "UTF-8"));//
91 PrintWriter pw0 = new PrintWriter(new OutputStreamWriter(s.getOutputStream(), "UTF-8"))) {
92 BufferedReader br = br0;
95 if ( !Sendmail.readSMTPResponse(br, 220)) {
99 pw.print("EHLO www.cacert.org\r\n");
101 boolean starttls = false;
103 line = br.readLine();
107 starttls |= line.substring(4).equals("STARTTLS");
108 } while (line.startsWith("250-"));
109 if (line == null || !line.startsWith("250 ")) {
114 pw.print("STARTTLS\r\n");
116 if ( !Sendmail.readSMTPResponse(br, 220)) {
119 Socket s1 = ((SSLSocketFactory) SSLSocketFactory.getDefault()).createSocket(s, host, 25, true);
120 br = new BufferedReader(new InputStreamReader(s1.getInputStream(), "UTF-8"));
121 pw = new PrintWriter(new OutputStreamWriter(s1.getOutputStream(), "UTF-8"));
122 pw.print("EHLO www.cacert.org\r\n");
124 if ( !Sendmail.readSMTPResponse(br, 250)) {
129 pw.print("MAIL FROM: <returns@cacert.org>\r\n");
132 if ( !Sendmail.readSMTPResponse(br, 250)) {
135 pw.print("RCPT TO: <" + address + ">\r\n");
138 if ( !Sendmail.readSMTPResponse(br, 250)) {
141 pw.print("QUIT\r\n");
143 if ( !Sendmail.readSMTPResponse(br, 221)) {
147 try (GigiPreparedStatement statmt = new GigiPreparedStatement("INSERT INTO `emailPinglog` SET `when`=NOW(), `email`=?, `result`=?, `uid`=?, `type`='fast', `status`=?::`pingState`")) {
148 statmt.setString(1, address);
149 statmt.setString(2, line);
150 statmt.setInt(3, forUid);
151 statmt.setString(4, "success");
155 if (line == null || !line.startsWith("250")) {
164 try (GigiPreparedStatement statmt = new GigiPreparedStatement("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");
174 private static void sortMX(String[] mxhosts) {
175 Arrays.sort(mxhosts, new Comparator<String>() {
178 public int compare(String o1, String o2) {
179 int i1 = Integer.parseInt(o1.split(" ")[0]);
180 int i2 = Integer.parseInt(o2.split(" ")[0]);
181 return Integer.compare(i1, i2);