X-Git-Url: https://code.wpia.club/?a=blobdiff_plain;f=src%2Forg%2Fcacert%2Fgigi%2Femail%2FEmailProvider.java;h=d82725d1783ebf5e971a7fc6a5e3aba23d845f3e;hb=ed391f95fa080212a1bec06afd0e0fd0dbb213bb;hp=6855f398d1332f73dccebb9bf70aeb2592751f7b;hpb=2233a655b4ef4c1122a406e83a0e6334b9fd49b6;p=gigi.git diff --git a/src/org/cacert/gigi/email/EmailProvider.java b/src/org/cacert/gigi/email/EmailProvider.java index 6855f398..d82725d1 100644 --- a/src/org/cacert/gigi/email/EmailProvider.java +++ b/src/org/cacert/gigi/email/EmailProvider.java @@ -3,132 +3,224 @@ package org.cacert.gigi.email; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; +import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.Socket; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.util.LinkedList; +import java.security.GeneralSecurityException; +import java.security.Key; +import java.security.PrivateKey; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Comparator; import java.util.Properties; import java.util.regex.Pattern; -import org.cacert.gigi.database.DatabaseConnection; +import javax.naming.NamingException; +import javax.net.ssl.SSLSocketFactory; + +import org.cacert.gigi.crypto.SMIME; +import org.cacert.gigi.database.GigiPreparedStatement; +import org.cacert.gigi.util.DNSUtil; +import org.cacert.gigi.util.DomainAssessment; public abstract class EmailProvider { - public abstract void sendmail(String to, String subject, String message, String from, String replyto, - String toname, String fromname, String errorsto, boolean extra) throws IOException; - - private static EmailProvider instance; - - public static EmailProvider getInstance() { - return instance; - } - - protected static void setInstance(EmailProvider instance) { - EmailProvider.instance = instance; - } - - public static void init(Properties conf) { - try { - Class c = Class.forName(conf.getProperty("emailProvider")); - instance = (EmailProvider) c.getDeclaredConstructor(Properties.class).newInstance(conf); - } catch (ReflectiveOperationException e) { - e.printStackTrace(); - } - } - - public static final String OK = "OK"; - public static final String FAIL = "FAIL"; - public static final Pattern MAIL = Pattern - .compile("^([a-zA-Z0-9])+([a-zA-Z0-9\\+\\._-])*@([a-zA-Z0-9_-])+([a-zA-Z0-9\\._-]+)+$"); - - public String checkEmailServer(int forUid, String address) throws IOException { - if (MAIL.matcher(address).matches()) { - String[] parts = address.split("@", 2); - String domain = parts[1]; - - LinkedList mxhosts = getMxHosts(domain); - - for (String host : mxhosts) { - try (Socket s = new Socket(host, 25); - BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); - PrintWriter pw = new PrintWriter(s.getOutputStream())) { - String line; - while ((line = br.readLine()) != null && line.startsWith("220-")) { - } - if (line == null || !line.startsWith("220")) { - continue; - } - - pw.print("HELO www.cacert.org\r\n"); - pw.flush(); - - while ((line = br.readLine()) != null && line.startsWith("220")) { - } - - if (line == null || !line.startsWith("250")) { - continue; - } - pw.print("MAIL FROM: \r\n"); - pw.flush(); - - line = br.readLine(); - - if (line == null || !line.startsWith("250")) { - continue; - } - pw.print("RCPT TO: <" + address + ">\r\n"); - pw.flush(); - - line = br.readLine(); - pw.print("QUIT\r\n"); - pw.flush(); - - try { - PreparedStatement statmt = DatabaseConnection.getInstance().prepare( - "insert into `pinglog` set `when`=NOW(), `email`=?, `result`=?, `uid`=?"); - statmt.setString(1, address); - statmt.setString(2, line); - statmt.setInt(3, forUid); - statmt.execute(); - } catch (SQLException e) { - e.printStackTrace(); - } - - if (line == null || !line.startsWith("250")) { - return line; - } else { - return OK; - } - } - - } - } - try { - PreparedStatement statmt = DatabaseConnection.getInstance().prepare( - "insert into `pinglog` set `when`=NOW(), `email`=?, `result`=?, `uid`=?"); - statmt.setString(1, address); - statmt.setString(2, "Failed to make a connection to the mail server"); - statmt.setInt(3, forUid); - statmt.execute(); - } catch (SQLException e) { - e.printStackTrace(); - } - return FAIL; - } - - private static LinkedList getMxHosts(String domain) throws IOException { - LinkedList mxhosts = new LinkedList(); - Process dig = Runtime.getRuntime().exec(new String[] { "dig", "+short", "MX", domain }); - try (BufferedReader br = new BufferedReader(new InputStreamReader(dig.getInputStream()))) { - String line; - while ((line = br.readLine()) != null) { - String[] mxparts = line.split(" ", 2); - if (mxparts.length != 2) { - continue; - } - mxhosts.add(mxparts[1].substring(0, mxparts[1].length() - 1)); - } - } - return mxhosts; - } + + public abstract void sendMail(String to, String subject, String message, String replyto, String toname, String fromname, String errorsto, boolean extra) throws IOException; + + private static EmailProvider instance; + + private X509Certificate c; + + private PrivateKey k; + + protected void init(Certificate c, Key k) { + this.c = (X509Certificate) c; + this.k = (PrivateKey) k; + } + + protected final void sendSigned(String contents, PrintWriter output) throws IOException, GeneralSecurityException { + if (k == null || c == null) { + output.print("Content-Transfer-Encoding: base64\r\n\r\n"); + output.print(contents); + } else { + SMIME.smime(contents, k, c, output); + } + } + + public static EmailProvider getInstance() { + return instance; + } + + protected static void setInstance(EmailProvider instance) { + EmailProvider.instance = instance; + } + + public static void initSystem(Properties conf, Certificate cert, Key pk) { + try { + Class c = Class.forName(conf.getProperty("emailProvider")); + EmailProvider ep = (EmailProvider) c.getDeclaredConstructor(Properties.class).newInstance(conf); + ep.init(cert, pk); + instance = ep; + } catch (ReflectiveOperationException e) { + e.printStackTrace(); + } + } + + public static final String OK = "OK"; + + public static final String FAIL = "FAIL"; + + private static final String MAIL_P_RFC_WORD = "[A-Za-z0-9\\+\\.!#$%&'*/=?^_`|~{}-]+"; + + private static final String MAIL_P_RFC_LOCAL = MAIL_P_RFC_WORD + "(?:\\." + MAIL_P_RFC_WORD + ")*"; + + private static final String MAIL_P_RFC_LABEL = "(?!(?!xn)..--|-)(?:[A-Za-z0-9-]+)(?\r\n"); + pw.flush(); + + if ( !SendMail.readSMTPResponse(br, 250)) { + continue; + } + pw.print("RCPT TO: <" + address + ">\r\n"); + pw.flush(); + + if ( !SendMail.readSMTPResponse(br, 250)) { + continue; + } + pw.print("QUIT\r\n"); + pw.flush(); + if ( !SendMail.readSMTPResponse(br, 221)) { + continue; + } + + try (GigiPreparedStatement statmt = new GigiPreparedStatement("INSERT INTO `emailPinglog` SET `when`=NOW(), `email`=?, `result`=?, `uid`=?, `type`='fast', `status`=?::`pingState`")) { + statmt.setString(1, address); + statmt.setString(2, line); + statmt.setInt(3, forUid); + statmt.setString(4, "success"); + statmt.execute(); + } + + if (line == null || !line.startsWith("250")) { + return line; + } else { + return OK; + } + } + + } + } + try (GigiPreparedStatement statmt = new GigiPreparedStatement("INSERT INTO `emailPinglog` SET `when`=NOW(), `email`=?, `result`=?, `uid`=?, `type`='fast', `status`=?::`pingState`")) { + statmt.setString(1, address); + statmt.setString(2, "Failed to make a connection to the mail server"); + statmt.setInt(3, forUid); + statmt.setString(4, "failed"); + statmt.execute(); + } + return FAIL; + } + + private static void sortMX(String[] mxhosts) { + Arrays.sort(mxhosts, new Comparator() { + + @Override + public int compare(String o1, String o2) { + int i1 = Integer.parseInt(o1.split(" ")[0]); + int i2 = Integer.parseInt(o2.split(" ")[0]); + return Integer.compare(i1, i2); + } + }); + } + + public static boolean isValidMailAddress(String address) { + if ( !MAIL_ADDRESS.matcher(address).matches()) { + return false; + } + + String[] parts = address.split("@", 2); + + String local = parts[0]; + String domain = parts[1]; + + if ( !MAIL_LOCAL.matcher(local).matches()) { + return false; + } + + for (String domainPart : domain.split("\\.", -1)) { + if ( !DomainAssessment.isValidDomainPart(domainPart)) { + return false; + } + } + + return true; + } + }